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.scxml2.io;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.Reader;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.net.URL;
25  import java.net.URLConnection;
26  import java.text.MessageFormat;
27  import java.util.ArrayList;
28  import java.util.EmptyStackException;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Stack;
33  
34  import javax.xml.parsers.DocumentBuilderFactory;
35  import javax.xml.parsers.ParserConfigurationException;
36  import javax.xml.stream.XMLInputFactory;
37  import javax.xml.stream.XMLReporter;
38  import javax.xml.stream.XMLResolver;
39  import javax.xml.stream.XMLStreamConstants;
40  import javax.xml.stream.XMLStreamException;
41  import javax.xml.stream.XMLStreamReader;
42  import javax.xml.stream.util.XMLEventAllocator;
43  import javax.xml.transform.Source;
44  import javax.xml.transform.stream.StreamSource;
45  import javax.xml.validation.Schema;
46  import javax.xml.validation.SchemaFactory;
47  import javax.xml.validation.Validator;
48  
49  import org.apache.commons.logging.LogFactory;
50  import org.apache.commons.scxml2.Evaluator;
51  import org.apache.commons.scxml2.PathResolver;
52  import org.apache.commons.scxml2.env.SimpleErrorHandler;
53  import org.apache.commons.scxml2.env.URLResolver;
54  import org.apache.commons.scxml2.model.Action;
55  import org.apache.commons.scxml2.model.ActionsContainer;
56  import org.apache.commons.scxml2.model.Assign;
57  import org.apache.commons.scxml2.model.Cancel;
58  import org.apache.commons.scxml2.model.Content;
59  import org.apache.commons.scxml2.model.ContentContainer;
60  import org.apache.commons.scxml2.model.CustomAction;
61  import org.apache.commons.scxml2.model.Data;
62  import org.apache.commons.scxml2.model.Datamodel;
63  import org.apache.commons.scxml2.model.Else;
64  import org.apache.commons.scxml2.model.ElseIf;
65  import org.apache.commons.scxml2.model.EnterableState;
66  import org.apache.commons.scxml2.model.Executable;
67  import org.apache.commons.scxml2.model.ExternalContent;
68  import org.apache.commons.scxml2.model.Final;
69  import org.apache.commons.scxml2.model.Finalize;
70  import org.apache.commons.scxml2.model.Foreach;
71  import org.apache.commons.scxml2.model.History;
72  import org.apache.commons.scxml2.model.If;
73  import org.apache.commons.scxml2.model.Initial;
74  import org.apache.commons.scxml2.model.Invoke;
75  import org.apache.commons.scxml2.model.Log;
76  import org.apache.commons.scxml2.model.ModelException;
77  import org.apache.commons.scxml2.model.NamespacePrefixesHolder;
78  import org.apache.commons.scxml2.model.OnEntry;
79  import org.apache.commons.scxml2.model.OnExit;
80  import org.apache.commons.scxml2.model.Parallel;
81  import org.apache.commons.scxml2.model.Param;
82  import org.apache.commons.scxml2.model.ParamsContainer;
83  import org.apache.commons.scxml2.model.Raise;
84  import org.apache.commons.scxml2.model.SCXML;
85  import org.apache.commons.scxml2.model.Script;
86  import org.apache.commons.scxml2.model.Send;
87  import org.apache.commons.scxml2.model.SimpleTransition;
88  import org.apache.commons.scxml2.model.State;
89  import org.apache.commons.scxml2.model.Transition;
90  import org.apache.commons.scxml2.model.TransitionType;
91  import org.apache.commons.scxml2.model.TransitionalState;
92  import org.apache.commons.scxml2.model.Var;
93  import org.w3c.dom.Attr;
94  import org.w3c.dom.Document;
95  import org.w3c.dom.Element;
96  import org.w3c.dom.Node;
97  import org.w3c.dom.NodeList;
98  import org.xml.sax.SAXException;
99  
100 /**
101  * <p>The SCXMLReader provides the ability to read a SCXML document into
102  * the Java object model provided in the model package.</p>
103  *
104  * <p>See latest version of the SCXML Working Draft for more details.</p>
105  *
106  * <p><b>NOTE:</b> The SCXMLReader assumes that the SCXML document to be
107  * parsed is well-formed and correct. If that assumption does not hold,
108  * any subsequent behavior is undefined.</p>
109  *
110  * @since 1.0
111  */
112 public final class SCXMLReader {
113 
114     //---------------------- PRIVATE CONSTANTS ----------------------//
115     //---- NAMESPACES ----//
116     /**
117      * The SCXML namespace that this Reader is built for. Any document
118      * that is intended to be parsed by this reader <b>must</b>
119      * bind the SCXML elements to this namespace.
120      */
121     private static final String XMLNS_SCXML =
122             "http://www.w3.org/2005/07/scxml";
123 
124     /**
125      * The namespace that defines any custom actions defined by the Commons
126      * SCXML implementation. Any document that intends to use these custom
127      * actions needs to ensure that they are in the correct namespace. Use
128      * of actions in this namespace makes the document non-portable across
129      * implementations.
130      */
131     private static final String XMLNS_COMMONS_SCXML =
132             "http://commons.apache.org/scxml";
133 
134     /**
135      * The version attribute value the SCXML element <em>must</em> have as stated by the spec: 3.2.1
136      */
137     private static final String SCXML_REQUIRED_VERSION = "1.0";
138     /**
139      * The default namespace for attributes.
140      */
141     private static final String XMLNS_DEFAULT = null;
142 
143     //---- ERROR MESSAGES ----//
144     /**
145      * Null URL passed as argument.
146      */
147     private static final String ERR_NULL_URL = "Cannot parse null URL";
148 
149     /**
150      * Null path passed as argument.
151      */
152     private static final String ERR_NULL_PATH = "Cannot parse null path";
153 
154     /**
155      * Null InputStream passed as argument.
156      */
157     private static final String ERR_NULL_ISTR = "Cannot parse null InputStream";
158 
159     /**
160      * Null Reader passed as argument.
161      */
162     private static final String ERR_NULL_READ = "Cannot parse null Reader";
163 
164     /**
165      * Null Source passed as argument.
166      */
167     private static final String ERR_NULL_SRC = "Cannot parse null Source";
168 
169     /**
170      * Error message while attempting to define a custom action which does
171      * not extend the Commons SCXML Action base class.
172      */
173     private static final String ERR_CUSTOM_ACTION_TYPE = "Custom actions list"
174             + " contained unknown object, class not a Commons SCXML Action class subtype: ";
175 
176     /**
177      * Parser configuration error while trying to parse stream to DOM node(s).
178      */
179     private static final String ERR_PARSER_CFG = "ParserConfigurationException while trying"
180             + " to parse stream into DOM node(s).";
181 
182     /**
183      * Error message when the URI in a &lt;state&gt;'s &quot;src&quot;
184      * attribute does not point to a valid SCXML document, and thus cannot be
185      * parsed.
186      */
187     private static final String ERR_STATE_SRC =
188             "Source attribute in <state src=\"{0}\"> cannot be parsed";
189 
190     /**
191      * Error message when the target of the URI fragment in a &lt;state&gt;'s
192      * &quot;src&quot; attribute is not defined in the referenced document.
193      */
194     private static final String ERR_STATE_SRC_FRAGMENT = "URI Fragment in "
195             + "<state src=\"{0}\"> is an unknown state in referenced document";
196 
197     /**
198      * Error message when the target of the URI fragment in a &lt;state&gt;'s
199      * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
200      * the referenced document.
201      */
202     private static final String ERR_STATE_SRC_FRAGMENT_TARGET = "URI Fragment"
203             + " in <state src=\"{0}\"> does not point to a <state> or <final>";
204 
205     /**
206      * Error message when the target of the URI fragment in a &lt;state&gt;'s
207      * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
208      * the referenced document.
209      */
210     private static final String ERR_REQUIRED_ATTRIBUTE_MISSING = "<{0}> is missing"
211             +" required attribute \"{1}\" value at {2}";
212 
213     /**
214      * Error message when the target of the URI fragment in a &lt;state&gt;'s
215      * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
216      * the referenced document.
217      */
218     private static final String ERR_ATTRIBUTE_NOT_BOOLEAN = "Illegal value \"{0}\""
219             + "for attribute \"{1}\" in element <{2}> at {3}."
220             +" Only the value \"true\" or \"false\" is allowed.";
221 
222     /**
223      * Error message when the element (state|parallel|final|history) uses an id value
224      * with the reserved prefix {@link SCXML#GENERATED_TT_ID_PREFIX}.
225      */
226     private static final String ERR_RESERVED_ID_PREFIX = "Reserved id prefix \""
227             +SCXML.GENERATED_TT_ID_PREFIX+"\" used for <{0} id=\"{1}\"> at {2}";
228 
229     /**
230      * Error message when the target of the URI fragment in a &lt;state&gt;'s
231      * &quot;src&quot; attribute is not defined in the referenced document.
232      */
233     private static final String ERR_UNSUPPORTED_TRANSITION_TYPE = "Unsupported transition type "
234             + "for <transition type=\"{0}\"> at {1}.";
235 
236     /**
237      * Error message when the target of the URI fragment in a &lt;state&gt;'s
238      * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
239      * the referenced document.
240      */
241     private static final String ERR_INVALID_VERSION = "The <scxml> element defines"
242             +" an unsupported version \"{0}\", only version \"1.0\" is supported.";
243 
244     //--------------------------- XML VOCABULARY ---------------------------//
245     //---- ELEMENT NAMES ----//
246     private static final String ELEM_ASSIGN = "assign";
247     private static final String ELEM_CANCEL = "cancel";
248     private static final String ELEM_CONTENT = "content";
249     private static final String ELEM_DATA = "data";
250     private static final String ELEM_DATAMODEL = "datamodel";
251     private static final String ELEM_ELSE = "else";
252     private static final String ELEM_ELSEIF = "elseif";
253     private static final String ELEM_RAISE = "raise";
254     private static final String ELEM_FINAL = "final";
255     private static final String ELEM_FINALIZE = "finalize";
256     private static final String ELEM_HISTORY = "history";
257     private static final String ELEM_IF = "if";
258     private static final String ELEM_INITIAL = "initial";
259     private static final String ELEM_INVOKE = "invoke";
260     private static final String ELEM_FOREACH = "foreach";
261     private static final String ELEM_LOG = "log";
262     private static final String ELEM_ONENTRY = "onentry";
263     private static final String ELEM_ONEXIT = "onexit";
264     private static final String ELEM_PARALLEL = "parallel";
265     private static final String ELEM_PARAM = "param";
266     private static final String ELEM_SCRIPT = "script";
267     private static final String ELEM_SCXML = "scxml";
268     private static final String ELEM_SEND = "send";
269     private static final String ELEM_STATE = "state";
270     private static final String ELEM_TRANSITION = "transition";
271     private static final String ELEM_VAR = "var";
272 
273     //---- ATTRIBUTE NAMES ----//
274     private static final String ATTR_ARRAY = "array";
275     private static final String ATTR_ATTR = "attr";
276     private static final String ATTR_AUTOFORWARD = "autoforward";
277     private static final String ATTR_COND = "cond";
278     private static final String ATTR_DATAMODEL = "datamodel";
279     private static final String ATTR_DELAY = "delay";
280     private static final String ATTR_DELAYEXPR = "delayexpr";
281     private static final String ATTR_EVENT = "event";
282     private static final String ATTR_EVENTEXPR = "eventexpr";
283     private static final String ATTR_EXMODE = "exmode";
284     private static final String ATTR_EXPR = "expr";
285     private static final String ATTR_HINTS = "hints";
286     private static final String ATTR_ID = "id";
287     private static final String ATTR_IDLOCATION = "idlocation";
288     private static final String ATTR_INDEX = "index";
289     private static final String ATTR_INITIAL = "initial";
290     private static final String ATTR_ITEM = "item";
291     private static final String ATTR_LABEL = "label";
292     private static final String ATTR_LOCATION = "location";
293     private static final String ATTR_NAME = "name";
294     private static final String ATTR_NAMELIST = "namelist";
295     private static final String ATTR_PROFILE = "profile";
296     private static final String ATTR_SENDID = "sendid";
297     private static final String ATTR_SENDIDEXPR = "sendidexpr";
298     private static final String ATTR_SRC = "src";
299     private static final String ATTR_SRCEXPR = "srcexpr";
300     private static final String ATTR_TARGET = "target";
301     private static final String ATTR_TARGETEXPR = "targetexpr";
302     private static final String ATTR_TYPE = "type";
303     private static final String ATTR_TYPEEXPR = "typeexpr";
304     private static final String ATTR_VERSION = "version";
305 
306     //------------------------- PUBLIC API METHODS -------------------------//
307     /*
308      * Public methods
309      */
310     /**
311      * Parse the SCXML document at the supplied path.
312      *
313      * @param scxmlPath The real path to the SCXML document.
314      *
315      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
316      *
317      * @throws IOException An IO error during parsing.
318      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
319      *                        errors in the SCXML document that may not be identified by the schema).
320      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
321      */
322     public static SCXML read(final String scxmlPath)
323             throws IOException, ModelException, XMLStreamException {
324 
325         return read(scxmlPath, new Configuration());
326     }
327 
328     /**
329      * Parse the SCXML document at the supplied path with the given {@link Configuration}.
330      *
331      * @param scxmlPath The real path to the SCXML document.
332      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
333      *
334      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
335      *
336      * @throws IOException An IO error during parsing.
337      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
338      *                        errors in the SCXML document that may not be identified by the schema).
339      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
340      */
341     public static SCXML read(final String scxmlPath, final Configuration configuration)
342             throws IOException, ModelException, XMLStreamException {
343 
344         if (scxmlPath == null) {
345             throw new IllegalArgumentException(ERR_NULL_PATH);
346         }
347         SCXML scxml = readInternal(configuration, null, scxmlPath, null, null, null);
348         if (scxml != null) {
349             ModelUpdater.updateSCXML(scxml);
350         }
351         return scxml;
352     }
353 
354     /**
355      * Parse the SCXML document at the supplied {@link URL}.
356      *
357      * @param scxmlURL The SCXML document {@link URL} to parse.
358      *
359      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
360      *
361      * @throws IOException An IO error during parsing.
362      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
363      *                        errors in the SCXML document that may not be identified by the schema).
364      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
365      */
366     public static SCXML read(final URL scxmlURL)
367             throws IOException, ModelException, XMLStreamException {
368 
369         return read(scxmlURL, new Configuration());
370     }
371 
372     /**
373      * Parse the SCXML document at the supplied {@link URL} with the given {@link Configuration}.
374      *
375      * @param scxmlURL The SCXML document {@link URL} to parse.
376      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
377      *
378      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
379      *
380      * @throws IOException An IO error during parsing.
381      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
382      *                        errors in the SCXML document that may not be identified by the schema).
383      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
384      */
385     public static SCXML read(final URL scxmlURL, final Configuration configuration)
386             throws IOException, ModelException, XMLStreamException {
387 
388         if (scxmlURL == null) {
389             throw new IllegalArgumentException(ERR_NULL_URL);
390         }
391         SCXML scxml = readInternal(configuration, scxmlURL, null, null, null, null);
392         if (scxml != null) {
393             ModelUpdater.updateSCXML(scxml);
394         }
395         return scxml;
396     }
397 
398     /**
399      * Parse the SCXML document supplied by the given {@link InputStream}.
400      *
401      * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
402      *
403      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
404      *
405      * @throws IOException An IO error during parsing.
406      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
407      *                        errors in the SCXML document that may not be identified by the schema).
408      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
409      */
410     public static SCXML read(final InputStream scxmlStream)
411             throws IOException, ModelException, XMLStreamException {
412 
413         return read(scxmlStream, new Configuration());
414     }
415 
416     /**
417      * Parse the SCXML document supplied by the given {@link InputStream} with the given {@link Configuration}.
418      *
419      * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
420      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
421      *
422      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
423      *
424      * @throws IOException An IO error during parsing.
425      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
426      *                        errors in the SCXML document that may not be identified by the schema).
427      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
428      */
429     public static SCXML read(final InputStream scxmlStream, final Configuration configuration)
430             throws IOException, ModelException, XMLStreamException {
431 
432         if (scxmlStream == null) {
433             throw new IllegalArgumentException(ERR_NULL_ISTR);
434         }
435         SCXML scxml = readInternal(configuration, null, null, scxmlStream, null, null);
436         if (scxml != null) {
437             ModelUpdater.updateSCXML(scxml);
438         }
439         return scxml;
440     }
441 
442     /**
443      * Parse the SCXML document supplied by the given {@link Reader}.
444      *
445      * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
446      *
447      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
448      *
449      * @throws IOException An IO error during parsing.
450      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
451      *                        errors in the SCXML document that may not be identified by the schema).
452      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
453      */
454     public static SCXML read(final Reader scxmlReader)
455             throws IOException, ModelException, XMLStreamException {
456 
457         return read(scxmlReader, new Configuration());
458     }
459 
460     /**
461      * Parse the SCXML document supplied by the given {@link Reader} with the given {@link Configuration}.
462      *
463      * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
464      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
465      *
466      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
467      *
468      * @throws IOException An IO error during parsing.
469      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
470      *                        errors in the SCXML document that may not be identified by the schema).
471      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
472      */
473     public static SCXML read(final Reader scxmlReader, final Configuration configuration)
474             throws IOException, ModelException, XMLStreamException {
475 
476         if (scxmlReader == null) {
477             throw new IllegalArgumentException(ERR_NULL_READ);
478         }
479         SCXML scxml = readInternal(configuration, null, null, null, scxmlReader, null);
480         if (scxml != null) {
481             ModelUpdater.updateSCXML(scxml);
482         }
483         return scxml;
484     }
485 
486     /**
487      * Parse the SCXML document supplied by the given {@link Source}.
488      *
489      * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
490      *
491      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
492      *
493      * @throws IOException An IO error during parsing.
494      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
495      *                        errors in the SCXML document that may not be identified by the schema).
496      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
497      */
498     public static SCXML read(final Source scxmlSource)
499             throws IOException, ModelException, XMLStreamException {
500 
501         return read(scxmlSource, new Configuration());
502     }
503 
504     /**
505      * Parse the SCXML document supplied by the given {@link Source} with the given {@link Configuration}.
506      *
507      * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
508      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
509      *
510      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
511      *
512      * @throws IOException An IO error during parsing.
513      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
514      *                        errors in the SCXML document that may not be identified by the schema).
515      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
516      */
517     public static SCXML read(final Source scxmlSource, final Configuration configuration)
518             throws IOException, ModelException, XMLStreamException {
519 
520         if (scxmlSource == null) {
521             throw new IllegalArgumentException(ERR_NULL_SRC);
522         }
523         SCXML scxml = readInternal(configuration, null, null, null, null, scxmlSource);
524         if (scxml != null) {
525             ModelUpdater.updateSCXML(scxml);
526         }
527         return scxml;
528     }
529 
530     //---------------------- PRIVATE UTILITY METHODS ----------------------//
531     /**
532      * Parse the SCXML document at the supplied {@link URL} using the supplied {@link Configuration}, but do not
533      * wire up the object model to be usable just yet. Exactly one of the url, path, stream, reader or source
534      * parameters must be provided.
535      *
536      * @param configuration The {@link Configuration} to use when parsing the SCXML document.
537      * @param scxmlURL The optional SCXML document {@link URL} to parse.
538      * @param scxmlPath The optional real path to the SCXML document as a string.
539      * @param scxmlStream The optional {@link InputStream} providing the SCXML document.
540      * @param scxmlReader The optional {@link Reader} providing the SCXML document.
541      * @param scxmlSource The optional {@link Source} providing the SCXML document.
542      *
543      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
544      *         (not wired up to be immediately usable).
545      *
546      * @throws IOException An IO error during parsing.
547      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
548      *                        errors in the SCXML document that may not be identified by the schema).
549      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
550      */
551     private static SCXML readInternal(final Configuration configuration, final URL scxmlURL, final String scxmlPath,
552                                       final InputStream scxmlStream, final Reader scxmlReader, final Source scxmlSource)
553             throws IOException, ModelException, XMLStreamException {
554 
555         if (configuration.pathResolver == null) {
556             if (scxmlURL != null) {
557                 configuration.pathResolver = new URLResolver(scxmlURL);
558             } else if (scxmlPath != null) {
559                 configuration.pathResolver = new URLResolver(new URL(scxmlPath));
560             }
561         }
562 
563         XMLStreamReader reader = getReader(configuration, scxmlURL, scxmlPath, scxmlStream, scxmlReader, scxmlSource);
564 
565         return readDocument(reader, configuration);
566     }
567 
568     /*
569      * Private utility functions for reading the SCXML document.
570      */
571     /**
572      * Read the SCXML document through the {@link XMLStreamReader}.
573      *
574      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
575      * @param configuration The {@link Configuration} to use while parsing.
576      *
577      * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
578      *         (not wired up to be immediately usable).
579      *
580      * @throws IOException An IO error during parsing.
581      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
582      *                        errors in the SCXML document that may not be identified by the schema).
583      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
584      */
585     private static SCXML readDocument(final XMLStreamReader reader, final Configuration configuration)
586             throws IOException, ModelException, XMLStreamException {
587 
588         SCXML scxml = new SCXML();
589         while (reader.hasNext()) {
590             String name, nsURI;
591             switch (reader.next()) {
592                 case XMLStreamConstants.START_ELEMENT:
593                     pushNamespaces(reader, configuration);
594                     nsURI = reader.getNamespaceURI();
595                     name = reader.getLocalName();
596                     if (XMLNS_SCXML.equals(nsURI)) {
597                         if (ELEM_SCXML.equals(name)) {
598                             readSCXML(reader, configuration, scxml);
599                         } else {
600                             reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
601                         }
602                     } else {
603                         reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
604                     }
605                     break;
606                 case XMLStreamConstants.NAMESPACE:
607                     System.err.println(reader.getNamespaceCount());
608                     break;
609                 default:
610             }
611         }
612         return scxml;
613     }
614 
615     /**
616      * Read the contents of this &lt;scxml&gt; element.
617      *
618      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
619      * @param configuration The {@link Configuration} to use while parsing.
620      * @param scxml The root of the object model being parsed.
621      *
622      * @throws IOException An IO error during parsing.
623      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
624      *                        errors in the SCXML document that may not be identified by the schema).
625      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
626      */
627     private static void readSCXML(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml)
628             throws IOException, ModelException, XMLStreamException {
629 
630         scxml.setDatamodelName(readAV(reader, ATTR_DATAMODEL));
631         scxml.setExmode(readAV(reader, ATTR_EXMODE));
632         scxml.setInitial(readAV(reader, ATTR_INITIAL));
633         scxml.setName(readAV(reader, ATTR_NAME));
634         scxml.setProfile(readAV(reader, ATTR_PROFILE));
635         scxml.setVersion(readRequiredAV(reader, ELEM_SCXML, ATTR_VERSION));
636         if (!SCXML_REQUIRED_VERSION.equals(scxml.getVersion())) {
637             throw new ModelException(new MessageFormat(ERR_INVALID_VERSION).format(new Object[] {scxml.getVersion()}));
638         }
639         readNamespaces(configuration, scxml);
640 
641         boolean hasGlobalScript = false;
642 
643         loop : while (reader.hasNext()) {
644             String name, nsURI;
645             switch (reader.next()) {
646                 case XMLStreamConstants.START_ELEMENT:
647                     pushNamespaces(reader, configuration);
648                     nsURI = reader.getNamespaceURI();
649                     name = reader.getLocalName();
650                     if (XMLNS_SCXML.equals(nsURI)) {
651                         if (ELEM_STATE.equals(name)) {
652                             readState(reader, configuration, scxml, null);
653                         } else if (ELEM_PARALLEL.equals(name)) {
654                             readParallel(reader, configuration, scxml, null);
655                         } else if (ELEM_FINAL.equals(name)) {
656                             readFinal(reader, configuration, scxml, null);
657                         } else if (ELEM_DATAMODEL.equals(name)) {
658                             readDatamodel(reader, configuration, scxml, null);
659                         } else if (ELEM_SCRIPT.equals(name) && !hasGlobalScript) {
660                             readGlobalScript(reader, configuration, scxml);
661                             hasGlobalScript = true;
662                         } else {
663                             reportIgnoredElement(reader, configuration, ELEM_SCXML, nsURI, name);
664                         }
665                     } else {
666                         reportIgnoredElement(reader, configuration, ELEM_SCXML, nsURI, name);
667                     }
668                     break;
669                 case XMLStreamConstants.END_ELEMENT:
670                     popNamespaces(reader, configuration);
671                     break loop;
672                 default:
673             }
674         }
675     }
676 
677     /**
678      * Read the contents of this &lt;state&gt; element.
679      *
680      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
681      * @param configuration The {@link Configuration} to use while parsing.
682      * @param scxml The root of the object model being parsed.
683      * @param parent The parent {@link TransitionalState} for this state (null for top level state).
684      *
685      * @throws IOException An IO error during parsing.
686      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
687      *                        errors in the SCXML document that may not be identified by the schema).
688      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
689      */
690     private static void readState(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
691                                   final TransitionalState parent)
692             throws IOException, ModelException, XMLStreamException {
693 
694         State state = new State();
695         state.setId(readOrGeneratedTransitionTargetId(reader, scxml, ELEM_STATE));
696         String initial = readAV(reader, ATTR_INITIAL);
697         if (initial != null) {
698             state.setFirst(initial);
699         }
700         String src = readAV(reader, ATTR_SRC);
701         if (src != null) {
702             String source = src;
703             Configuration copy = new Configuration(configuration);
704             if (copy.parent == null) {
705                 copy.parent = scxml;
706             }
707             if (configuration.pathResolver != null) {
708                 source = configuration.pathResolver.resolvePath(src);
709                 copy.pathResolver = configuration.pathResolver.getResolver(src);
710             }
711             readTransitionalStateSrc(copy, source, state);
712         }
713 
714         if (parent == null) {
715             scxml.addChild(state);
716         } else if (parent instanceof State) {
717             ((State)parent).addChild(state);
718         }
719         else {
720             ((Parallel)parent).addChild(state);
721         }
722         scxml.addTarget(state);
723         if (configuration.parent != null) {
724             configuration.parent.addTarget(state);
725         }
726 
727         loop : while (reader.hasNext()) {
728             String name, nsURI;
729             switch (reader.next()) {
730                 case XMLStreamConstants.START_ELEMENT:
731                     pushNamespaces(reader, configuration);
732                     nsURI = reader.getNamespaceURI();
733                     name = reader.getLocalName();
734                     if (XMLNS_SCXML.equals(nsURI)) {
735                         if (ELEM_TRANSITION.equals(name)) {
736                             state.addTransition(readTransition(reader, configuration));
737                         } else if (ELEM_STATE.equals(name)) {
738                             readState(reader, configuration, scxml, state);
739                         } else if (ELEM_INITIAL.equals(name)) {
740                             readInitial(reader, configuration, state);
741                         } else if (ELEM_FINAL.equals(name)) {
742                             readFinal(reader, configuration, scxml, state);
743                         } else if (ELEM_ONENTRY.equals(name)) {
744                             readOnEntry(reader, configuration, state);
745                         } else if (ELEM_ONEXIT.equals(name)) {
746                             readOnExit(reader, configuration, state);
747                         } else if (ELEM_PARALLEL.equals(name)) {
748                             readParallel(reader, configuration, scxml, state);
749                         } else if (ELEM_DATAMODEL.equals(name)) {
750                             readDatamodel(reader, configuration, null, state);
751                         } else if (ELEM_INVOKE.equals(name)) {
752                             readInvoke(reader, configuration, state);
753                         } else if (ELEM_HISTORY.equals(name)) {
754                             readHistory(reader, configuration, scxml, state);
755                         } else {
756                             reportIgnoredElement(reader, configuration, ELEM_STATE, nsURI, name);
757                         }
758                     } else {
759                         reportIgnoredElement(reader, configuration, ELEM_STATE, nsURI, name);
760                     }
761                     break;
762                 case XMLStreamConstants.END_ELEMENT:
763                     popNamespaces(reader, configuration);
764                     break loop;
765                 default:
766             }
767         }
768     }
769 
770     /**
771      * Read the contents of this &lt;parallel&gt; element.
772      *
773      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
774      * @param configuration The {@link Configuration} to use while parsing.
775      * @param scxml The root of the object model being parsed.
776      * @param parent The parent {@link TransitionalState} for this parallel (null for top level state).
777      *
778      * @throws IOException An IO error during parsing.
779      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
780      *                        errors in the SCXML document that may not be identified by the schema).
781      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
782      */
783     private static void readParallel(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
784                                      final TransitionalState parent)
785             throws IOException, ModelException, XMLStreamException {
786 
787         Parallel parallel = new Parallel();
788         parallel.setId(readOrGeneratedTransitionTargetId(reader, scxml, ELEM_PARALLEL));
789         String src = readAV(reader, ATTR_SRC);
790         if (src != null) {
791             String source = src;
792             Configuration copy = new Configuration(configuration);
793             if (copy.parent == null) {
794                 copy.parent = scxml;
795             }
796             if (configuration.pathResolver != null) {
797                 source = configuration.pathResolver.resolvePath(src);
798                 copy.pathResolver = configuration.pathResolver.getResolver(src);
799             }
800             readTransitionalStateSrc(copy, source, parallel);
801         }
802 
803         if (parent == null) {
804             scxml.addChild(parallel);
805         } else if (parent instanceof State) {
806             ((State)parent).addChild(parallel);
807         }
808         else {
809             ((Parallel)parent).addChild(parallel);
810         }
811         scxml.addTarget(parallel);
812         if (configuration.parent != null) {
813             configuration.parent.addTarget(parallel);
814         }
815 
816         loop : while (reader.hasNext()) {
817             String name, nsURI;
818             switch (reader.next()) {
819                 case XMLStreamConstants.START_ELEMENT:
820                     pushNamespaces(reader, configuration);
821                     nsURI = reader.getNamespaceURI();
822                     name = reader.getLocalName();
823                     if (XMLNS_SCXML.equals(nsURI)) {
824                         if (ELEM_TRANSITION.equals(name)) {
825                             parallel.addTransition(readTransition(reader, configuration));
826                         } else if (ELEM_STATE.equals(name)) {
827                             readState(reader, configuration, scxml, parallel);
828                         } else if (ELEM_PARALLEL.equals(name)) {
829                             readParallel(reader, configuration, scxml, parallel);
830                         } else if (ELEM_ONENTRY.equals(name)) {
831                             readOnEntry(reader, configuration, parallel);
832                         } else if (ELEM_ONEXIT.equals(name)) {
833                             readOnExit(reader, configuration, parallel);
834                         } else if (ELEM_DATAMODEL.equals(name)) {
835                             readDatamodel(reader, configuration, null, parallel);
836                         } else if (ELEM_INVOKE.equals(name)) {
837                             readInvoke(reader, configuration, parallel);
838                         } else if (ELEM_HISTORY.equals(name)) {
839                             readHistory(reader, configuration, scxml, parallel);
840                         } else {
841                             reportIgnoredElement(reader, configuration, ELEM_PARALLEL, nsURI, name);
842                         }
843                     } else {
844                         reportIgnoredElement(reader, configuration, ELEM_PARALLEL, nsURI, name);
845                     }
846                     break;
847                 case XMLStreamConstants.END_ELEMENT:
848                     popNamespaces(reader, configuration);
849                     break loop;
850                 default:
851             }
852         }
853     }
854 
855     /**
856      * Read the contents of this &lt;final&gt; element.
857      *
858      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
859      * @param configuration The {@link Configuration} to use while parsing.
860      * @param scxml The root of the object model being parsed.
861      * @param parent The parent {@link State} for this final (null for top level state).
862      *
863      * @throws IOException An IO error during parsing.
864      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
865      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
866      *                        errors in the SCXML document that may not be identified by the schema).
867      */
868     private static void readFinal(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
869                                   final State parent)
870             throws XMLStreamException, ModelException, IOException {
871 
872         Final end = new Final();
873         end.setId(readOrGeneratedTransitionTargetId(reader, scxml, ELEM_FINAL));
874 
875         if (parent == null) {
876             scxml.addChild(end);
877         } else {
878             parent.addChild(end);
879         }
880 
881         scxml.addTarget(end);
882         if (configuration.parent != null) {
883             configuration.parent.addTarget(end);
884         }
885 
886         loop : while (reader.hasNext()) {
887             String name, nsURI;
888             switch (reader.next()) {
889                 case XMLStreamConstants.START_ELEMENT:
890                     pushNamespaces(reader, configuration);
891                     nsURI = reader.getNamespaceURI();
892                     name = reader.getLocalName();
893                     if (XMLNS_SCXML.equals(nsURI)) {
894                         if (ELEM_ONENTRY.equals(name)) {
895                             readOnEntry(reader, configuration, end);
896                         } else if (ELEM_ONEXIT.equals(name)) {
897                             readOnExit(reader, configuration, end);
898                         } else {
899                             reportIgnoredElement(reader, configuration, ELEM_FINAL, nsURI, name);
900                         }
901                     } else {
902                         reportIgnoredElement(reader, configuration, ELEM_FINAL, nsURI, name);
903                     }
904                     break;
905                 case XMLStreamConstants.END_ELEMENT:
906                     popNamespaces(reader, configuration);
907                     break loop;
908                 default:
909             }
910         }
911     }
912 
913     /**
914      * Parse the contents of the SCXML document that this "src" attribute value of a &lt;state&gt; or &lt;parallel&gt;
915      * element points to. Without a URL fragment, the entire state machine is imported as contents of the
916      * &lt;state&gt; or &lt;parallel&gt;. If a URL fragment is present, the fragment must specify the id of the
917      * corresponding &lt;state&gt; or &lt;parallel&gt; to import.
918      *
919      * @param configuration The {@link Configuration} to use while parsing.
920      * @param src The "src" attribute value.
921      * @param ts The parent {@link TransitionalState} that specifies this "src" attribute.
922      *
923      * @throws IOException An IO error during parsing.
924      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
925      *                        errors in the SCXML document that may not be identified by the schema).
926      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
927      */
928     private static void readTransitionalStateSrc(final Configuration configuration, final String src,
929                                                  final TransitionalState ts)
930             throws IOException, ModelException, XMLStreamException {
931 
932         // Check for URI fragment
933         String[] fragments = src.split("#", 2);
934         String location = fragments[0];
935         String fragment = null;
936         if (fragments.length > 1) {
937             fragment = fragments[1];
938         }
939 
940         // Parse external document
941         SCXML externalSCXML;
942         try {
943             externalSCXML = SCXMLReader.readInternal(configuration, new URL(location), null, null, null, null);
944         } catch (Exception e) {
945             MessageFormat msgFormat = new MessageFormat(ERR_STATE_SRC);
946             String errMsg = msgFormat.format(new Object[] {src});
947             throw new ModelException(errMsg + " : " + e.getMessage(), e);
948         }
949 
950         // Pull in the parts of the external document as needed
951         if (fragment == null) {
952             // All targets pulled in since its not a src fragment
953             if (ts instanceof State) {
954                 State s = (State) ts;
955                 Initial ini = new Initial();
956                 SimpleTransition t = new SimpleTransition();
957                 t.setNext(externalSCXML.getInitial());
958                 ini.setTransition(t);
959                 s.setInitial(ini);
960                 for (EnterableState child : externalSCXML.getChildren()) {
961                     s.addChild(child);
962                 }
963                 s.setDatamodel(externalSCXML.getDatamodel());
964             } else if (ts instanceof Parallel) {
965                 // TODO src attribute for <parallel>
966             }
967         } else {
968             // Need to pull in only descendent targets
969             Object source = externalSCXML.getTargets().get(fragment);
970             if (source == null) {
971                 MessageFormat msgFormat = new MessageFormat(ERR_STATE_SRC_FRAGMENT);
972                 String errMsg = msgFormat.format(new Object[] {src});
973                 throw new ModelException(errMsg);
974             }
975             if (source instanceof State && ts instanceof State) {
976                 State s = (State) ts;
977                 State include = (State) source;
978                 for (OnEntry onentry : include.getOnEntries()) {
979                     s.addOnEntry(onentry);
980                 }
981                 for (OnExit onexit : include.getOnExits()) {
982                     s.addOnExit(onexit);
983                 }
984                 s.setDatamodel(include.getDatamodel());
985                 List<History> histories = include.getHistory();
986                 for (History h : histories) {
987                     s.addHistory(h);
988                     configuration.parent.addTarget(h);
989                 }
990                 for (EnterableState child : include.getChildren()) {
991                     s.addChild(child);
992                     configuration.parent.addTarget(child);
993                     readInExternalTargets(configuration.parent, child);
994                 }
995                 for (Invoke invoke : include.getInvokes()) {
996                     s.addInvoke(invoke);
997                 }
998                 if (include.getInitial() != null) {
999                     s.setInitial(include.getInitial());
1000                 }
1001                 List<Transition> transitions = include.getTransitionsList();
1002                 for (Transition t : transitions) {
1003                     s.addTransition(t);
1004                 }
1005             } else if (ts instanceof Parallel && source instanceof Parallel) {
1006                 // TODO src attribute for <parallel>
1007             } else {
1008                 MessageFormat msgFormat =
1009                         new MessageFormat(ERR_STATE_SRC_FRAGMENT_TARGET);
1010                 String errMsg = msgFormat.format(new Object[] {src});
1011                 throw new ModelException(errMsg);
1012             }
1013         }
1014     }
1015 
1016     /**
1017      * Add all the nested targets from given target to given parent state machine.
1018      *
1019      * @param parent The state machine
1020      * @param es The target to import
1021      */
1022     private static void readInExternalTargets(final SCXML parent, final EnterableState es) {
1023         if (es instanceof TransitionalState) {
1024             for (History h : ((TransitionalState)es).getHistory()) {
1025                 parent.addTarget(h);
1026             }
1027             for (EnterableState child : ((TransitionalState) es).getChildren()) {
1028                 parent.addTarget(child);
1029                 readInExternalTargets(parent, child);
1030             }
1031         }
1032     }
1033 
1034     /**
1035      * Read the contents of this &lt;datamodel&gt; element.
1036      *
1037      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1038      * @param configuration The {@link Configuration} to use while parsing.
1039      * @param scxml The root of the object model being parsed.
1040      * @param parent The parent {@link TransitionalState} for this datamodel (null for top level).
1041      *
1042      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1043      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1044      *                        errors in the SCXML document that may not be identified by the schema).
1045      */
1046     private static void readDatamodel(final XMLStreamReader reader, final Configuration configuration,
1047                                       final SCXML scxml, final TransitionalState parent)
1048             throws XMLStreamException, ModelException {
1049 
1050         Datamodel dm = new Datamodel();
1051 
1052         loop : while (reader.hasNext()) {
1053             String name, nsURI;
1054             switch (reader.next()) {
1055                 case XMLStreamConstants.START_ELEMENT:
1056                     pushNamespaces(reader, configuration);
1057                     nsURI = reader.getNamespaceURI();
1058                     name = reader.getLocalName();
1059                     if (XMLNS_SCXML.equals(nsURI)) {
1060                         if (ELEM_DATA.equals(name)) {
1061                             readData(reader, configuration, dm);
1062                         } else {
1063                             reportIgnoredElement(reader, configuration, ELEM_DATAMODEL, nsURI, name);
1064                         }
1065                     } else {
1066                         reportIgnoredElement(reader, configuration, ELEM_DATAMODEL, nsURI, name);
1067                     }
1068                     break;
1069                 case XMLStreamConstants.END_ELEMENT:
1070                     popNamespaces(reader, configuration);
1071                     break loop;
1072                 default:
1073             }
1074         }
1075 
1076         if (parent == null) {
1077             scxml.setDatamodel(dm);
1078         } else {
1079             parent.setDatamodel(dm);
1080         }
1081     }
1082 
1083     /**
1084      * Read the contents of this &lt;data&gt; element.
1085      *
1086      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1087      * @param configuration The {@link Configuration} to use while parsing.
1088      * @param dm The parent {@link Datamodel} for this data.
1089      *
1090      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1091      */
1092     private static void readData(final XMLStreamReader reader, final Configuration configuration, final Datamodel dm)
1093             throws XMLStreamException, ModelException {
1094 
1095         Data datum = new Data();
1096         datum.setId(readRequiredAV(reader, ELEM_DATA, ATTR_ID));
1097         datum.setExpr(readAV(reader, ATTR_EXPR));
1098         readNamespaces(configuration, datum);
1099         datum.setNode(readNode(reader, configuration, XMLNS_SCXML, ELEM_DATA, new String[]{"id"}));
1100         dm.addData(datum);
1101     }
1102 
1103     /**
1104      * Read the contents of this &lt;invoke&gt; element.
1105      *
1106      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1107      * @param configuration The {@link Configuration} to use while parsing.
1108      * @param parent The parent {@link TransitionalState} for this invoke.
1109      *
1110      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1111      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1112      *                        errors in the SCXML document that may not be identified by the schema).
1113      */
1114     private static void readInvoke(final XMLStreamReader reader, final Configuration configuration,
1115                                    final TransitionalState parent)
1116             throws XMLStreamException, ModelException {
1117 
1118         Invoke invoke = new Invoke();
1119         invoke.setId(readAV(reader, ATTR_ID));
1120         invoke.setSrc(readAV(reader, ATTR_SRC));
1121         invoke.setSrcexpr(readAV(reader, ATTR_SRCEXPR));
1122         invoke.setType(readAV(reader, ATTR_TYPE));
1123         invoke.setAutoForward(readBooleanAV(reader, ELEM_INVOKE, ATTR_AUTOFORWARD));
1124         invoke.setPathResolver(configuration.pathResolver);
1125         readNamespaces(configuration, invoke);
1126 
1127         loop : while (reader.hasNext()) {
1128             String name, nsURI;
1129             switch (reader.next()) {
1130                 case XMLStreamConstants.START_ELEMENT:
1131                     pushNamespaces(reader, configuration);
1132                     nsURI = reader.getNamespaceURI();
1133                     name = reader.getLocalName();
1134                     if (XMLNS_SCXML.equals(nsURI)) {
1135                         if (ELEM_PARAM.equals(name)) {
1136                             readParam(reader, configuration, invoke);
1137                         } else if (ELEM_FINALIZE.equals(name)) {
1138                             readFinalize(reader, configuration, parent, invoke);
1139                         } else if (ELEM_CONTENT.equals(name)) {
1140                             readContent(reader, configuration, invoke);
1141                         } else {
1142                             reportIgnoredElement(reader, configuration, ELEM_INVOKE, nsURI, name);
1143                         }
1144                     } else {
1145                         reportIgnoredElement(reader, configuration, ELEM_INVOKE, nsURI, name);
1146                     }
1147                     break;
1148                 case XMLStreamConstants.END_ELEMENT:
1149                     popNamespaces(reader, configuration);
1150                     break loop;
1151                 default:
1152             }
1153         }
1154 
1155         parent.addInvoke(invoke);
1156     }
1157 
1158     /**
1159      * Read the contents of this &lt;param&gt; element.
1160      *
1161      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1162      * @param configuration The {@link Configuration} to use while parsing.
1163      * @param parent The parent {@link org.apache.commons.scxml2.model.ParamsContainer} for this param.
1164      *
1165      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1166      */
1167     private static void readParam(final XMLStreamReader reader, final Configuration configuration,
1168                                   final ParamsContainer parent)
1169             throws XMLStreamException, ModelException {
1170 
1171         Param param = new Param();
1172         param.setName(readRequiredAV(reader, ELEM_PARAM, ATTR_NAME));
1173         String location = readAV(reader, ATTR_LOCATION);
1174         String expr = readAV(reader, ATTR_EXPR);
1175         if (expr != null) {
1176             if (location != null) {
1177                 reportConflictingAttribute(reader, configuration, ELEM_PARAM, ATTR_LOCATION, ATTR_EXPR);
1178             }
1179             else {
1180                 param.setExpr(expr);
1181             }
1182         }
1183         else if (location == null) {
1184             // force error missing required location or expr: use location attr for this
1185             param.setLocation(readRequiredAV(reader, ELEM_PARAM, ATTR_LOCATION));
1186         }
1187         else {
1188             param.setLocation(location);
1189         }
1190         readNamespaces(configuration, param);
1191         parent.getParams().add(param);
1192         skipToEndElement(reader);
1193     }
1194 
1195     /**
1196      * Read the contents of this &lt;finalize&gt; element.
1197      *
1198      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1199      * @param configuration The {@link Configuration} to use while parsing.
1200      * @param state The {@link TransitionalState} which contains the parent {@link Invoke}.
1201      * @param invoke The parent {@link Invoke} for this finalize.
1202      *
1203      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1204      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1205      *                        errors in the SCXML document that may not be identified by the schema).
1206      */
1207     private static void readFinalize(final XMLStreamReader reader, final Configuration configuration,
1208                                      final TransitionalState state, final Invoke invoke)
1209             throws XMLStreamException, ModelException {
1210 
1211         Finalize finalize = new Finalize();
1212         readExecutableContext(reader, configuration, finalize, null);
1213         invoke.setFinalize(finalize);
1214         finalize.setParent(state);
1215     }
1216 
1217     /**
1218      * Read the contents of this &lt;content&gt; element.
1219      *
1220      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1221      * @param configuration The {@link Configuration} to use while parsing.
1222      * @param contentContainer The {@link ContentContainer} for this content.
1223      *
1224      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1225      */
1226     private static void readContent(final XMLStreamReader reader, final Configuration configuration,
1227                                     final ContentContainer contentContainer)
1228             throws XMLStreamException {
1229 
1230         Content content = new Content();
1231         content.setExpr(readAV(reader, ATTR_EXPR));
1232         if (content.getExpr() != null) {
1233             skipToEndElement(reader);
1234         }
1235         else {
1236             Node body = readNode(reader, configuration, XMLNS_SCXML, ELEM_CONTENT, new String[]{});
1237             if (body.hasChildNodes()) {
1238                 NodeList children = body.getChildNodes();
1239                 if (children.getLength() == 1 && children.item(0).getNodeType() == Node.TEXT_NODE) {
1240                     content.setBody(children.item(0).getNodeValue());
1241                 }
1242                 else {
1243                     content.setBody(body);
1244                 }
1245             }
1246         }
1247         contentContainer.setContent(content);
1248     }
1249 
1250     /**
1251      * Read the contents of this &lt;initial&gt; element.
1252      *
1253      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1254      * @param configuration The {@link Configuration} to use while parsing.
1255      * @param state The parent composite {@link State} for this initial.
1256      *
1257      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1258      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1259      *                        errors in the SCXML document that may not be identified by the schema).
1260      */
1261     private static void readInitial(final XMLStreamReader reader, final Configuration configuration,
1262                                     final State state)
1263             throws XMLStreamException, ModelException {
1264 
1265         Initial initial = new Initial();
1266 
1267         loop : while (reader.hasNext()) {
1268             String name, nsURI;
1269             switch (reader.next()) {
1270                 case XMLStreamConstants.START_ELEMENT:
1271                     pushNamespaces(reader, configuration);
1272                     nsURI = reader.getNamespaceURI();
1273                     name = reader.getLocalName();
1274                     if (XMLNS_SCXML.equals(nsURI)) {
1275                         if (ELEM_TRANSITION.equals(name)) {
1276                             initial.setTransition(readSimpleTransition(reader, configuration));
1277                         } else {
1278                             reportIgnoredElement(reader, configuration, ELEM_INITIAL, nsURI, name);
1279                         }
1280                     } else {
1281                         reportIgnoredElement(reader, configuration, ELEM_INITIAL, nsURI, name);
1282                     }
1283                     break;
1284                 case XMLStreamConstants.END_ELEMENT:
1285                     popNamespaces(reader, configuration);
1286                     break loop;
1287                 default:
1288             }
1289         }
1290 
1291         state.setInitial(initial);
1292     }
1293 
1294     /**
1295      * Read the contents of this &lt;history&gt; element.
1296      *
1297      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1298      * @param configuration The {@link Configuration} to use while parsing.
1299      * @param scxml The root of the object model being parsed.
1300      * @param ts The parent {@link org.apache.commons.scxml2.model.TransitionalState} for this history.
1301      *
1302      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1303      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1304      *                        errors in the SCXML document that may not be identified by the schema).
1305      */
1306     private static void readHistory(final XMLStreamReader reader, final Configuration configuration,
1307                                     final SCXML scxml, final TransitionalState ts)
1308             throws XMLStreamException, ModelException {
1309 
1310         History history = new History();
1311         history.setId(readOrGeneratedTransitionTargetId(reader, scxml, ELEM_HISTORY));
1312         history.setType(readAV(reader, ATTR_TYPE));
1313 
1314         ts.addHistory(history);
1315         scxml.addTarget(history);
1316 
1317         loop : while (reader.hasNext()) {
1318             String name, nsURI;
1319             switch (reader.next()) {
1320                 case XMLStreamConstants.START_ELEMENT:
1321                     pushNamespaces(reader, configuration);
1322                     nsURI = reader.getNamespaceURI();
1323                     name = reader.getLocalName();
1324                     if (XMLNS_SCXML.equals(nsURI)) {
1325                         if (ELEM_TRANSITION.equals(name)) {
1326                             history.setTransition(readTransition(reader, configuration));
1327                         } else {
1328                             reportIgnoredElement(reader, configuration, ELEM_HISTORY, nsURI, name);
1329                         }
1330                     } else {
1331                         reportIgnoredElement(reader, configuration, ELEM_HISTORY, nsURI, name);
1332                     }
1333                     break;
1334                 case XMLStreamConstants.END_ELEMENT:
1335                     popNamespaces(reader, configuration);
1336                     break loop;
1337                 default:
1338             }
1339         }
1340     }
1341 
1342     /**
1343      * Read the contents of this &lt;onentry&gt; element.
1344      *
1345      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1346      * @param configuration The {@link Configuration} to use while parsing.
1347      * @param es The parent {@link EnterableState} for this onentry.
1348      *
1349      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1350      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1351      *                        errors in the SCXML document that may not be identified by the schema).
1352      */
1353     private static void readOnEntry(final XMLStreamReader reader, final Configuration configuration,
1354                                     final EnterableState es)
1355             throws XMLStreamException, ModelException {
1356 
1357         OnEntry onentry = new OnEntry();
1358         onentry.setRaiseEvent(readBooleanAV(reader, ELEM_ONENTRY, ATTR_EVENT));
1359         readExecutableContext(reader, configuration, onentry, null);
1360         es.addOnEntry(onentry);
1361     }
1362 
1363     /**
1364      * Read the contents of this &lt;onexit&gt; element.
1365      *
1366      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1367      * @param configuration The {@link Configuration} to use while parsing.
1368      * @param es The parent {@link EnterableState} for this onexit.
1369      *
1370      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1371      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1372      *                        errors in the SCXML document that may not be identified by the schema).
1373      */
1374     private static void readOnExit(final XMLStreamReader reader, final Configuration configuration,
1375                                    final EnterableState es)
1376             throws XMLStreamException, ModelException {
1377 
1378         OnExit onexit = new OnExit();
1379         onexit.setRaiseEvent(readBooleanAV(reader, ELEM_ONEXIT, ATTR_EVENT));
1380         readExecutableContext(reader, configuration, onexit, null);
1381         es.addOnExit(onexit);
1382     }
1383 
1384     /**
1385      * Read the contents of this simple &lt;transition&gt; element.
1386      *
1387      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1388      * @param configuration The {@link Configuration} to use while parsing.
1389      *
1390      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1391      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1392      *                        errors in the SCXML document that may not be identified by the schema).
1393      */
1394     private static SimpleTransition readSimpleTransition(final XMLStreamReader reader, final Configuration configuration)
1395             throws XMLStreamException, ModelException {
1396 
1397         SimpleTransition transition = new SimpleTransition();
1398         transition.setNext(readAV(reader, ATTR_TARGET));
1399         String type = readAV(reader, ATTR_TYPE);
1400         if (type != null) {
1401             try {
1402                 transition.setType(TransitionType.valueOf(type));
1403             }
1404             catch (IllegalArgumentException e) {
1405                 MessageFormat msgFormat = new MessageFormat(ERR_UNSUPPORTED_TRANSITION_TYPE);
1406                 String errMsg = msgFormat.format(new Object[] {type, reader.getLocation()});
1407                 throw new ModelException(errMsg);
1408             }
1409         }
1410 
1411         readNamespaces(configuration, transition);
1412         readExecutableContext(reader, configuration, transition, null);
1413 
1414         return transition;
1415     }
1416 
1417     /**
1418      * Read the contents of this &lt;transition&gt; element.
1419      *
1420      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1421      * @param configuration The {@link Configuration} to use while parsing.
1422      *
1423      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1424      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1425      *                        errors in the SCXML document that may not be identified by the schema).
1426      */
1427     private static Transition readTransition(final XMLStreamReader reader, final Configuration configuration)
1428             throws XMLStreamException, ModelException {
1429 
1430         Transition transition = new Transition();
1431         transition.setCond(readAV(reader, ATTR_COND));
1432         transition.setEvent(readAV(reader, ATTR_EVENT));
1433         transition.setNext(readAV(reader, ATTR_TARGET));
1434         String type = readAV(reader, ATTR_TYPE);
1435         if (type != null) {
1436             try {
1437                 transition.setType(TransitionType.valueOf(type));
1438             }
1439             catch (IllegalArgumentException e) {
1440                 MessageFormat msgFormat = new MessageFormat(ERR_UNSUPPORTED_TRANSITION_TYPE);
1441                 String errMsg = msgFormat.format(new Object[] {type, reader.getLocation()});
1442                 throw new ModelException(errMsg);
1443             }
1444         }
1445 
1446         readNamespaces(configuration, transition);
1447         readExecutableContext(reader, configuration, transition, null);
1448 
1449         return transition;
1450     }
1451 
1452     /**
1453      * Read this set of executable content elements.
1454      *
1455      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1456      * @param configuration The {@link Configuration} to use while parsing.
1457      * @param executable The parent {@link Executable} to which this content belongs.
1458      * @param parent The optional parent {@link ActionsContainer} if this is child content of an ActionsContainer action.
1459      *
1460      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1461      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1462      *                        errors in the SCXML document that may not be identified by the schema).
1463      */
1464     private static void readExecutableContext(final XMLStreamReader reader, final Configuration configuration,
1465                                               final Executable executable, final ActionsContainer parent)
1466             throws XMLStreamException, ModelException {
1467 
1468         String end = "";
1469         if (parent != null) {
1470             end = parent.getContainerElementName();
1471         } else if (executable instanceof SimpleTransition) {
1472             end = ELEM_TRANSITION;
1473         } else if (executable instanceof OnEntry) {
1474             end = ELEM_ONENTRY;
1475         } else if (executable instanceof OnExit) {
1476             end = ELEM_ONEXIT;
1477         } else if (executable instanceof Finalize) {
1478             end = ELEM_FINALIZE;
1479         }
1480 
1481         loop : while (reader.hasNext()) {
1482             String name, nsURI;
1483             switch (reader.next()) {
1484                 case XMLStreamConstants.START_ELEMENT:
1485                     pushNamespaces(reader, configuration);
1486                     nsURI = reader.getNamespaceURI();
1487                     name = reader.getLocalName();
1488                     if (XMLNS_SCXML.equals(nsURI)) {
1489                         if (ELEM_RAISE.equals(name)) {
1490                             readRaise(reader, configuration, executable, parent);
1491                         } else if (ELEM_FOREACH.equals(name)) {
1492                             readForeach(reader, configuration, executable, parent);
1493                         } else if (ELEM_IF.equals(name)) {
1494                             readIf(reader, configuration, executable, parent);
1495                         } else if (ELEM_LOG.equals(name)) {
1496                             readLog(reader, configuration, executable, parent);
1497                         } else if (ELEM_ASSIGN.equals(name)) {
1498                             readAssign(reader, configuration, executable, parent);
1499                         } else if (ELEM_SEND.equals(name)) {
1500                             readSend(reader, configuration, executable, parent);
1501                         } else if (ELEM_CANCEL.equals(name)) {
1502                             readCancel(reader, configuration, executable, parent);
1503                         } else if (ELEM_SCRIPT.equals(name)) {
1504                             readScript(reader, configuration, executable, parent);
1505                         } else if (ELEM_IF.equals(end) && ELEM_ELSEIF.equals(name)) {
1506                             readElseIf(reader, configuration, executable, (If) parent);
1507                         } else if (ELEM_IF.equals(end) && ELEM_ELSE.equals(name)) {
1508                             readElse(reader, configuration, executable, (If)parent);
1509                         } else {
1510                             reportIgnoredElement(reader, configuration, end, nsURI, name);
1511                         }
1512                     } else if (XMLNS_COMMONS_SCXML.equals(nsURI)) {
1513                         if (ELEM_VAR.equals(name)) {
1514                             readVar(reader, configuration, executable, parent);
1515                         } else {
1516                             reportIgnoredElement(reader, configuration, end, nsURI, name);
1517                         }
1518                     } else { // custom action
1519                         CustomAction customAction = null;
1520                         if (!configuration.customActions.isEmpty()) {
1521                             for (CustomAction ca : configuration.customActions) {
1522                                 if (ca.getNamespaceURI().equals(nsURI) && ca.getLocalName().equals(name)) {
1523                                     customAction = ca;
1524                                 }
1525                             }
1526                         }
1527                         if (customAction != null) {
1528                             readCustomAction(reader, configuration, customAction, executable, parent);
1529                         } else {
1530                             reportIgnoredElement(reader, configuration, end, nsURI, name);
1531                         }
1532                     }
1533                     break;
1534                 case XMLStreamConstants.END_ELEMENT:
1535                     popNamespaces(reader, configuration);
1536                     break loop;
1537                 default:
1538             }
1539         }
1540     }
1541 
1542     /**
1543      * Read the contents of this &lt;raise&gt; element.
1544      *
1545      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1546      * @param configuration The {@link Configuration} to use while parsing.
1547      * @param executable The parent {@link Executable} for this action.
1548      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1549      *
1550      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1551      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1552      *                        errors in the SCXML document that may not be identified by the schema).
1553      */
1554     private static void readRaise(final XMLStreamReader reader, final Configuration configuration,
1555                                   final Executable executable, final ActionsContainer parent)
1556             throws XMLStreamException, ModelException {
1557 
1558         if (executable instanceof Finalize) {
1559             // http://www.w3.org/TR/2013/WD-scxml-20130801/#finalize
1560             // [...] the executable content inside <finalize> MUST NOT raise events or invoke external actions.
1561             // In particular, the <send> and <raise> elements MUST NOT occur.
1562             reportIgnoredElement(reader, configuration, ELEM_FINALIZE, XMLNS_SCXML, ELEM_RAISE);
1563         }
1564         else {
1565             Raise raise = new Raise();
1566             raise.setEvent(readAV(reader, ATTR_EVENT));
1567             readNamespaces(configuration, raise);
1568             raise.setParent(executable);
1569             if (parent != null) {
1570                 parent.addAction(raise);
1571             } else {
1572                 executable.addAction(raise);
1573             }
1574             skipToEndElement(reader);
1575         }
1576     }
1577 
1578     /**
1579      * Read the contents of this &lt;if&gt; element.
1580      *
1581      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1582      * @param configuration The {@link Configuration} to use while parsing.
1583      * @param executable The parent {@link Executable} for this action.
1584      * @param parent The optional parent {@link ActionsContainer} if this &lt;if&gt; is a child of one.
1585      *
1586      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1587      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1588      *                        errors in the SCXML document that may not be identified by the schema).
1589      */
1590     private static void readIf(final XMLStreamReader reader, final Configuration configuration,
1591                                final Executable executable, final ActionsContainer parent)
1592             throws XMLStreamException, ModelException {
1593 
1594         If iff = new If();
1595         iff.setCond(readRequiredAV(reader, ELEM_IF, ATTR_COND));
1596         readNamespaces(configuration, iff);
1597         iff.setParent(executable);
1598         if (parent != null) {
1599             parent.addAction(iff);
1600         } else {
1601             executable.addAction(iff);
1602         }
1603         readExecutableContext(reader, configuration, executable, iff);
1604     }
1605 
1606     /**
1607      * Read the contents of this &lt;elseif&gt; element.
1608      *
1609      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1610      * @param configuration The {@link Configuration} to use while parsing.
1611      * @param executable The parent {@link Executable} for this action.
1612      * @param iff The parent {@link If} for this &lt;elseif&gt;.
1613      *
1614      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1615      */
1616     private static void readElseIf(final XMLStreamReader reader, final Configuration configuration,
1617                                    final Executable executable, final If iff)
1618             throws XMLStreamException, ModelException {
1619 
1620         ElseIf elseif = new ElseIf();
1621         elseif.setCond(readRequiredAV(reader, ELEM_ELSEIF, ATTR_COND));
1622         readNamespaces(configuration, elseif);
1623         elseif.setParent(executable);
1624         iff.addAction(elseif);
1625         skipToEndElement(reader);
1626     }
1627 
1628     /**
1629      * Read the contents of this &lt;else&gt; element.
1630      *
1631      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1632      * @param configuration The {@link Configuration} to use while parsing.
1633      * @param executable The parent {@link Executable} for this action.
1634      * @param iff The parent {@link If} for this &lt;else&gt;.
1635      *
1636      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1637      */
1638     private static void readElse(final XMLStreamReader reader, final Configuration configuration,
1639                                  final Executable executable, final If iff)
1640             throws XMLStreamException {
1641 
1642         Else els = new Else();
1643         readNamespaces(configuration, els);
1644         els.setParent(executable);
1645         iff.addAction(els);
1646         skipToEndElement(reader);
1647     }
1648 
1649     /**
1650      * Read the contents of this &lt;foreach&gt; element.
1651      *
1652      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1653      * @param configuration The {@link Configuration} to use while parsing.
1654      * @param executable The parent {@link Executable} for this action.
1655      * @param parent The optional parent {@link ActionsContainer} if this &lt;foreach&gt; is a child of one.
1656      *
1657      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1658      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1659      *                        errors in the SCXML document that may not be identified by the schema).
1660      */
1661     private static void readForeach(final XMLStreamReader reader, final Configuration configuration,
1662                                     final Executable executable, final ActionsContainer parent)
1663             throws XMLStreamException, ModelException {
1664 
1665         Foreach fe = new Foreach();
1666         fe.setArray(readRequiredAV(reader, ELEM_FOREACH, ATTR_ARRAY));
1667         fe.setItem(readRequiredAV(reader, ELEM_FOREACH, ATTR_ITEM));
1668         fe.setIndex(readAV(reader, ATTR_INDEX));
1669         readNamespaces(configuration, fe);
1670         fe.setParent(executable);
1671         if (parent != null) {
1672             parent.addAction(fe);
1673         } else {
1674             executable.addAction(fe);
1675         }
1676         readExecutableContext(reader, configuration, executable, fe);
1677     }
1678 
1679     /**
1680      * Read the contents of this &lt;log&gt; element.
1681      *
1682      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1683      * @param configuration The {@link Configuration} to use while parsing.
1684      * @param executable The parent {@link Executable} for this action.
1685      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1686      *
1687      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1688      */
1689     private static void readLog(final XMLStreamReader reader, final Configuration configuration,
1690                                 final Executable executable, final ActionsContainer parent)
1691             throws XMLStreamException {
1692 
1693         Log log = new Log();
1694         log.setExpr(readAV(reader, ATTR_EXPR));
1695         log.setLabel(readAV(reader, ATTR_LABEL));
1696         readNamespaces(configuration, log);
1697         log.setParent(executable);
1698         if (parent != null) {
1699             parent.addAction(log);
1700         } else {
1701             executable.addAction(log);
1702         }
1703         skipToEndElement(reader);
1704     }
1705 
1706     /**
1707      * Read the contents of this &lt;assign&gt; element.
1708      *
1709      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1710      * @param configuration The {@link Configuration} to use while parsing.
1711      * @param executable The parent {@link Executable} for this action.
1712      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1713      *
1714      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1715      */
1716     private static void readAssign(final XMLStreamReader reader, final Configuration configuration,
1717                                    final Executable executable, final ActionsContainer parent)
1718             throws XMLStreamException, ModelException {
1719 
1720         Assign assign = new Assign();
1721         assign.setExpr(readAV(reader, ATTR_EXPR));
1722         assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, ATTR_LOCATION));
1723         String attrValue = readAV(reader, ATTR_TYPE);
1724         if (attrValue != null) {
1725             assign.setType(Evaluator.AssignType.fromValue(attrValue));
1726             if (assign.getType() == null) {
1727                 reportIgnoredAttribute(reader, configuration, ELEM_ASSIGN, ATTR_TYPE, attrValue);
1728             }
1729         }
1730         attrValue = readAV(reader, ATTR_ATTR);
1731         if (attrValue != null) {
1732             if (Evaluator.AssignType.ADD_ATTRIBUTE.equals(assign.getType())) {
1733                 assign.setAttr(attrValue);
1734             }
1735             else {
1736                 reportIgnoredAttribute(reader, configuration, ELEM_ASSIGN, ATTR_ATTR, attrValue);
1737             }
1738         }
1739         assign.setSrc(readAV(reader, ATTR_SRC));
1740         assign.setPathResolver(configuration.pathResolver);
1741         readNamespaces(configuration, assign);
1742         assign.setParent(executable);
1743         if (parent != null) {
1744             parent.addAction(assign);
1745         } else {
1746             executable.addAction(assign);
1747         }
1748         skipToEndElement(reader);
1749     }
1750 
1751     /**
1752      * Read the contents of this &lt;send&gt; element.
1753      *
1754      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1755      * @param configuration The {@link Configuration} to use while parsing.
1756      * @param executable The parent {@link Executable} for this action.
1757      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1758      *
1759      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1760      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
1761      *                        errors in the SCXML document that may not be identified by the schema).
1762      */
1763     private static void readSend(final XMLStreamReader reader, final Configuration configuration,
1764                                  final Executable executable, final ActionsContainer parent)
1765             throws XMLStreamException, ModelException {
1766 
1767         if (executable instanceof Finalize) {
1768             // http://www.w3.org/TR/2013/WD-scxml-20130801/#finalize
1769             // [...] the executable content inside <finalize> MUST NOT raise events or invoke external actions.
1770             // In particular, the <send> and <raise> elements MUST NOT occur.
1771             reportIgnoredElement(reader, configuration, ELEM_FINALIZE, XMLNS_SCXML, ELEM_SEND);
1772             return;
1773         }
1774 
1775         Send send = new Send();
1776         send.setId(readAV(reader, ATTR_ID));
1777         String attrValue = readAV(reader, ATTR_IDLOCATION);
1778         if (attrValue != null) {
1779             if (send.getId() != null) {
1780                 reportConflictingAttribute(reader, configuration, ELEM_SEND, ATTR_ID, ATTR_IDLOCATION);
1781             }
1782             else {
1783                 send.setIdlocation(attrValue);
1784             }
1785         }
1786         send.setDelay(readAV(reader, ATTR_DELAY));
1787         attrValue = readAV(reader, ATTR_DELAYEXPR);
1788         if (attrValue != null) {
1789             if (send.getDelay() != null) {
1790                 reportConflictingAttribute(reader, configuration, ELEM_SEND, ATTR_DELAY, ATTR_DELAYEXPR);
1791             }
1792             else {
1793                 send.setDelayexpr(attrValue);
1794             }
1795         }
1796         send.setEvent(readAV(reader, ATTR_EVENT));
1797         attrValue = readAV(reader, ATTR_EVENTEXPR);
1798         if (attrValue != null) {
1799             if (send.getEvent() != null) {
1800                 reportConflictingAttribute(reader, configuration, ELEM_SEND, ATTR_EVENT, ATTR_EVENTEXPR);
1801             }
1802             else {
1803                 send.setEventexpr(attrValue);
1804             }
1805         }
1806         send.setHints(readAV(reader, ATTR_HINTS));
1807         send.setNamelist(readAV(reader, ATTR_NAMELIST));
1808         send.setTarget(readAV(reader, ATTR_TARGET));
1809         attrValue = readAV(reader, ATTR_TARGETEXPR);
1810         if (attrValue != null) {
1811             if (send.getTarget() != null) {
1812                 reportConflictingAttribute(reader, configuration, ELEM_SEND, ATTR_TARGET, ATTR_TARGETEXPR);
1813             }
1814             else {
1815                 send.setTargetexpr(attrValue);
1816             }
1817         }
1818         send.setType(readAV(reader, ATTR_TYPE));
1819         attrValue = readAV(reader, ATTR_TYPEEXPR);
1820         if (attrValue != null) {
1821             if (send.getType() != null) {
1822                 reportConflictingAttribute(reader, configuration, ELEM_SEND, ATTR_TYPE, ATTR_TYPEEXPR);
1823             }
1824             else {
1825                 send.setTypeexpr(attrValue);
1826             }
1827         }
1828         readNamespaces(configuration, send);
1829 
1830         loop : while (reader.hasNext()) {
1831             String name, nsURI;
1832             switch (reader.next()) {
1833                 case XMLStreamConstants.START_ELEMENT:
1834                     pushNamespaces(reader, configuration);
1835                     nsURI = reader.getNamespaceURI();
1836                     name = reader.getLocalName();
1837                     if (XMLNS_SCXML.equals(nsURI)) {
1838                         if (ELEM_PARAM.equals(name)) {
1839                             if (send.getContent() == null) {
1840                                 readParam(reader, configuration, send);
1841                             }
1842                             else {
1843                                 reportIgnoredElement(reader, configuration, ELEM_SEND, nsURI, name);
1844                             }
1845                         } else if (ELEM_CONTENT.equals(name)) {
1846                             if (send.getNamelist() == null && send.getParams().isEmpty()) {
1847                                 readContent(reader, configuration, send);
1848                             }
1849                             else {
1850                                 reportIgnoredElement(reader, configuration, ELEM_SEND, nsURI, name);
1851                             }
1852                         } else {
1853                             reportIgnoredElement(reader, configuration, ELEM_SEND, nsURI, name);
1854                         }
1855                     } else {
1856                         reportIgnoredElement(reader, configuration, ELEM_SEND, nsURI, name);
1857                     }
1858                     break;
1859                 case XMLStreamConstants.END_ELEMENT:
1860                     popNamespaces(reader, configuration);
1861                     break loop;
1862                 default:
1863             }
1864         }
1865 
1866         send.setParent(executable);
1867         if (parent != null) {
1868             parent.addAction(send);
1869         } else {
1870             executable.addAction(send);
1871         }
1872     }
1873 
1874     /**
1875      * Read the contents of this &lt;cancel&gt; element.
1876      *
1877      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1878      * @param configuration The {@link Configuration} to use while parsing.
1879      * @param executable The parent {@link Executable} for this action.
1880      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1881      *
1882      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1883      */
1884     private static void readCancel(final XMLStreamReader reader, final Configuration configuration,
1885                                    final Executable executable, final ActionsContainer parent)
1886             throws XMLStreamException, ModelException {
1887 
1888         Cancel cancel = new Cancel();
1889         cancel.setSendid(readAV(reader, ATTR_SENDID));
1890         String attrValue = readAV(reader, ATTR_SENDIDEXPR);
1891         if (attrValue != null) {
1892             if (cancel.getSendid() != null) {
1893                 reportConflictingAttribute(reader, configuration, ELEM_CANCEL, ATTR_SENDID, ATTR_SENDIDEXPR);
1894             }
1895             else {
1896                 cancel.setSendidexpr(attrValue);
1897             }
1898         }
1899         readNamespaces(configuration, cancel);
1900         cancel.setParent(executable);
1901         if (parent != null) {
1902             parent.addAction(cancel);
1903         } else {
1904             executable.addAction(cancel);
1905         }
1906         skipToEndElement(reader);
1907     }
1908 
1909     /**
1910      * Read the contents of this &lt;script&gt; element.
1911      *
1912      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1913      * @param configuration The {@link Configuration} to use while parsing.
1914      * @param executable The parent {@link Executable} for this action.
1915      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1916      *
1917      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1918      */
1919     private static void readScript(final XMLStreamReader reader, final Configuration configuration,
1920                                    final Executable executable, final ActionsContainer parent)
1921             throws XMLStreamException {
1922 
1923         Script script = new Script();
1924         readNamespaces(configuration, script);
1925         script.setBody(readBody(reader));
1926         script.setParent(executable);
1927         if (parent != null) {
1928             parent.addAction(script);
1929         } else {
1930             executable.addAction(script);
1931         }
1932     }
1933 
1934     /**
1935      * Read the contents of the initial &lt;script&gt; element.
1936      * @see <a href="http://www.w3.org/TR/2013/WD-scxml-20130801/#scxml">
1937      *     http://www.w3.org/TR/2013/WD-scxml-20130801/#scxml<a> section 3.2.2
1938      *
1939      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1940      * @param configuration The {@link Configuration} to use while parsing.
1941      * @param scxml The root of the object model being parsed.
1942      *
1943      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1944      */
1945     private static void readGlobalScript(final XMLStreamReader reader, final Configuration configuration,
1946                                          final SCXML scxml)
1947             throws XMLStreamException {
1948 
1949         Script globalScript = new Script();
1950         globalScript.setGlobalScript(true);
1951         readNamespaces(configuration, globalScript);
1952         globalScript.setBody(readBody(reader));
1953         scxml.setGlobalScript(globalScript);
1954     }
1955 
1956     /**
1957      * Read the contents of this &lt;var&gt; element.
1958      *
1959      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1960      * @param configuration The {@link Configuration} to use while parsing.
1961      * @param executable The parent {@link Executable} for this action.
1962      * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
1963      *
1964      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1965      */
1966     private static void readVar(final XMLStreamReader reader, final Configuration configuration,
1967                                 final Executable executable, final ActionsContainer parent)
1968             throws XMLStreamException {
1969 
1970         Var var = new Var();
1971         var.setName(readAV(reader, ATTR_NAME));
1972         var.setExpr(readAV(reader, ATTR_EXPR));
1973         readNamespaces(configuration, var);
1974         var.setParent(executable);
1975         if (parent != null) {
1976             parent.addAction(var);
1977         } else {
1978             executable.addAction(var);
1979         }
1980         skipToEndElement(reader);
1981     }
1982 
1983     /**
1984      * Read the contents of this custom action.
1985      *
1986      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
1987      * @param configuration The {@link Configuration} to use while parsing.
1988      * @param customAction The {@link CustomAction} to read.
1989      * @param executable The parent {@link Executable} for this custom action.
1990      * @param parent The optional parent {@link ActionsContainer} if this custom action is a child of one.
1991      *
1992      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
1993      */
1994     private static void readCustomAction(final XMLStreamReader reader, final Configuration configuration,
1995                                          final CustomAction customAction, final Executable executable,
1996                                          final ActionsContainer parent)
1997             throws XMLStreamException {
1998 
1999         // Instantiate custom action
2000         Object actionObject;
2001         String className = customAction.getActionClass().getName();
2002         ClassLoader cl = configuration.customActionClassLoader;
2003         if (configuration.useContextClassLoaderForCustomActions) {
2004             cl = Thread.currentThread().getContextClassLoader();
2005         }
2006         if (cl == null) {
2007             cl = SCXMLReader.class.getClassLoader();
2008         }
2009         Class<?> clazz;
2010         try {
2011             clazz = cl.loadClass(className);
2012             actionObject = clazz.newInstance();
2013         } catch (ClassNotFoundException cnfe) {
2014             throw new XMLStreamException("Cannot find custom action class:" + className, cnfe);
2015         } catch (IllegalAccessException iae) {
2016             throw new XMLStreamException("Cannot access custom action class:" + className, iae);
2017         } catch (InstantiationException ie) {
2018             throw new XMLStreamException("Cannot instantiate custom action class:" + className, ie);
2019         }
2020         if (!(actionObject instanceof Action)) {
2021             throw new IllegalArgumentException(ERR_CUSTOM_ACTION_TYPE + className);
2022         }
2023 
2024         // Set the attribute values as properties
2025         Action action = (Action) actionObject;
2026         for (int i = 0; i < reader.getAttributeCount(); i++) {
2027             String name = reader.getAttributeLocalName(i);
2028             String value = reader.getAttributeValue(i);
2029             String setter = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
2030             Method method;
2031             try {
2032                 method = clazz.getMethod(setter, String.class);
2033                 method.invoke(action, value);
2034             } catch (NoSuchMethodException nsme) {
2035                 throw new XMLStreamException("No setter in class:" + className + ", for string property:" + name,
2036                         nsme);
2037             } catch (InvocationTargetException ite) {
2038                 throw new XMLStreamException("Exception calling setter for string property:" + name + " in class:"
2039                         + className, ite);
2040             } catch (IllegalAccessException iae) {
2041                 throw new XMLStreamException("Cannot access setter for string property:" + name + " in class:"
2042                         + className, iae);
2043             }
2044         }
2045 
2046         // Add any body content if necessary
2047         if (action instanceof ExternalContent) {
2048             Node body = readNode(reader, configuration, customAction.getNamespaceURI(),
2049                     customAction.getLocalName(), new String [] {});
2050             NodeList childNodes = body.getChildNodes();
2051             List<Node> externalNodes = ((ExternalContent) action).getExternalNodes();
2052             for (int i = 0; i < childNodes.getLength(); i++) {
2053                 externalNodes.add(childNodes.item(i));
2054             }
2055         }
2056         else {
2057             skipToEndElement(reader);
2058         }
2059 
2060         // Wire in the action and add to parent
2061         readNamespaces(configuration, action);
2062         action.setParent(executable);
2063         if (parent != null) {
2064             parent.addAction(action);
2065         } else {
2066             executable.addAction(action);
2067         }
2068     }
2069 
2070     /**
2071      * Read the following contents into a DOM {@link Node}.
2072      *
2073      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2074      * @param configuration The {@link Configuration} to use while parsing.
2075      * @param namespaceURI The namespace URI of the parent element
2076      * @param localName The local name of the parent element
2077      * @param attrs The attributes that will be read into the root DOM node.
2078      *
2079      * @return The parsed content as a DOM {@link Node}.
2080      *
2081      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2082      */
2083     private static Node readNode(final XMLStreamReader reader, final Configuration configuration,
2084                                  final String namespaceURI, final String localName, final String[] attrs)
2085             throws XMLStreamException {
2086 
2087         // Create a document in which to build the DOM node
2088         Document document;
2089         try {
2090             document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
2091         } catch (ParserConfigurationException pce) {
2092             throw new XMLStreamException(ERR_PARSER_CFG);
2093         }
2094 
2095         // This root element will be returned, add any attributes as specified
2096         Element root = document.createElementNS(namespaceURI, localName);
2097         for (final String attr1 : attrs) {
2098             Attr attr = document.createAttributeNS(XMLNS_DEFAULT, attr1);
2099             attr.setValue(readAV(reader, attr1));
2100             root.setAttributeNodeNS(attr);
2101         }
2102         document.appendChild(root);
2103 
2104         boolean children = false;
2105         Node parent = root;
2106 
2107         // Convert stream to DOM node(s) while maintaining parent child relationships
2108         loop : while (reader.hasNext()) {
2109             String name, nsURI;
2110             Node child = null;
2111             switch (reader.next()) {
2112                 case XMLStreamConstants.START_ELEMENT:
2113                     if (!children && root.hasChildNodes()) {
2114                         // remove any children
2115                         root.setTextContent(null);
2116                     }
2117                     children = true;
2118                     pushNamespaces(reader, configuration);
2119                     nsURI = reader.getNamespaceURI();
2120                     name = reader.getLocalName();
2121                     Element elem = document.createElementNS(nsURI, name);
2122                     for (int i = 0; i < reader.getAttributeCount(); i++) {
2123                         nsURI = reader.getAttributeNamespace(i);
2124                         name = reader.getAttributeLocalName(i);
2125                         String prefix = reader.getAttributePrefix(i);
2126                         if (prefix != null && prefix.length() > 0) {
2127                             name = prefix + ":" + name;
2128                         }
2129                         Attr attr = document.createAttributeNS(nsURI, name);
2130                         attr.setValue(reader.getAttributeValue(i));
2131                         elem.setAttributeNodeNS(attr);
2132                     }
2133                     parent.appendChild(elem);
2134                     parent = elem;
2135                     break;
2136                 case XMLStreamConstants.SPACE:
2137                 case XMLStreamConstants.CHARACTERS:
2138                 case XMLStreamConstants.ENTITY_REFERENCE:
2139                     if (!children || parent != root) {
2140                         child = document.createTextNode(reader.getText());
2141                     }
2142                     break;
2143                 case XMLStreamConstants.CDATA:
2144                     children = true;
2145                     child = document.createCDATASection(reader.getText());
2146                     break;
2147                 case XMLStreamConstants.COMMENT:
2148                     children = true;
2149                     child = document.createComment(reader.getText());
2150                     break;
2151                 case XMLStreamConstants.END_ELEMENT:
2152                     popNamespaces(reader, configuration);
2153                     parent = parent.getParentNode();
2154                     if (parent == document) {
2155                         break loop;
2156                     }
2157                     break;
2158                 default: // rest is ignored
2159             }
2160             if (child != null) {
2161                 parent.appendChild(child);
2162             }
2163         }
2164         if (!children && root.hasChildNodes()) {
2165             root.setTextContent(root.getTextContent().trim());
2166         }
2167         return root;
2168     }
2169 
2170     /**
2171      * Read the following body contents into a String.
2172      *
2173      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2174      *
2175      * @return The body content read into a String.
2176      *
2177      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2178      */
2179     private static String readBody(final XMLStreamReader reader)
2180             throws XMLStreamException {
2181 
2182         StringBuilder body = new StringBuilder();
2183         org.apache.commons.logging.Log log;
2184 
2185         // Add all body content to StringBuilder
2186         loop : while (reader.hasNext()) {
2187             switch (reader.next()) {
2188                 case XMLStreamConstants.START_ELEMENT:
2189                     log = LogFactory.getLog(SCXMLReader.class);
2190                     log.warn("Ignoring XML content in <script> element, encountered element with local name: "
2191                             + reader.getLocalName());
2192                     skipToEndElement(reader);
2193                     break;
2194                 case XMLStreamConstants.SPACE:
2195                 case XMLStreamConstants.CHARACTERS:
2196                 case XMLStreamConstants.ENTITY_REFERENCE:
2197                 case XMLStreamConstants.CDATA:
2198                 case XMLStreamConstants.COMMENT:
2199                     body.append(reader.getText());
2200                     break;
2201                 case XMLStreamConstants.END_ELEMENT:
2202                     break loop;
2203                 default: // rest is ignored
2204             }
2205         }
2206         return body.toString();
2207     }
2208 
2209     /**
2210      * @param input input string to check if null or empty after trim
2211      * @return null if input is null or empty after trim()
2212      */
2213     private static String nullIfEmpty(String input) {
2214         return input == null || input.trim().length()==0 ? null : input.trim();
2215     }
2216 
2217     /**
2218      * Get the attribute value at the current reader location.
2219      *
2220      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2221      * @param attrLocalName The attribute name whose value is needed.
2222      *
2223      * @return The value of the attribute.
2224      */
2225     private static String readAV(final XMLStreamReader reader, final String attrLocalName) {
2226         return nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
2227     }
2228 
2229     /**
2230      * Get the Boolean attribute value at the current reader location.
2231      *
2232      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2233      * @param elementName The name of the element for which the attribute value is needed.
2234      * @param attrLocalName The attribute name whose value is needed.
2235      *
2236      * @return The Boolean value of the attribute.
2237      * @throws ModelException When the attribute value is not empty but neither "true" or "false".
2238      */
2239     private static Boolean readBooleanAV(final XMLStreamReader reader, final String elementName,
2240                                          final String attrLocalName)
2241             throws ModelException {
2242         String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
2243         Boolean result = "true".equals(value) ? Boolean.TRUE : "false".equals(value) ? Boolean.FALSE : null;
2244         if (result == null && value != null) {
2245             MessageFormat msgFormat = new MessageFormat(ERR_ATTRIBUTE_NOT_BOOLEAN);
2246             String errMsg = msgFormat.format(new Object[] {value, attrLocalName, elementName, reader.getLocation()});
2247             throw new ModelException(errMsg);
2248         }
2249         return result;
2250     }
2251 
2252     /**
2253      * Get a required attribute value at the current reader location,
2254      *
2255      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2256      * @param elementName The name of the element for which the attribute value is needed.
2257      * @param attrLocalName The attribute name whose value is needed.
2258      *
2259      * @return The value of the attribute.
2260      * @throws ModelException When the required attribute is missing or empty.
2261      */
2262     private static String readRequiredAV(final XMLStreamReader reader, final String elementName, final String attrLocalName)
2263             throws ModelException {
2264         String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
2265         if (value == null) {
2266             MessageFormat msgFormat = new MessageFormat(ERR_REQUIRED_ATTRIBUTE_MISSING);
2267             String errMsg = msgFormat.format(new Object[] {elementName, attrLocalName, reader.getLocation()});
2268             throw new ModelException(errMsg);
2269         }
2270         return value;
2271     }
2272 
2273     private static String readOrGeneratedTransitionTargetId(final XMLStreamReader reader, final SCXML scxml,
2274                                                             final String elementName)
2275             throws ModelException {
2276         String id = readAV(reader, ATTR_ID);
2277         if (id == null) {
2278             id = scxml.generateTransitionTargetId();
2279         }
2280         else if (id.startsWith(SCXML.GENERATED_TT_ID_PREFIX)) {
2281             MessageFormat msgFormat = new MessageFormat(ERR_RESERVED_ID_PREFIX);
2282             String errMsg = msgFormat.format(new Object[] {elementName, id, reader.getLocation()});
2283             throw new ModelException(errMsg);
2284         }
2285         return id;
2286     }
2287 
2288     /**
2289      * Read the current active namespace declarations into the namespace prefixes holder.
2290      *
2291      * @param configuration The {@link Configuration} to use while parsing.
2292      * @param holder The {@link NamespacePrefixesHolder} to populate.
2293      */
2294     private static void readNamespaces(final Configuration configuration, final NamespacePrefixesHolder holder) {
2295 
2296         holder.setNamespaces(configuration.getCurrentNamespaces());
2297     }
2298 
2299     /**
2300      * Report an ignored element via the {@link XMLReporter} if available and the class
2301      * {@link org.apache.commons.logging.Log}.
2302      *
2303      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2304      * @param configuration The {@link Configuration} to use while parsing.
2305      * @param parent The parent element local name in the SCXML namespace.
2306      * @param nsURI The namespace URI of the ignored element.
2307      * @param name The local name of the ignored element.
2308      *
2309      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2310      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
2311      *                        errors in the SCXML document that may not be identified by the schema).
2312      */
2313     private static void reportIgnoredElement(final XMLStreamReader reader, final Configuration configuration,
2314                                              final String parent, final String nsURI, final String name)
2315             throws XMLStreamException, ModelException {
2316 
2317         org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLReader.class);
2318         StringBuilder sb = new StringBuilder();
2319         sb.append("Ignoring unknown or invalid element <").append(name)
2320                 .append("> in namespace \"").append(nsURI)
2321                 .append("\" as child of <").append(parent)
2322                 .append("> at ").append(reader.getLocation());
2323         if (!configuration.isSilent() && log.isWarnEnabled()) {
2324             log.warn(sb.toString());
2325         }
2326         if (configuration.isStrict()) {
2327             throw new ModelException(sb.toString());
2328         }
2329         XMLReporter reporter = configuration.reporter;
2330         if (reporter != null) {
2331             reporter.report(sb.toString(), "COMMONS_SCXML", null, reader.getLocation());
2332         }
2333         skipToEndElement(reader);
2334     }
2335 
2336     /**
2337      * Advances the XMLStreamReader until after the end of the current element: all children will be skipped as well
2338      * @param reader the reader
2339      * @throws XMLStreamException
2340      */
2341     private static void skipToEndElement(final XMLStreamReader reader) throws XMLStreamException {
2342         int elementsToSkip = 1;
2343         while (elementsToSkip > 0 && reader.hasNext()) {
2344             int next = reader.next();
2345             if (next == XMLStreamConstants.START_ELEMENT) {
2346                 elementsToSkip++;
2347             }
2348             else if (next == XMLStreamConstants.END_ELEMENT) {
2349                 elementsToSkip--;
2350             }
2351         }
2352     }
2353 
2354     /**
2355      * Report an ignored attribute via the {@link XMLReporter} if available and the class
2356      * {@link org.apache.commons.logging.Log}.
2357      *
2358      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2359      * @param configuration The {@link Configuration} to use while parsing.
2360      * @param element The element name.
2361      * @param attr The attribute which is ignored.
2362      * @param value The value of the attribute which is ignored.
2363      *
2364      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2365      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
2366      *                        errors in the SCXML document that may not be identified by the schema).
2367      */
2368     private static void reportIgnoredAttribute(final XMLStreamReader reader, final Configuration configuration,
2369                                                final String element, final String attr, final String value)
2370             throws XMLStreamException, ModelException {
2371 
2372         org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLReader.class);
2373         StringBuilder sb = new StringBuilder();
2374         sb.append("Ignoring unknown or invalid <").append(element).append("> attribute ").append(attr)
2375                 .append("=\"").append(value).append("\" at ").append(reader.getLocation());
2376         if (!configuration.isSilent() && log.isWarnEnabled()) {
2377             log.warn(sb.toString());
2378         }
2379         if (configuration.isStrict()) {
2380             throw new ModelException(sb.toString());
2381         }
2382         XMLReporter reporter = configuration.reporter;
2383         if (reporter != null) {
2384             reporter.report(sb.toString(), "COMMONS_SCXML", null, reader.getLocation());
2385         }
2386     }
2387 
2388     /**
2389      * Report a conflicting attribute via the {@link XMLReporter} if available and the class
2390      * {@link org.apache.commons.logging.Log}.
2391      *
2392      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2393      * @param configuration The {@link Configuration} to use while parsing.
2394      * @param element The element name.
2395      * @param attr The attribute with which a conflict is detected.
2396      * @param conflictingAttr The conflicting attribute
2397      *
2398      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2399      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
2400      *                        errors in the SCXML document that may not be identified by the schema).
2401      */
2402     private static void reportConflictingAttribute(final XMLStreamReader reader, final Configuration configuration,
2403                                              final String element, final String attr, final String conflictingAttr)
2404             throws XMLStreamException, ModelException {
2405 
2406         org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLReader.class);
2407         StringBuilder sb = new StringBuilder();
2408         sb.append("Ignoring <").append(element).append("> attribute \"").append(conflictingAttr)
2409                 .append("\" which conflicts with already defined attribute \"").append(attr)
2410                 .append("\" at ").append(reader.getLocation());
2411         if (!configuration.isSilent() && log.isWarnEnabled()) {
2412             log.warn(sb.toString());
2413         }
2414         if (configuration.isStrict()) {
2415             throw new ModelException(sb.toString());
2416         }
2417         XMLReporter reporter = configuration.reporter;
2418         if (reporter != null) {
2419             reporter.report(sb.toString(), "COMMONS_SCXML", null, reader.getLocation());
2420         }
2421     }
2422 
2423     /**
2424      * Push any new namespace declarations on the configuration namespaces map.
2425      *
2426      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2427      * @param configuration The {@link Configuration} to use while parsing.
2428      */
2429     private static void pushNamespaces(final XMLStreamReader reader, final Configuration configuration) {
2430 
2431         for (int i = 0; i < reader.getNamespaceCount(); i++) {
2432             Stack<String> stack = configuration.namespaces.get(reader.getNamespacePrefix(i));
2433             if (stack == null) {
2434                 stack = new Stack<String>();
2435                 configuration.namespaces.put(reader.getNamespacePrefix(i), stack);
2436             }
2437             stack.push(reader.getNamespaceURI(i));
2438         }
2439     }
2440 
2441     /**
2442      * Pop any expiring namespace declarations from the configuration namespaces map.
2443      *
2444      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
2445      * @param configuration The {@link Configuration} to use while parsing.
2446      *
2447      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
2448      */
2449     private static void popNamespaces(final XMLStreamReader reader, final Configuration configuration)
2450             throws XMLStreamException {
2451 
2452         for (int i = 0; i < reader.getNamespaceCount(); i++) {
2453             Stack<String> stack = configuration.namespaces.get(reader.getNamespacePrefix(i));
2454             if (stack == null) {
2455                 throw new XMLStreamException("Configuration namespaces stack null");
2456             }
2457             try {
2458                 stack.pop();
2459                 if (stack.empty()) {
2460                     configuration.namespaces.remove(reader.getNamespacePrefix(i));
2461                 }
2462             } catch (EmptyStackException e) {
2463                 throw new XMLStreamException("Configuration namespaces stack popped too many times");
2464             }
2465         }
2466     }
2467 
2468     /**
2469      * Use the supplied {@link Configuration} to create an appropriate {@link XMLStreamReader} for this
2470      * {@link SCXMLReader}. Exactly one of the url, path, stream, reader or source parameters must be provided.
2471      *
2472      * @param configuration The {@link Configuration} to be used.
2473      * @param url The {@link URL} to the SCXML document to read.
2474      * @param path The optional real path to the SCXML document as a string.
2475      * @param stream The optional {@link InputStream} providing the SCXML document.
2476      * @param reader The optional {@link Reader} providing the SCXML document.
2477      * @param source The optional {@link Source} providing the SCXML document.
2478      *
2479      * @return The appropriately configured {@link XMLStreamReader}.
2480      *
2481      * @throws IOException Exception with the URL IO.
2482      * @throws XMLStreamException A problem with the XML stream creation or an wrapped {@link SAXException}
2483      *                            thrown in trying to validate the document against the XML Schema for SCXML.
2484      */
2485     private static XMLStreamReader getReader(final Configuration configuration, final URL url, final String path,
2486                                              final InputStream stream, final Reader reader, final Source source)
2487             throws IOException, XMLStreamException {
2488 
2489         // Instantiate the XMLInputFactory
2490         XMLInputFactory factory = XMLInputFactory.newInstance();
2491         if (configuration.factoryId != null && configuration.factoryClassLoader != null) {
2492             factory = XMLInputFactory.newFactory(configuration.factoryId, configuration.factoryClassLoader);
2493         }
2494         factory.setEventAllocator(configuration.allocator);
2495         for (Map.Entry<String, Object> property : configuration.properties.entrySet()) {
2496             factory.setProperty(property.getKey(), property.getValue());
2497         }
2498         factory.setXMLReporter(configuration.reporter);
2499         factory.setXMLResolver(configuration.resolver);
2500 
2501         // Consolidate InputStream options
2502         InputStream urlStream = null;
2503         if (url != null || path != null) {
2504             URL scxml = (url != null ? url : new URL(path));
2505             URLConnection conn = scxml.openConnection();
2506             conn.setUseCaches(false);
2507             urlStream = conn.getInputStream();
2508         } else if (stream != null) {
2509             urlStream = stream;
2510         }
2511 
2512         // Create the XMLStreamReader
2513         XMLStreamReader xsr = null;
2514 
2515         if (configuration.validate) {
2516             // Validation requires us to use a Source
2517 
2518             URL scxmlSchema = new URL("TODO"); // TODO, point to appropriate location
2519             SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
2520             Schema schema;
2521             try {
2522                 schema = schemaFactory.newSchema(scxmlSchema);
2523             } catch (SAXException se) {
2524                 throw new XMLStreamException("Failed to create SCXML Schema for validation", se);
2525             }
2526 
2527             Validator validator = schema.newValidator();
2528             validator.setErrorHandler(new SimpleErrorHandler());
2529 
2530             Source src = null;
2531             if (urlStream != null) {
2532                 // configuration.encoding is ignored
2533                 if (configuration.systemId != null) {
2534                     src = new StreamSource(urlStream, configuration.systemId);
2535                 } else {
2536                     src = new StreamSource(urlStream);
2537                 }
2538             } else if (reader != null) {
2539                 if (configuration.systemId != null) {
2540                     src = new StreamSource(reader, configuration.systemId);
2541                 } else {
2542                     src = new StreamSource(reader);
2543                 }
2544             } else if (source != null) {
2545                 src = source;
2546             }
2547             xsr = factory.createXMLStreamReader(src);
2548             try {
2549                 validator.validate(src);
2550             } catch (SAXException se) {
2551                 throw new XMLStreamException("Failed to create apply SCXML Validator", se);
2552             }
2553 
2554         } else {
2555             // We can use the more direct XMLInputFactory API if validation isn't needed
2556 
2557             if (urlStream != null) {
2558                 // systemId gets preference, then encoding if either are present
2559                 if (configuration.systemId != null) {
2560                     xsr = factory.createXMLStreamReader(configuration.systemId, urlStream);
2561                 } else if (configuration.encoding != null) {
2562                     xsr = factory.createXMLStreamReader(urlStream, configuration.encoding);
2563                 } else {
2564                     xsr = factory.createXMLStreamReader(urlStream);
2565                 }
2566             } else if (reader != null) {
2567                 if (configuration.systemId != null) {
2568                     xsr = factory.createXMLStreamReader(configuration.systemId, reader);
2569                 } else {
2570                     xsr = factory.createXMLStreamReader(reader);
2571                 }
2572             } else if (source != null) {
2573                 xsr = factory.createXMLStreamReader(source);
2574             }
2575 
2576         }
2577 
2578         return xsr;
2579     }
2580 
2581     /**
2582      * Discourage instantiation since this is a utility class.
2583      */
2584     private SCXMLReader() {
2585         super();
2586     }
2587 
2588     //------------------------- CONFIGURATION CLASS -------------------------//
2589     /**
2590      * <p>
2591      * Configuration for the {@link SCXMLReader}. The configuration properties necessary for the following are
2592      * covered:
2593      * </p>
2594      *
2595      * <ul>
2596      *   <li>{@link XMLInputFactory} configuration properties such as {@link XMLReporter}, {@link XMLResolver} and
2597      *   {@link XMLEventAllocator}</li>
2598      *   <li>{@link XMLStreamReader} configuration properties such as <code>systemId</code> and <code>encoding</code>
2599      *   </li>
2600      *   <li>Commons SCXML object model configuration properties such as the list of custom actions and the
2601      *   {@link PathResolver} to use.</li>
2602      * </ul>
2603      */
2604     public static class Configuration {
2605 
2606         /*
2607          * Configuration properties for this {@link SCXMLReader}.
2608          */
2609         // XMLInputFactory configuration properties.
2610         /**
2611          * The <code>factoryId</code> to use for the {@link XMLInputFactory}.
2612          */
2613         final String factoryId;
2614 
2615         /**
2616          * The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to create.
2617          */
2618         final ClassLoader factoryClassLoader;
2619 
2620         /**
2621          * The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
2622          */
2623         final XMLEventAllocator allocator;
2624 
2625         /**
2626          * The map of properties (keys are property name strings, values are object property values) for the
2627          * {@link XMLInputFactory}.
2628          */
2629         final Map<String, Object> properties;
2630 
2631         /**
2632          * The {@link XMLResolver} for the {@link XMLInputFactory}.
2633          */
2634         final XMLResolver resolver;
2635 
2636         /**
2637          * The {@link XMLReporter} for the {@link XMLInputFactory}.
2638          */
2639         final XMLReporter reporter;
2640 
2641         // XMLStreamReader configuration properties.
2642         /**
2643          * The <code>encoding</code> to use for the {@link XMLStreamReader}.
2644          */
2645         final String encoding;
2646 
2647         /**
2648          * The <code>systemId</code> to use for the {@link XMLStreamReader}.
2649          */
2650         final String systemId;
2651 
2652         /**
2653          * Whether to validate the input with the XML Schema for SCXML.
2654          */
2655         final boolean validate;
2656 
2657         // Commons SCXML object model configuration properties.
2658         /**
2659          * The list of Commons SCXML custom actions that will be available for this document.
2660          */
2661         final List<CustomAction> customActions;
2662 
2663         /**
2664          * The {@link ClassLoader} to use for loading the {@link CustomAction} instances to create.
2665          */
2666         final ClassLoader customActionClassLoader;
2667 
2668         /**
2669          * Whether to use the thread context {@link ClassLoader} for loading any {@link CustomAction} classes.
2670          */
2671         final boolean useContextClassLoaderForCustomActions;
2672 
2673         /**
2674          * The map for bookkeeping the current active namespace declarations. The keys are prefixes and the values are
2675          * {@link Stack}s containing the corresponding namespaceURIs, with the active one on top.
2676          */
2677         final Map<String, Stack<String>> namespaces;
2678 
2679         // Mutable Commons SCXML object model configuration properties.
2680         /**
2681          * The parent SCXML document if this document is src'ed in via the &lt;state&gt; or &lt;parallel&gt; element's
2682          * "src" attribute.
2683          */
2684         SCXML parent;
2685 
2686         /**
2687          * The Commons SCXML {@link PathResolver} to use for this document.
2688          */
2689         PathResolver pathResolver;
2690 
2691         /**
2692          * Whether to silently ignore any unknown or invalid elements
2693          * or to leave warning logs for those.
2694          */
2695         boolean silent;
2696 
2697         /**
2698          * Whether to strictly throw a model exception when there are any unknown or invalid elements
2699          * or to leniently allow to read the model even with those.
2700          */
2701         boolean strict;
2702 
2703         /*
2704          * Public constructors
2705          */
2706         /**
2707          * Default constructor.
2708          */
2709         public Configuration() {
2710             this(null, null);
2711         }
2712 
2713         /**
2714          * Minimal convenience constructor.
2715          *
2716          * @param reporter The {@link XMLReporter} to use for this reading.
2717          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this reading.
2718          */
2719         public Configuration(final XMLReporter reporter, final PathResolver pathResolver) {
2720             this(null, null, null, null, null, reporter, null, null, false, pathResolver, null, null, null, false);
2721         }
2722 
2723         /**
2724          * Convenience constructor.
2725          *
2726          * @param reporter The {@link XMLReporter} to use for this reading.
2727          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this reading.
2728          * @param customActions The list of Commons SCXML custom actions that will be available for this document.
2729          */
2730         public Configuration(final XMLReporter reporter, final PathResolver pathResolver,
2731                              final List<CustomAction> customActions) {
2732             this(null, null, null, null, null, reporter, null, null, false, pathResolver, null, customActions, null,
2733                     false);
2734         }
2735 
2736         /**
2737          * All purpose constructor. Any of the parameters passed in can be <code>null</code> (booleans should default
2738          * to <code>false</code>).
2739          *
2740          * @param factoryId The <code>factoryId</code> to use.
2741          * @param classLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to create.
2742          * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
2743          * @param properties The map of properties (keys are property name strings, values are object property values)
2744          *                   for the {@link XMLInputFactory}.
2745          * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
2746          * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
2747          * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
2748          * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
2749          * @param validate Whether to validate the input with the XML Schema for SCXML.
2750          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
2751          * @param customActions The list of Commons SCXML custom actions that will be available for this document.
2752          * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
2753          *                                create.
2754          * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
2755          *                                             {@link CustomAction} instances to create.
2756          */
2757         public Configuration(final String factoryId, final ClassLoader classLoader, final XMLEventAllocator allocator,
2758                              final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
2759                              final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
2760                              final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
2761                              final boolean useContextClassLoaderForCustomActions) {
2762             this(factoryId, classLoader, allocator, properties, resolver, reporter, encoding, systemId, validate,
2763                     pathResolver, null, customActions, customActionClassLoader,
2764                     useContextClassLoaderForCustomActions);
2765         }
2766 
2767         /*
2768          * Package access constructors
2769          */
2770         /**
2771          * Convenience package access constructor.
2772          *
2773          * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
2774          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
2775          * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
2776          *               &lt;parallel&gt; element's "src" attribute.
2777          */
2778         Configuration(final XMLReporter reporter, final PathResolver pathResolver, final SCXML parent) {
2779             this(null, null, null, null, null, reporter, null, null, false, pathResolver, parent, null, null, false);
2780         }
2781 
2782         /**
2783          * Package access copy constructor.
2784          *
2785          * @param source The source {@link Configuration} to replicate.
2786          */
2787         Configuration(final Configuration source) {
2788             this(source.factoryId, source.factoryClassLoader, source.allocator, source.properties, source.resolver,
2789                     source.reporter, source.encoding, source.systemId, source.validate, source.pathResolver,
2790                     source.parent, source.customActions, source.customActionClassLoader,
2791                     source.useContextClassLoaderForCustomActions, source.silent, source.strict);
2792         }
2793 
2794         /**
2795          * All-purpose package access constructor.
2796          *
2797          * @param factoryId The <code>factoryId</code> to use.
2798          * @param factoryClassLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to
2799          *                           create.
2800          * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
2801          * @param properties The map of properties (keys are property name strings, values are object property values)
2802          *                   for the {@link XMLInputFactory}.
2803          * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
2804          * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
2805          * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
2806          * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
2807          * @param validate Whether to validate the input with the XML Schema for SCXML.
2808          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
2809          * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
2810          *               &lt;parallel&gt; element's "src" attribute.
2811          * @param customActions The list of Commons SCXML custom actions that will be available for this document.
2812          * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
2813          *                                create.
2814          * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
2815          *                                             {@link CustomAction} instances to create.
2816          */
2817         Configuration(final String factoryId, final ClassLoader factoryClassLoader, final XMLEventAllocator allocator,
2818                       final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
2819                       final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
2820                       final SCXML parent, final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
2821                       final boolean useContextClassLoaderForCustomActions) {
2822             this(factoryId, factoryClassLoader, allocator, properties, resolver, reporter, encoding, systemId,
2823                     validate, pathResolver, parent, customActions, customActionClassLoader,
2824                     useContextClassLoaderForCustomActions, false, false);
2825         }
2826 
2827         /**
2828          * All-purpose package access constructor.
2829          *
2830          * @param factoryId The <code>factoryId</code> to use.
2831          * @param factoryClassLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to
2832          *                           create.
2833          * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
2834          * @param properties The map of properties (keys are property name strings, values are object property values)
2835          *                   for the {@link XMLInputFactory}.
2836          * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
2837          * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
2838          * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
2839          * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
2840          * @param validate Whether to validate the input with the XML Schema for SCXML.
2841          * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
2842          * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
2843          *               &lt;parallel&gt; element's "src" attribute.
2844          * @param customActions The list of Commons SCXML custom actions that will be available for this document.
2845          * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
2846          *                                create.
2847          * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
2848          *                                             {@link CustomAction} instances to create.
2849          * @param silent Whether to silently ignore any unknown or invalid elements or to leave warning logs for those.
2850          * @param strict Whether to strictly throw a model exception when there are any unknown or invalid elements
2851          *               or to leniently allow to read the model even with those.
2852          */
2853         Configuration(final String factoryId, final ClassLoader factoryClassLoader, final XMLEventAllocator allocator,
2854                       final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
2855                       final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
2856                       final SCXML parent, final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
2857                       final boolean useContextClassLoaderForCustomActions, final boolean silent, final boolean strict) {
2858             this.factoryId = factoryId;
2859             this.factoryClassLoader = factoryClassLoader;
2860             this.allocator = allocator;
2861             this.properties = (properties == null ? new HashMap<String, Object>() : properties);
2862             this.resolver = resolver;
2863             this.reporter = reporter;
2864             this.encoding = encoding;
2865             this.systemId = systemId;
2866             this.validate = validate;
2867             this.pathResolver = pathResolver;
2868             this.parent = parent;
2869             this.customActions = (customActions == null ? new ArrayList<CustomAction>() : customActions);
2870             this.customActionClassLoader = customActionClassLoader;
2871             this.useContextClassLoaderForCustomActions = useContextClassLoaderForCustomActions;
2872             this.namespaces = new HashMap<String, Stack<String>>();
2873             this.silent = silent;
2874             this.strict = strict;
2875         }
2876 
2877         /*
2878          * Package access convenience methods
2879          */
2880         /**
2881          * Get the current namespaces at this point in the StAX reading.
2882          *
2883          * @return Map<String,String> The namespace map (keys are prefixes and values are the corresponding current
2884          *                            namespace URIs).
2885          */
2886         Map<String, String> getCurrentNamespaces() {
2887             Map<String, String> currentNamespaces = new HashMap<String, String>();
2888             for (Map.Entry<String, Stack<String>> nsEntry : namespaces.entrySet()) {
2889                 currentNamespaces.put(nsEntry.getKey(), nsEntry.getValue().peek());
2890             }
2891             return currentNamespaces;
2892         }
2893 
2894         /**
2895          * Returns true if it is set to read models silently without any model error warning logs.
2896          * @return
2897          * @see {@link #silent}
2898          */
2899         public boolean isSilent() {
2900             return silent;
2901         }
2902 
2903         /**
2904          * Turn on/off silent mode (whether to read models silently without any model error warning logs)
2905          * @param silent
2906          * @see {@link #silent}
2907          */
2908         public void setSilent(boolean silent) {
2909             this.silent = silent;
2910         }
2911 
2912         /**
2913          * Returns true if it is set to check model strictly with throwing exceptions on any model error.
2914          * @return
2915          * @see {@link #strict}
2916          */
2917         public boolean isStrict() {
2918             return strict;
2919         }
2920 
2921         /**
2922          * Turn on/off strict model (whether to check model strictly with throwing exception on any model error)
2923          * @param strict
2924          * @see {@link #strict}
2925          */
2926         public void setStrict(boolean strict) {
2927             this.strict = strict;
2928         }
2929     }
2930 }