##### Coverage Report - org.apache.commons.scxml.io.SCXMLDigester

Classes in this File | Line Coverage | Branch Coverage | Complexity
SCXMLDigester |
76% |
79% |
| 1.9148936170212767;1.915

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