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