View Javadoc

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