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 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
648
649
650
651 /***
652 * Initialize the Digester rules for the current document.
653 *
654 * @param scxml The parent SCXML document (or null)
655 * @param pr The PathResolver
656 * @param customActions The list of custom actions this digester needs
657 * to be able to process
658 *
659 * @return scxmlRules The rule set to be used for digestion
660 */
661 private static ExtendedBaseRules initRules(final SCXML scxml,
662 final PathResolver pr, final List customActions) {
663
664 ExtendedBaseRules scxmlRules = new ExtendedBaseRules();
665 scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
666
667
668 scxmlRules.add(XP_SM, new ObjectCreateRule(SCXML.class));
669 scxmlRules.add(XP_SM, new SetPropertiesRule());
670
671
672 addDatamodelRules(XP_SM + XPF_DM, scxmlRules, scxml, pr);
673
674
675
676 addStateRules(XP_SM_ST, scxmlRules, customActions, scxml, pr, 0);
677 scxmlRules.add(XP_SM_ST, new SetNextRule("addState"));
678
679 addStateRules(XPU_ST_ST, scxmlRules, customActions, scxml, pr, 1);
680 scxmlRules.add(XPU_ST_ST, new SetNextRule("addChild"));
681
682
683 addStateRules(XPU_PAR_ST, scxmlRules, customActions, scxml, pr, 1);
684 scxmlRules.add(XPU_PAR_ST, new SetNextRule("addState"));
685
686 addStateRules(XPU_TR_TAR_ST, scxmlRules, customActions, scxml, pr, 2);
687 scxmlRules.add(XPU_TR_TAR_ST, new SetNextRule("setTarget"));
688
689
690 addParallelRules(XPU_ST_PAR, scxmlRules, pr, customActions, scxml);
691
692
693 addIfRules(XPU_IF, scxmlRules, pr, customActions);
694
695
696 addCustomActionRules(XPU_ONEN, scxmlRules, customActions);
697 addCustomActionRules(XPU_ONEX, scxmlRules, customActions);
698 addCustomActionRules(XPU_TR, scxmlRules, customActions);
699 addCustomActionRules(XPU_IF, scxmlRules, customActions);
700 addCustomActionRules(XPU_FIN, scxmlRules, customActions);
701
702 return scxmlRules;
703
704 }
705
706 /***
707 * Add Digester rules for all <state> elements.
708 *
709 * @param xp The Digester style XPath expression of the parent
710 * XML element
711 * @param scxmlRules The rule set to be used for digestion
712 * @param customActions The list of custom actions this digester needs
713 * to be able to process
714 * @param scxml The parent SCXML document (or null)
715 * @param pr The PathResolver
716 * @param parent The distance between this state and its parent
717 * state on the Digester stack
718 */
719 private static void addStateRules(final String xp,
720 final ExtendedBaseRules scxmlRules, final List customActions,
721 final SCXML scxml, final PathResolver pr, final int parent) {
722 scxmlRules.add(xp, new ObjectCreateRule(State.class));
723 addStatePropertiesRules(xp, scxmlRules, customActions, pr, scxml);
724 addDatamodelRules(xp + XPF_DM, scxmlRules, scxml, pr);
725 addInvokeRules(xp + XPF_INV, scxmlRules, customActions, pr, scxml);
726 addInitialRules(xp + XPF_INI, scxmlRules, customActions, pr, scxml);
727 addHistoryRules(xp + XPF_HIST, scxmlRules, customActions, pr, scxml);
728 addParentRule(xp, scxmlRules, parent);
729 addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition",
730 pr, customActions);
731 addHandlerRules(xp, scxmlRules, pr, customActions);
732 scxmlRules.add(xp, new UpdateModelRule(scxml));
733 }
734
735 /***
736 * Add Digester rules for all <parallel> elements.
737 *
738 * @param xp The Digester style XPath expression of the parent
739 * XML element
740 * @param scxmlRules The rule set to be used for digestion
741 * @param customActions The list of custom actions this digester needs
742 * to be able to process
743 * @param pr The {@link PathResolver} for this document
744 * @param scxml The parent SCXML document (or null)
745 */
746 private static void addParallelRules(final String xp,
747 final ExtendedBaseRules scxmlRules, final PathResolver pr,
748 final List customActions, final SCXML scxml) {
749 addSimpleRulesTuple(xp, scxmlRules, Parallel.class, null, null,
750 "setParallel");
751 addHandlerRules(xp, scxmlRules, pr, customActions);
752 addParentRule(xp, scxmlRules, 1);
753 scxmlRules.add(xp, new UpdateModelRule(scxml));
754 }
755
756 /***
757 * Add Digester rules for all <state> element attributes.
758 *
759 * @param xp The Digester style XPath expression of the parent
760 * XML element
761 * @param scxmlRules The rule set to be used for digestion
762 * @param customActions The list of custom actions this digester needs
763 * to be able to process
764 * @param pr The PathResolver
765 * @param scxml The root document, if this one is src'ed in
766 */
767 private static void addStatePropertiesRules(final String xp,
768 final ExtendedBaseRules scxmlRules, final List customActions,
769 final PathResolver pr, final SCXML scxml) {
770 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id", "final"},
771 new String[] {"id", "isFinal"}));
772 scxmlRules.add(xp, new DigestSrcAttributeRule(scxml,
773 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 SetCurrentNamespacesRule());
792 scxmlRules.add(xp + XPF_DATA, new SetNextRule("addData"));
793 try {
794 scxmlRules.add(xp + XPF_DATA, new ParseDataRule(pr));
795 } catch (ParserConfigurationException pce) {
796 org.apache.commons.logging.Log log = LogFactory.
797 getLog(SCXMLDigester.class);
798 log.error(ERR_PARSER_CFG_DATA, pce);
799 }
800 scxmlRules.add(xp, new SetNextRule("setDatamodel"));
801 }
802
803 /***
804 * Add Digester rules for all <invoke> elements.
805 *
806 * @param xp The Digester style XPath expression of the parent
807 * XML element
808 * @param scxmlRules The rule set to be used for digestion
809 * @param customActions The list of {@link CustomAction}s this digester
810 * instance will process, can be null or empty
811 * @param pr The PathResolver
812 * @param scxml The parent SCXML document (or null)
813 */
814 private static void addInvokeRules(final String xp,
815 final ExtendedBaseRules scxmlRules, final List customActions,
816 final PathResolver pr, final SCXML scxml) {
817 scxmlRules.add(xp, new ObjectCreateRule(Invoke.class));
818 scxmlRules.add(xp, new SetPropertiesRule());
819 scxmlRules.add(xp, new SetCurrentNamespacesRule());
820 scxmlRules.add(xp, new SetPathResolverRule(pr));
821 scxmlRules.add(xp + XPF_PRM, new ObjectCreateRule(Param.class));
822 scxmlRules.add(xp + XPF_PRM, new SetPropertiesRule());
823 scxmlRules.add(xp + XPF_PRM, new SetCurrentNamespacesRule());
824 scxmlRules.add(xp + XPF_PRM, new SetNextRule("addParam"));
825 scxmlRules.add(xp + XPF_FIN, new ObjectCreateRule(Finalize.class));
826 scxmlRules.add(xp + XPF_FIN, new UpdateFinalizeRule());
827 addActionRules(xp + XPF_FIN, scxmlRules, pr, customActions);
828 scxmlRules.add(xp + XPF_FIN, new SetNextRule("setFinalize"));
829 scxmlRules.add(xp, new SetNextRule("setInvoke"));
830 }
831
832 /***
833 * Add Digester rules for all <initial> elements.
834 *
835 * @param xp The Digester style XPath expression of the parent
836 * XML element
837 * @param scxmlRules The rule set to be used for digestion
838 * @param customActions The list of custom actions this digester needs
839 * to be able to process
840 * @param pr The PathResolver
841 * @param scxml The parent SCXML document (or null)
842 */
843 private static void addInitialRules(final String xp,
844 final ExtendedBaseRules scxmlRules, final List customActions,
845 final PathResolver pr, final SCXML scxml) {
846 scxmlRules.add(xp, new ObjectCreateRule(Initial.class));
847 addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr,
848 scxml);
849 scxmlRules.add(xp, new UpdateModelRule(scxml));
850 addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
851 pr, customActions);
852 scxmlRules.add(xp, new SetNextRule("setInitial"));
853 }
854
855 /***
856 * Add Digester rules for all <history> elements.
857 *
858 * @param xp The Digester style XPath expression of the parent
859 * XML element
860 * @param scxmlRules The rule set to be used for digestion
861 * @param customActions The list of custom actions this digester needs
862 * to be able to process
863 * @param pr The PathResolver
864 * @param scxml The parent SCXML document (or null)
865 */
866 private static void addHistoryRules(final String xp,
867 final ExtendedBaseRules scxmlRules, final List customActions,
868 final PathResolver pr, final SCXML scxml) {
869 scxmlRules.add(xp, new ObjectCreateRule(History.class));
870 addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr,
871 scxml);
872 scxmlRules.add(xp, new UpdateModelRule(scxml));
873 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"type"},
874 new String[] {"type"}));
875 addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
876 pr, customActions);
877 scxmlRules.add(xp, new SetNextRule("addHistory"));
878 }
879
880 /***
881 * Add Digester rules for all pseudo state (initial, history) element
882 * attributes.
883 *
884 * @param xp The Digester style XPath expression of the parent
885 * XML element
886 * @param scxmlRules The rule set to be used for digestion
887 * @param customActions The list of custom actions this digester needs
888 * to be able to process
889 * @param pr The PathResolver
890 * @param scxml The root document, if this one is src'ed in
891 */
892 private static void addPseudoStatePropertiesRules(final String xp,
893 final ExtendedBaseRules scxmlRules, final List customActions,
894 final PathResolver pr, final SCXML scxml) {
895 scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id"},
896 new String[] {"id"}));
897 scxmlRules.add(xp, new DigestSrcAttributeRule(scxml, customActions,
898 pr));
899 addParentRule(xp, scxmlRules, 1);
900 }
901
902 /***
903 * Add Digester rule for all setting parent state.
904 *
905 * @param xp The Digester style XPath expression of the parent
906 * XML element
907 * @param scxmlRules The rule set to be used for digestion
908 * @param parent The distance between this state and its parent
909 * state on the Digester stack
910 */
911 private static void addParentRule(final String xp,
912 final ExtendedBaseRules scxmlRules, final int parent) {
913 if (parent < 1) {
914 return;
915 }
916 scxmlRules.add(xp, new Rule() {
917
918 public void body(final String namespace, final String name,
919 final String text) throws Exception {
920 TransitionTarget t = (TransitionTarget) getDigester().peek();
921 TransitionTarget p = (TransitionTarget) getDigester().peek(
922 parent);
923
924 t.setParent(p);
925 }
926 });
927 }
928
929 /***
930 * Add Digester rules for all <transition> elements.
931 *
932 * @param xp The Digester style XPath expression of the parent
933 * XML element
934 * @param scxmlRules The rule set to be used for digestion
935 * @param setNextMethod The method name for adding this transition
936 * to its parent (defined by the SCXML Java object model).
937 * @param pr The {@link PathResolver} for this document
938 * @param customActions The list of custom actions this digester needs
939 * to be able to process
940 */
941 private static void addTransitionRules(final String xp,
942 final ExtendedBaseRules scxmlRules, final String setNextMethod,
943 final PathResolver pr, final List customActions) {
944 scxmlRules.add(xp, new ObjectCreateRule(Transition.class));
945 scxmlRules.add(xp, new SetPropertiesRule(
946 new String[] {"event", "cond", "target"},
947 new String[] {"event", "cond", "next"}));
948 scxmlRules.add(xp, new SetCurrentNamespacesRule());
949 scxmlRules.add(xp + XPF_TAR, new SetPropertiesRule());
950 addActionRules(xp, scxmlRules, pr, customActions);
951 scxmlRules.add(xp + XPF_EXT, new Rule() {
952 public void end(final String namespace, final String name) {
953 Transition t = (Transition) getDigester().peek(1);
954 State exitState = new State();
955 exitState.setIsFinal(true);
956 t.setTarget(exitState);
957 }
958 });
959 scxmlRules.add(xp, new SetNextRule(setNextMethod));
960 }
961
962 /***
963 * Add Digester rules for all <onentry> and <onexit>
964 * elements.
965 *
966 * @param xp The Digester style XPath expression of the parent
967 * XML element
968 * @param scxmlRules The rule set to be used for digestion
969 * @param pr The {@link PathResolver} for this document
970 * @param customActions The list of custom actions this digester needs
971 * to be able to process
972 */
973 private static void addHandlerRules(final String xp,
974 final ExtendedBaseRules scxmlRules, final PathResolver pr,
975 final List customActions) {
976 scxmlRules.add(xp + XPF_ONEN, new ObjectCreateRule(OnEntry.class));
977 addActionRules(xp + XPF_ONEN, scxmlRules, pr, customActions);
978 scxmlRules.add(xp + XPF_ONEN, new SetNextRule("setOnEntry"));
979 scxmlRules.add(xp + XPF_ONEX, new ObjectCreateRule(OnExit.class));
980 addActionRules(xp + XPF_ONEX, scxmlRules, pr, customActions);
981 scxmlRules.add(xp + XPF_ONEX, new SetNextRule("setOnExit"));
982 }
983
984 /***
985 * Add Digester rules for all actions ("executable" elements).
986 *
987 * @param xp The Digester style XPath expression of the parent
988 * XML element
989 * @param scxmlRules The rule set to be used for digestion
990 * @param pr The {@link PathResolver} for this document
991 * @param customActions The list of custom actions this digester needs
992 * to be able to process
993 */
994 private static void addActionRules(final String xp,
995 final ExtendedBaseRules scxmlRules, final PathResolver pr,
996 final List customActions) {
997 addActionRulesTuple(xp + XPF_ASN, scxmlRules, Assign.class);
998 scxmlRules.add(xp + XPF_ASN, new SetPathResolverRule(pr));
999 addActionRulesTuple(xp + XPF_VAR, scxmlRules, Var.class);
1000 addActionRulesTuple(xp + XPF_LOG, scxmlRules, Log.class);
1001 addSendRulesTuple(xp + XPF_SND, scxmlRules);
1002 addActionRulesTuple(xp + XPF_CAN, scxmlRules, Cancel.class);
1003 addActionRulesTuple(xp + XPF_EXT, scxmlRules, Exit.class);
1004
1005 }
1006
1007 /***
1008 * Add custom action rules, if any custom actions are provided.
1009 *
1010 * @param xp The Digester style XPath expression of the parent
1011 * XML element
1012 * @param scxmlRules The rule set to be used for digestion
1013 * @param customActions The list of custom actions this digester needs
1014 * to be able to process
1015 */
1016 private static void addCustomActionRules(final String xp,
1017 final ExtendedBaseRules scxmlRules, final List customActions) {
1018 if (customActions == null || customActions.size() == 0) {
1019 return;
1020 }
1021 for (int i = 0; i < customActions.size(); i++) {
1022 Object item = customActions.get(i);
1023 if (item == null || !(item instanceof CustomAction)) {
1024 org.apache.commons.logging.Log log = LogFactory.
1025 getLog(SCXMLDigester.class);
1026 log.warn(ERR_CUSTOM_ACTION_TYPE);
1027 } else {
1028 CustomAction ca = (CustomAction) item;
1029 scxmlRules.setNamespaceURI(ca.getNamespaceURI());
1030 String xpfLocalName = STR_SLASH + ca.getLocalName();
1031 Class klass = ca.getActionClass();
1032 if (SCXMLHelper.implementationOf(klass,
1033 ExternalContent.class)) {
1034 addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
1035 klass, true);
1036 } else {
1037 addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
1038 klass, false);
1039 }
1040 }
1041 }
1042 scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
1043 }
1044
1045 /***
1046 * Add Digester rules that are specific to the <send> action
1047 * element.
1048 *
1049 * @param xp The Digester style XPath expression of <send> element
1050 * @param scxmlRules The rule set to be used for digestion
1051 */
1052 private static void addSendRulesTuple(final String xp,
1053 final ExtendedBaseRules scxmlRules) {
1054 addActionRulesTuple(xp, scxmlRules, Send.class);
1055 try {
1056 scxmlRules.add(xp, new ParseExternalContentRule());
1057 } catch (ParserConfigurationException pce) {
1058 org.apache.commons.logging.Log log = LogFactory.
1059 getLog(SCXMLDigester.class);
1060 log.error(ERR_PARSER_CFG_SEND, pce);
1061 }
1062 }
1063
1064 /***
1065 * Add Digester rules for a simple custom action (no body content).
1066 *
1067 * @param xp The path to the custom action element
1068 * @param scxmlRules The rule set to be used for digestion
1069 * @param klass The <code>Action</code> class implementing the custom
1070 * action.
1071 * @param bodyContent Whether the custom rule has body content
1072 * that should be parsed using
1073 * <code>NodeCreateRule</code>
1074 */
1075 private static void addCustomActionRulesTuple(final String xp,
1076 final ExtendedBaseRules scxmlRules, final Class klass,
1077 final boolean bodyContent) {
1078 addActionRulesTuple(xp, scxmlRules, klass);
1079 if (bodyContent) {
1080 try {
1081 scxmlRules.add(xp, new ParseExternalContentRule());
1082 } catch (ParserConfigurationException pce) {
1083 org.apache.commons.logging.Log log = LogFactory.
1084 getLog(SCXMLDigester.class);
1085 log.error(ERR_PARSER_CFG_CUSTOM, pce);
1086 }
1087 }
1088 }
1089
1090 /***
1091 * Add Digester rules for all <if> elements.
1092 *
1093 * @param xp The Digester style XPath expression of the parent
1094 * XML element
1095 * @param scxmlRules The rule set to be used for digestion
1096 * @param pr The {@link PathResolver} for this document
1097 * @param customActions The list of custom actions this digester needs
1098 * to be able to process
1099 */
1100 private static void addIfRules(final String xp,
1101 final ExtendedBaseRules scxmlRules, final PathResolver pr,
1102 final List customActions) {
1103 addActionRulesTuple(xp, scxmlRules, If.class);
1104 addActionRules(xp, scxmlRules, pr, customActions);
1105 addActionRulesTuple(xp + XPF_EIF, scxmlRules, ElseIf.class);
1106 addActionRulesTuple(xp + XPF_ELS, scxmlRules, Else.class);
1107 }
1108
1109 /***
1110 * Add Digester rules that are common across all actions elements.
1111 *
1112 * @param xp The Digester style XPath expression of the parent
1113 * XML element
1114 * @param scxmlRules The rule set to be used for digestion
1115 * @param klass The class in the Java object model to be instantiated
1116 * in the ObjectCreateRule for this action
1117 */
1118 private static void addActionRulesTuple(final String xp,
1119 final ExtendedBaseRules scxmlRules, final Class klass) {
1120 addSimpleRulesTuple(xp, scxmlRules, klass, null, null, "addAction");
1121 scxmlRules.add(xp, new SetExecutableParentRule());
1122 scxmlRules.add(xp, new SetCurrentNamespacesRule());
1123 }
1124
1125 /***
1126 * Add the run of the mill Digester rules for any element.
1127 *
1128 * @param xp The Digester style XPath expression of the parent
1129 * XML element
1130 * @param scxmlRules The rule set to be used for digestion
1131 * @param klass The class in the Java object model to be instantiated
1132 * in the ObjectCreateRule for this action
1133 * @param args The attributes to be mapped into the object model
1134 * @param props The properties that args get mapped to
1135 * @param addMethod The method that the SetNextRule should call
1136 */
1137 private static void addSimpleRulesTuple(final String xp,
1138 final ExtendedBaseRules scxmlRules, final Class klass,
1139 final String[] args, final String[] props,
1140 final String addMethod) {
1141 scxmlRules.add(xp, new ObjectCreateRule(klass));
1142 if (args == null) {
1143 scxmlRules.add(xp, new SetPropertiesRule());
1144 } else {
1145 scxmlRules.add(xp, new SetPropertiesRule(args, props));
1146 }
1147 scxmlRules.add(xp, new SetNextRule(addMethod));
1148 }
1149
1150 /***
1151 * Discourage instantiation since this is a utility class.
1152 */
1153 private SCXMLDigester() {
1154 super();
1155 }
1156
1157 /***
1158 * Custom digestion rule for establishing necessary associations of this
1159 * TransitionTarget with the root SCXML object.
1160 * These include: <br>
1161 * 1) Updation of the SCXML object's global targets Map <br>
1162 * 2) Obtaining a handle to the SCXML object's NotificationRegistry <br>
1163 *
1164 * @deprecated Will be removed in version 1.0
1165 */
1166 public static class UpdateModelRule extends Rule {
1167
1168 /***
1169 * The root SCXML object.
1170 */
1171 private SCXML scxml;
1172
1173 /***
1174 * Constructor.
1175 * @param scxml The root SCXML object
1176 */
1177 public UpdateModelRule(final SCXML scxml) {
1178 super();
1179 this.scxml = scxml;
1180 }
1181
1182 /***
1183 * @see Rule#end(String, String)
1184 */
1185 public final void end(final String namespace, final String name) {
1186 if (scxml == null) {
1187 scxml = (SCXML) getDigester()
1188 .peek(getDigester().getCount() - 1);
1189 }
1190 TransitionTarget tt = (TransitionTarget) getDigester().peek();
1191 scxml.addTarget(tt);
1192 }
1193 }
1194
1195 /***
1196 * Custom digestion rule for setting Executable parent of Action elements.
1197 *
1198 * @deprecated Will be removed in version 1.0
1199 */
1200 public static class SetExecutableParentRule extends Rule {
1201
1202 /***
1203 * Constructor.
1204 */
1205 public SetExecutableParentRule() {
1206 super();
1207 }
1208
1209 /***
1210 * @see Rule#end(String, String)
1211 */
1212 public final void end(final String namespace, final String name) {
1213 Action child = (Action) getDigester().peek();
1214 for (int i = 1; i < getDigester().getCount() - 1; i++) {
1215 Object ancestor = getDigester().peek(i);
1216 if (ancestor instanceof Executable) {
1217 child.setParent((Executable) ancestor);
1218 return;
1219 }
1220 }
1221 }
1222 }
1223
1224 /***
1225 * Custom digestion rule for parsing bodies of
1226 * <code>ExternalContent</code> elements.
1227 *
1228 * @see ExternalContent
1229 *
1230 * @deprecated Will be removed in version 1.0
1231 */
1232 public static class ParseExternalContentRule extends NodeCreateRule {
1233 /***
1234 * Constructor.
1235 * @throws ParserConfigurationException A JAXP configuration error
1236 */
1237 public ParseExternalContentRule()
1238 throws ParserConfigurationException {
1239 super();
1240 }
1241 /***
1242 * @see Rule#end(String, String)
1243 */
1244 public final void end(final String namespace, final String name) {
1245 Element bodyElement = (Element) getDigester().pop();
1246 NodeList childNodes = bodyElement.getChildNodes();
1247 List externalNodes = ((ExternalContent) getDigester().
1248 peek()).getExternalNodes();
1249 for (int i = 0; i < childNodes.getLength(); i++) {
1250 externalNodes.add(childNodes.item(i));
1251 }
1252 }
1253 }
1254
1255 /***
1256 * Custom digestion rule for parsing bodies of <data> elements.
1257 *
1258 * @deprecated Will be removed in version 1.0
1259 */
1260 public static class ParseDataRule extends NodeCreateRule {
1261
1262 /***
1263 * The PathResolver used to resolve the src attribute to the
1264 * SCXML document it points to.
1265 * @see PathResolver
1266 */
1267 private PathResolver pr;
1268
1269 /***
1270 * The "src" attribute, retained to check if body content is legal.
1271 */
1272 private String src;
1273
1274 /***
1275 * The "expr" attribute, retained to check if body content is legal.
1276 */
1277 private String expr;
1278
1279 /***
1280 * The XML tree for this data, parse as a Node, obtained from
1281 * either the "src" or the "expr" attributes.
1282 */
1283 private Node attrNode;
1284
1285 /***
1286 * Constructor.
1287 *
1288 * @param pr The <code>PathResolver</code>
1289 * @throws ParserConfigurationException A JAXP configuration error
1290 */
1291 public ParseDataRule(final PathResolver pr)
1292 throws ParserConfigurationException {
1293 super();
1294 this.pr = pr;
1295 }
1296
1297 /***
1298 * @see Rule#begin(String, String, Attributes)
1299 */
1300 public final void begin(final String namespace, final String name,
1301 final Attributes attributes) throws Exception {
1302 super.begin(namespace, name, attributes);
1303 src = attributes.getValue("src");
1304 expr = attributes.getValue("expr");
1305 if (!SCXMLHelper.isStringEmpty(src)) {
1306 String path = null;
1307 if (pr == null) {
1308 path = src;
1309 } else {
1310 path = pr.resolvePath(src);
1311 }
1312 try {
1313 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.
1314 newInstance();
1315 DocumentBuilder db = dbFactory.newDocumentBuilder();
1316 attrNode = db.parse(path);
1317 } catch (Throwable t) {
1318 org.apache.commons.logging.Log log = LogFactory.
1319 getLog(SCXMLDigester.class);
1320 log.error(t.getMessage(), t);
1321 }
1322 return;
1323 }
1324 }
1325
1326 /***
1327 * @see Rule#end(String, String)
1328 */
1329 public final void end(final String namespace, final String name) {
1330 Node bodyNode = (Node) getDigester().pop();
1331 Data data = ((Data) getDigester().peek());
1332
1333
1334 if (!SCXMLHelper.isStringEmpty(src)) {
1335 data.setNode(attrNode);
1336 } else if (SCXMLHelper.isStringEmpty(expr)) {
1337
1338 data.setNode(bodyNode);
1339 }
1340 }
1341 }
1342
1343 /***
1344 * Custom digestion rule for external sources, that is, the src attribute of
1345 * the <state> element.
1346 *
1347 * @deprecated Will be removed in version 1.0
1348 */
1349 public static class DigestSrcAttributeRule extends Rule {
1350
1351 /***
1352 * The PathResolver used to resolve the src attribute to the
1353 * SCXML document it points to.
1354 * @see PathResolver
1355 */
1356 private PathResolver pr;
1357
1358 /***
1359 * The root document.
1360 */
1361 private SCXML root;
1362
1363 /***
1364 * The list of custom actions the parent document is capable of
1365 * processing (and hence, the child should be, by transitivity).
1366 * @see CustomAction
1367 */
1368 private List customActions;
1369
1370 /***
1371 * Constructor.
1372 * @param pr The PathResolver
1373 * @param customActions The list of custom actions this digester needs
1374 * to be able to process
1375 *
1376 * @see PathResolver
1377 * @see CustomAction
1378 *
1379 * TODO: Remove in v1.0
1380 */
1381 public DigestSrcAttributeRule(final List customActions,
1382 final PathResolver pr) {
1383 super();
1384 this.customActions = customActions;
1385 this.pr = pr;
1386 }
1387
1388 /***
1389 * Constructor.
1390 * @param root The root document, if this one is src'ed in
1391 * @param pr The PathResolver
1392 * @param customActions The list of custom actions this digester needs
1393 * to be able to process
1394 *
1395 * @see PathResolver
1396 * @see CustomAction
1397 */
1398 public DigestSrcAttributeRule(final SCXML root,
1399 final List customActions, final PathResolver pr) {
1400 super();
1401 this.root = root;
1402 this.customActions = customActions;
1403 this.pr = pr;
1404 }
1405
1406 /***
1407 * @see Rule#begin(String, String, Attributes)
1408 */
1409 public final void begin(final String namespace, final String name,
1410 final Attributes attributes) {
1411 String src = attributes.getValue("src");
1412 if (SCXMLHelper.isStringEmpty(src)) {
1413 return;
1414 }
1415 Digester digester = getDigester();
1416 SCXML scxml = (SCXML) digester.peek(digester.getCount() - 1);
1417
1418 SCXML externalSCXML = null;
1419 String path;
1420 Digester externalSrcDigester;
1421 if (pr == null) {
1422 path = src;
1423 if (root != null) {
1424 externalSrcDigester = newInstance(root, null,
1425 customActions);
1426 } else {
1427 externalSrcDigester = newInstance(scxml, null,
1428 customActions);
1429 }
1430 } else {
1431 path = pr.resolvePath(src);
1432 if (root != null) {
1433 externalSrcDigester = newInstance(root,
1434 pr.getResolver(src), customActions);
1435 } else {
1436 externalSrcDigester = newInstance(scxml,
1437 pr.getResolver(src), customActions);
1438 }
1439 }
1440
1441 try {
1442 externalSCXML = (SCXML) externalSrcDigester.parse(path);
1443 } catch (Exception e) {
1444 org.apache.commons.logging.Log log = LogFactory.
1445 getLog(SCXMLDigester.class);
1446 log.error(e.getMessage(), e);
1447 }
1448
1449 if (externalSCXML == null) {
1450 return;
1451 }
1452 State s = (State) digester.peek();
1453 Transition t = new Transition();
1454 t.setNext(externalSCXML.getInitialstate());
1455 Initial ini = new Initial();
1456 ini.setTransition(t);
1457 s.setInitial(ini);
1458 Map children = externalSCXML.getStates();
1459 Object[] ids = children.keySet().toArray();
1460 for (int i = 0; i < ids.length; i++) {
1461 s.addChild((State) children.get(ids[i]));
1462 }
1463 s.setDatamodel(externalSCXML.getDatamodel());
1464 }
1465 }
1466
1467 /***
1468 * Custom digestion rule for setting PathResolver for runtime retrieval.
1469 *
1470 * @deprecated Will be removed in version 1.0
1471 */
1472 public static class SetPathResolverRule extends Rule {
1473
1474 /***
1475 * The PathResolver to set.
1476 * @see PathResolver
1477 */
1478 private PathResolver pr;
1479
1480 /***
1481 * Constructor.
1482 * @param pr The PathResolver
1483 *
1484 * @see PathResolver
1485 */
1486 public SetPathResolverRule(final PathResolver pr) {
1487 super();
1488 this.pr = pr;
1489 }
1490
1491 /***
1492 * @see Rule#begin(String, String, Attributes)
1493 */
1494 public final void begin(final String namespace, final String name,
1495 final Attributes attributes) {
1496 PathResolverHolder prHolder = (PathResolverHolder) getDigester().
1497 peek();
1498 prHolder.setPathResolver(pr);
1499 }
1500 }
1501
1502 /***
1503 * Custom digestion rule for setting state parent of finalize.
1504 *
1505 * @deprecated Will be removed in version 1.0
1506 */
1507 public static class UpdateFinalizeRule extends Rule {
1508
1509 /***
1510 * @see Rule#begin(String, String, Attributes)
1511 */
1512 public final void begin(final String namespace, final String name,
1513 final Attributes attributes) {
1514 Finalize finalize = (Finalize) getDigester().peek();
1515
1516 TransitionTarget tt = (TransitionTarget) getDigester().peek(2);
1517 finalize.setParent(tt);
1518 }
1519 }
1520
1521
1522 /***
1523 * Custom digestion rule for attaching a snapshot of current namespaces
1524 * to SCXML actions for deferred XPath evaluation.
1525 *
1526 */
1527 private static class SetCurrentNamespacesRule extends Rule {
1528
1529 /***
1530 * @see Rule#begin(String, String, Attributes)
1531 */
1532 public final void begin(final String namespace, final String name,
1533 final Attributes attributes) {
1534 NamespacePrefixesHolder nsHolder =
1535 (NamespacePrefixesHolder) getDigester().peek();
1536 nsHolder.setNamespaces(getDigester().getCurrentNamespaces());
1537 }
1538 }
1539
1540 }
1541