Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
SCXMLDigester |
|
| 1.9183673469387754;1.918 |
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may obtain a copy of the License at |
|
8 | * |
|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 | * |
|
11 | * Unless required by applicable law or agreed to in writing, software |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
16 | */ |
|
17 | package org.apache.commons.scxml.io; |
|
18 | ||
19 | import java.io.IOException; |
|
20 | import java.net.URL; |
|
21 | import java.text.MessageFormat; |
|
22 | import java.util.List; |
|
23 | import java.util.Map; |
|
24 | ||
25 | import javax.xml.parsers.DocumentBuilder; |
|
26 | import javax.xml.parsers.DocumentBuilderFactory; |
|
27 | import javax.xml.parsers.ParserConfigurationException; |
|
28 | ||
29 | import org.apache.commons.digester.Digester; |
|
30 | import org.apache.commons.digester.ExtendedBaseRules; |
|
31 | import org.apache.commons.digester.NodeCreateRule; |
|
32 | import org.apache.commons.digester.ObjectCreateRule; |
|
33 | import org.apache.commons.digester.Rule; |
|
34 | import org.apache.commons.digester.SetNextRule; |
|
35 | import org.apache.commons.digester.SetPropertiesRule; |
|
36 | import org.apache.commons.logging.LogFactory; |
|
37 | ||
38 | import org.apache.commons.scxml.PathResolver; |
|
39 | import org.apache.commons.scxml.SCXMLHelper; |
|
40 | import org.apache.commons.scxml.env.URLResolver; |
|
41 | import org.apache.commons.scxml.model.Action; |
|
42 | import org.apache.commons.scxml.model.Assign; |
|
43 | import org.apache.commons.scxml.model.Cancel; |
|
44 | import org.apache.commons.scxml.model.CustomAction; |
|
45 | import org.apache.commons.scxml.model.Data; |
|
46 | import org.apache.commons.scxml.model.Datamodel; |
|
47 | import org.apache.commons.scxml.model.Else; |
|
48 | import org.apache.commons.scxml.model.ElseIf; |
|
49 | import org.apache.commons.scxml.model.Executable; |
|
50 | import org.apache.commons.scxml.model.Exit; |
|
51 | import org.apache.commons.scxml.model.ExternalContent; |
|
52 | import org.apache.commons.scxml.model.Finalize; |
|
53 | import org.apache.commons.scxml.model.History; |
|
54 | import org.apache.commons.scxml.model.If; |
|
55 | import org.apache.commons.scxml.model.Initial; |
|
56 | import org.apache.commons.scxml.model.Invoke; |
|
57 | import org.apache.commons.scxml.model.Log; |
|
58 | import org.apache.commons.scxml.model.ModelException; |
|
59 | import org.apache.commons.scxml.model.NamespacePrefixesHolder; |
|
60 | import org.apache.commons.scxml.model.OnEntry; |
|
61 | import org.apache.commons.scxml.model.OnExit; |
|
62 | import org.apache.commons.scxml.model.Parallel; |
|
63 | import org.apache.commons.scxml.model.Param; |
|
64 | import org.apache.commons.scxml.model.PathResolverHolder; |
|
65 | import org.apache.commons.scxml.model.SCXML; |
|
66 | import org.apache.commons.scxml.model.Send; |
|
67 | import org.apache.commons.scxml.model.State; |
|
68 | import org.apache.commons.scxml.model.Transition; |
|
69 | import org.apache.commons.scxml.model.TransitionTarget; |
|
70 | import org.apache.commons.scxml.model.Var; |
|
71 | ||
72 | import org.w3c.dom.Element; |
|
73 | import org.w3c.dom.Node; |
|
74 | import org.w3c.dom.NodeList; |
|
75 | ||
76 | import org.xml.sax.Attributes; |
|
77 | import org.xml.sax.ErrorHandler; |
|
78 | import org.xml.sax.InputSource; |
|
79 | import org.xml.sax.SAXException; |
|
80 | ||
81 | /** |
|
82 | * <p>The SCXMLDigester provides the ability to digest a SCXML document into |
|
83 | * the Java object model provided in the model package.</p> |
|
84 | * <p>The SCXMLDigester can be used for:</p> |
|
85 | * <ol> |
|
86 | * <li>Digest a SCXML file into the Commons SCXML Java object model.</li> |
|
87 | * <li>Obtain a SCXML Digester for further customization of the default |
|
88 | * ruleset.</li> |
|
89 | * </ol> |
|
90 | */ |
|
91 | 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 | 50 | SCXML scxml = null; |
224 | 50 | Digester scxmlDigester = SCXMLDigester |
225 | .newInstance(null, new URLResolver(scxmlURL), customActions); |
|
226 | 50 | scxmlDigester.setErrorHandler(errHandler); |
227 | ||
228 | try { |
|
229 | 50 | 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 | 50 | } |
242 | ||
243 | 50 | if (scxml != null) { |
244 | 50 | ModelUpdater.updateSCXML(scxml); |
245 | } |
|
246 | ||
247 | 50 | 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 | 0 | 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 | 54 | Digester digester = new Digester(); |
459 | 54 | digester.setNamespaceAware(true); |
460 | //Uncomment next line after SCXML DTD is available |
|
461 | //digester.setValidating(true); |
|
462 | 54 | digester.setRules(initRules(scxml, pr, customActions)); |
463 | 54 | 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 <scxml> element. */ |
|
484 | private static final String XP_SM = "scxml"; |
|
485 | ||
486 | /** <state> children of root <scxml> element. */ |
|
487 | private static final String XP_SM_ST = "scxml/state"; |
|
488 | ||
489 | //// Universal matches, prefixed by XPU_ |
|
490 | // State |
|
491 | /** <state> children of <state> elements. */ |
|
492 | private static final String XPU_ST_ST = "!*/state/state"; |
|
493 | ||
494 | /** <state> children of <parallel> elements. */ |
|
495 | private static final String XPU_PAR_ST = "!*/parallel/state"; |
|
496 | ||
497 | /** <state> children of transition <target> elements. */ |
|
498 | private static final String XPU_TR_TAR_ST = "!*/transition/target/state"; |
|
499 | ||
500 | //private static final String XPU_ST_TAR_ST = "!*/state/target/state"; |
|
501 | ||
502 | // Parallel |
|
503 | /** <parallel> child of <state> elements. */ |
|
504 | private static final String XPU_ST_PAR = "!*/state/parallel"; |
|
505 | ||
506 | // If |
|
507 | /** <if> element. */ |
|
508 | private static final String XPU_IF = "!*/if"; |
|
509 | ||
510 | // Executables, next three patterns useful when adding custom actions |
|
511 | /** <onentry> element. */ |
|
512 | private static final String XPU_ONEN = "!*/onentry"; |
|
513 | ||
514 | /** <onexit> element. */ |
|
515 | private static final String XPU_ONEX = "!*/onexit"; |
|
516 | ||
517 | /** <transition> element. */ |
|
518 | private static final String XPU_TR = "!*/transition"; |
|
519 | ||
520 | /** <finalize> element. */ |
|
521 | private static final String XPU_FIN = "!*/finalize"; |
|
522 | ||
523 | //// Path Fragments, constants prefixed by XPF_ |
|
524 | // Onentries and Onexits |
|
525 | /** <onentry> child element. */ |
|
526 | private static final String XPF_ONEN = "/onentry"; |
|
527 | ||
528 | /** <onexit> child element. */ |
|
529 | private static final String XPF_ONEX = "/onexit"; |
|
530 | ||
531 | // Datamodel section |
|
532 | /** <datamodel> child element. */ |
|
533 | private static final String XPF_DM = "/datamodel"; |
|
534 | ||
535 | /** Individual <data> elements. */ |
|
536 | private static final String XPF_DATA = "/data"; |
|
537 | ||
538 | // Initial |
|
539 | /** <initial> child element. */ |
|
540 | private static final String XPF_INI = "/initial"; |
|
541 | ||
542 | // Invoke, param and finalize |
|
543 | /** <invoke> child element of <state>. */ |
|
544 | private static final String XPF_INV = "/invoke"; |
|
545 | ||
546 | /** <param> child element of <invoke>. */ |
|
547 | private static final String XPF_PRM = "/param"; |
|
548 | ||
549 | /** <finalize> child element of <invoke>. */ |
|
550 | private static final String XPF_FIN = "/finalize"; |
|
551 | ||
552 | // History |
|
553 | /** <history> child element. */ |
|
554 | private static final String XPF_HIST = "/history"; |
|
555 | ||
556 | // Transition, target and exit |
|
557 | /** <transition> child element. */ |
|
558 | private static final String XPF_TR = "/transition"; |
|
559 | ||
560 | /** <target> child element. */ |
|
561 | private static final String XPF_TAR = "/target"; |
|
562 | ||
563 | /** <exit> child element. */ |
|
564 | private static final String XPF_EXT = "/exit"; |
|
565 | ||
566 | // Actions |
|
567 | /** <var> child element. */ |
|
568 | private static final String XPF_VAR = "/var"; |
|
569 | ||
570 | /** <assign> child element. */ |
|
571 | private static final String XPF_ASN = "/assign"; |
|
572 | ||
573 | /** <log> child element. */ |
|
574 | private static final String XPF_LOG = "/log"; |
|
575 | ||
576 | /** <send> child element. */ |
|
577 | private static final String XPF_SND = "/send"; |
|
578 | ||
579 | /** <cancel> child element. */ |
|
580 | private static final String XPF_CAN = "/cancel"; |
|
581 | ||
582 | /** <elseif> child element. */ |
|
583 | private static final String XPF_EIF = "/elseif"; |
|
584 | ||
585 | /** <else> child element. */ |
|
586 | private static final String XPF_ELS = "/else"; |
|
587 | ||
588 | //// 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 | //---------------------- PRIVATE UTILITY METHODS ----------------------// |
|
648 | /* |
|
649 | * Private utility functions for configuring digester rule base for SCXML. |
|
650 | */ |
|
651 | /** |
|
652 | * Initialize the Digester rules for the current document. |
|
653 | * |
|
654 | * @param scxml The parent SCXML document (or null) |
|
655 | * @param pr The PathResolver |
|
656 | * @param customActions The list of custom actions this digester needs |
|
657 | * to be able to process |
|
658 | * |
|
659 | * @return scxmlRules The rule set to be used for digestion |
|
660 | */ |
|
661 | private static ExtendedBaseRules initRules(final SCXML scxml, |
|
662 | final PathResolver pr, final List customActions) { |
|
663 | ||
664 | 54 | ExtendedBaseRules scxmlRules = new ExtendedBaseRules(); |
665 | 54 | scxmlRules.setNamespaceURI(NAMESPACE_SCXML); |
666 | ||
667 | //// SCXML |
|
668 | 54 | scxmlRules.add(XP_SM, new ObjectCreateRule(SCXML.class)); |
669 | 54 | scxmlRules.add(XP_SM, new SetPropertiesRule()); |
670 | ||
671 | //// Datamodel at document root i.e. <scxml> datamodel |
|
672 | 54 | addDatamodelRules(XP_SM + XPF_DM, scxmlRules, scxml, pr); |
673 | ||
674 | //// States |
|
675 | // Level one states |
|
676 | 54 | addStateRules(XP_SM_ST, scxmlRules, customActions, scxml, pr, 0); |
677 | 54 | scxmlRules.add(XP_SM_ST, new SetNextRule("addState")); |
678 | // Nested states |
|
679 | 54 | addStateRules(XPU_ST_ST, scxmlRules, customActions, scxml, pr, 1); |
680 | 54 | scxmlRules.add(XPU_ST_ST, new SetNextRule("addChild")); |
681 | ||
682 | // Parallel states |
|
683 | 54 | addStateRules(XPU_PAR_ST, scxmlRules, customActions, scxml, pr, 1); |
684 | 54 | scxmlRules.add(XPU_PAR_ST, new SetNextRule("addState")); |
685 | // Target states |
|
686 | 54 | addStateRules(XPU_TR_TAR_ST, scxmlRules, customActions, scxml, pr, 2); |
687 | 54 | scxmlRules.add(XPU_TR_TAR_ST, new SetNextRule("setTarget")); |
688 | ||
689 | //// Parallels |
|
690 | 54 | addParallelRules(XPU_ST_PAR, scxmlRules, pr, customActions, scxml); |
691 | ||
692 | //// Ifs |
|
693 | 54 | addIfRules(XPU_IF, scxmlRules, pr, customActions); |
694 | ||
695 | //// Custom actions |
|
696 | 54 | addCustomActionRules(XPU_ONEN, scxmlRules, customActions); |
697 | 54 | addCustomActionRules(XPU_ONEX, scxmlRules, customActions); |
698 | 54 | addCustomActionRules(XPU_TR, scxmlRules, customActions); |
699 | 54 | addCustomActionRules(XPU_IF, scxmlRules, customActions); |
700 | 54 | addCustomActionRules(XPU_FIN, scxmlRules, customActions); |
701 | ||
702 | 54 | return scxmlRules; |
703 | ||
704 | } |
|
705 | ||
706 | /** |
|
707 | * Add Digester rules for all <state> elements. |
|
708 | * |
|
709 | * @param xp The Digester style XPath expression of the parent |
|
710 | * XML element |
|
711 | * @param scxmlRules The rule set to be used for digestion |
|
712 | * @param customActions The list of custom actions this digester needs |
|
713 | * to be able to process |
|
714 | * @param scxml The parent SCXML document (or null) |
|
715 | * @param pr The PathResolver |
|
716 | * @param parent The distance between this state and its parent |
|
717 | * state on the Digester stack |
|
718 | */ |
|
719 | private static void addStateRules(final String xp, |
|
720 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
721 | final SCXML scxml, final PathResolver pr, final int parent) { |
|
722 | 216 | scxmlRules.add(xp, new ObjectCreateRule(State.class)); |
723 | 216 | addStatePropertiesRules(xp, scxmlRules, customActions, pr, scxml); |
724 | 216 | addDatamodelRules(xp + XPF_DM, scxmlRules, scxml, pr); |
725 | 216 | addInvokeRules(xp + XPF_INV, scxmlRules, customActions, pr, scxml); |
726 | 216 | addInitialRules(xp + XPF_INI, scxmlRules, customActions, pr, scxml); |
727 | 216 | addHistoryRules(xp + XPF_HIST, scxmlRules, customActions, pr, scxml); |
728 | 216 | addParentRule(xp, scxmlRules, parent); |
729 | 216 | addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition", |
730 | pr, customActions); |
|
731 | 216 | addHandlerRules(xp, scxmlRules, pr, customActions); |
732 | 216 | scxmlRules.add(xp, new UpdateModelRule(scxml)); |
733 | 216 | } |
734 | ||
735 | /** |
|
736 | * Add Digester rules for all <parallel> elements. |
|
737 | * |
|
738 | * @param xp The Digester style XPath expression of the parent |
|
739 | * XML element |
|
740 | * @param scxmlRules The rule set to be used for digestion |
|
741 | * @param customActions The list of custom actions this digester needs |
|
742 | * to be able to process |
|
743 | * @param pr The {@link PathResolver} for this document |
|
744 | * @param scxml The parent SCXML document (or null) |
|
745 | */ |
|
746 | private static void addParallelRules(final String xp, |
|
747 | final ExtendedBaseRules scxmlRules, final PathResolver pr, |
|
748 | final List customActions, final SCXML scxml) { |
|
749 | 54 | addSimpleRulesTuple(xp, scxmlRules, Parallel.class, null, null, |
750 | "setParallel"); |
|
751 | 54 | addHandlerRules(xp, scxmlRules, pr, customActions); |
752 | 54 | addParentRule(xp, scxmlRules, 1); |
753 | 54 | scxmlRules.add(xp, new UpdateModelRule(scxml)); |
754 | 54 | } |
755 | ||
756 | /** |
|
757 | * Add Digester rules for all <state> element attributes. |
|
758 | * |
|
759 | * @param xp The Digester style XPath expression of the parent |
|
760 | * XML element |
|
761 | * @param scxmlRules The rule set to be used for digestion |
|
762 | * @param customActions The list of custom actions this digester needs |
|
763 | * to be able to process |
|
764 | * @param pr The PathResolver |
|
765 | * @param scxml The root document, if this one is src'ed in |
|
766 | */ |
|
767 | private static void addStatePropertiesRules(final String xp, |
|
768 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
769 | final PathResolver pr, final SCXML scxml) { |
|
770 | 216 | scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id", "final"}, |
771 | new String[] {"id", "isFinal"})); |
|
772 | 216 | scxmlRules.add(xp, new DigestSrcAttributeRule(scxml, |
773 | customActions, pr)); |
|
774 | 216 | } |
775 | ||
776 | /** |
|
777 | * Add Digester rules for all <datamodel> elements. |
|
778 | * |
|
779 | * @param xp The Digester style XPath expression of the parent |
|
780 | * XML element |
|
781 | * @param scxmlRules The rule set to be used for digestion |
|
782 | * @param pr The PathResolver |
|
783 | * @param scxml The parent SCXML document (or null) |
|
784 | */ |
|
785 | private static void addDatamodelRules(final String xp, |
|
786 | final ExtendedBaseRules scxmlRules, final SCXML scxml, |
|
787 | final PathResolver pr) { |
|
788 | 270 | scxmlRules.add(xp, new ObjectCreateRule(Datamodel.class)); |
789 | 270 | scxmlRules.add(xp + XPF_DATA, new ObjectCreateRule(Data.class)); |
790 | 270 | scxmlRules.add(xp + XPF_DATA, new SetPropertiesRule()); |
791 | 270 | scxmlRules.add(xp + XPF_DATA, new SetCurrentNamespacesRule()); |
792 | 270 | scxmlRules.add(xp + XPF_DATA, new SetNextRule("addData")); |
793 | try { |
|
794 | 270 | scxmlRules.add(xp + XPF_DATA, new ParseDataRule(pr)); |
795 | 0 | } catch (ParserConfigurationException pce) { |
796 | 0 | org.apache.commons.logging.Log log = LogFactory. |
797 | getLog(SCXMLDigester.class); |
|
798 | 0 | log.error(ERR_PARSER_CFG_DATA, pce); |
799 | 270 | } |
800 | 270 | scxmlRules.add(xp, new SetNextRule("setDatamodel")); |
801 | 270 | } |
802 | ||
803 | /** |
|
804 | * Add Digester rules for all <invoke> elements. |
|
805 | * |
|
806 | * @param xp The Digester style XPath expression of the parent |
|
807 | * XML element |
|
808 | * @param scxmlRules The rule set to be used for digestion |
|
809 | * @param customActions The list of {@link CustomAction}s this digester |
|
810 | * instance will process, can be null or empty |
|
811 | * @param pr The PathResolver |
|
812 | * @param scxml The parent SCXML document (or null) |
|
813 | */ |
|
814 | private static void addInvokeRules(final String xp, |
|
815 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
816 | final PathResolver pr, final SCXML scxml) { |
|
817 | 216 | scxmlRules.add(xp, new ObjectCreateRule(Invoke.class)); |
818 | 216 | scxmlRules.add(xp, new SetPropertiesRule()); |
819 | 216 | scxmlRules.add(xp, new SetCurrentNamespacesRule()); |
820 | 216 | scxmlRules.add(xp, new SetPathResolverRule(pr)); |
821 | 216 | scxmlRules.add(xp + XPF_PRM, new ObjectCreateRule(Param.class)); |
822 | 216 | scxmlRules.add(xp + XPF_PRM, new SetPropertiesRule()); |
823 | 216 | scxmlRules.add(xp + XPF_PRM, new SetCurrentNamespacesRule()); |
824 | 216 | scxmlRules.add(xp + XPF_PRM, new SetNextRule("addParam")); |
825 | 216 | scxmlRules.add(xp + XPF_FIN, new ObjectCreateRule(Finalize.class)); |
826 | 216 | scxmlRules.add(xp + XPF_FIN, new UpdateFinalizeRule()); |
827 | 216 | addActionRules(xp + XPF_FIN, scxmlRules, pr, customActions); |
828 | 216 | scxmlRules.add(xp + XPF_FIN, new SetNextRule("setFinalize")); |
829 | 216 | scxmlRules.add(xp, new SetNextRule("setInvoke")); |
830 | 216 | } |
831 | ||
832 | /** |
|
833 | * Add Digester rules for all <initial> elements. |
|
834 | * |
|
835 | * @param xp The Digester style XPath expression of the parent |
|
836 | * XML element |
|
837 | * @param scxmlRules The rule set to be used for digestion |
|
838 | * @param customActions The list of custom actions this digester needs |
|
839 | * to be able to process |
|
840 | * @param pr The PathResolver |
|
841 | * @param scxml The parent SCXML document (or null) |
|
842 | */ |
|
843 | private static void addInitialRules(final String xp, |
|
844 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
845 | final PathResolver pr, final SCXML scxml) { |
|
846 | 216 | scxmlRules.add(xp, new ObjectCreateRule(Initial.class)); |
847 | 216 | addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr, |
848 | scxml); |
|
849 | 216 | scxmlRules.add(xp, new UpdateModelRule(scxml)); |
850 | 216 | addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition", |
851 | pr, customActions); |
|
852 | 216 | scxmlRules.add(xp, new SetNextRule("setInitial")); |
853 | 216 | } |
854 | ||
855 | /** |
|
856 | * Add Digester rules for all <history> elements. |
|
857 | * |
|
858 | * @param xp The Digester style XPath expression of the parent |
|
859 | * XML element |
|
860 | * @param scxmlRules The rule set to be used for digestion |
|
861 | * @param customActions The list of custom actions this digester needs |
|
862 | * to be able to process |
|
863 | * @param pr The PathResolver |
|
864 | * @param scxml The parent SCXML document (or null) |
|
865 | */ |
|
866 | private static void addHistoryRules(final String xp, |
|
867 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
868 | final PathResolver pr, final SCXML scxml) { |
|
869 | 216 | scxmlRules.add(xp, new ObjectCreateRule(History.class)); |
870 | 216 | addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr, |
871 | scxml); |
|
872 | 216 | scxmlRules.add(xp, new UpdateModelRule(scxml)); |
873 | 216 | scxmlRules.add(xp, new SetPropertiesRule(new String[] {"type"}, |
874 | new String[] {"type"})); |
|
875 | 216 | addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition", |
876 | pr, customActions); |
|
877 | 216 | scxmlRules.add(xp, new SetNextRule("addHistory")); |
878 | 216 | } |
879 | ||
880 | /** |
|
881 | * Add Digester rules for all pseudo state (initial, history) element |
|
882 | * attributes. |
|
883 | * |
|
884 | * @param xp The Digester style XPath expression of the parent |
|
885 | * XML element |
|
886 | * @param scxmlRules The rule set to be used for digestion |
|
887 | * @param customActions The list of custom actions this digester needs |
|
888 | * to be able to process |
|
889 | * @param pr The PathResolver |
|
890 | * @param scxml The root document, if this one is src'ed in |
|
891 | */ |
|
892 | private static void addPseudoStatePropertiesRules(final String xp, |
|
893 | final ExtendedBaseRules scxmlRules, final List customActions, |
|
894 | final PathResolver pr, final SCXML scxml) { |
|
895 | 432 | scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id"}, |
896 | new String[] {"id"})); |
|
897 | 432 | scxmlRules.add(xp, new DigestSrcAttributeRule(scxml, customActions, |
898 | pr)); |
|
899 | 432 | addParentRule(xp, scxmlRules, 1); |
900 | 432 | } |
901 | ||
902 | /** |
|
903 | * Add Digester rule for all setting parent state. |
|
904 | * |
|
905 | * @param xp The Digester style XPath expression of the parent |
|
906 | * XML element |
|
907 | * @param scxmlRules The rule set to be used for digestion |
|
908 | * @param parent The distance between this state and its parent |
|
909 | * state on the Digester stack |
|
910 | */ |
|
911 | private static void addParentRule(final String xp, |
|
912 | final ExtendedBaseRules scxmlRules, final int parent) { |
|
913 | 702 | if (parent < 1) { |
914 | 54 | return; |
915 | } |
|
916 | 648 | scxmlRules.add(xp, new Rule() { |
917 | // A generic version of setTopRule |
|
918 | 648 | public void body(final String namespace, final String name, |
919 | final String text) throws Exception { |
|
920 | 153 | TransitionTarget t = (TransitionTarget) getDigester().peek(); |
921 | 153 | TransitionTarget p = (TransitionTarget) getDigester().peek( |
922 | parent); |
|
923 | // CHANGE - Moved parent property to TransitionTarget |
|
924 | 153 | t.setParent(p); |
925 | 153 | } |
926 | }); |
|
927 | 648 | } |
928 | ||
929 | /** |
|
930 | * Add Digester rules for all <transition> elements. |
|
931 | * |
|
932 | * @param xp The Digester style XPath expression of the parent |
|
933 | * XML element |
|
934 | * @param scxmlRules The rule set to be used for digestion |
|
935 | * @param setNextMethod The method name for adding this transition |
|
936 | * to its parent (defined by the SCXML Java object model). |
|
937 | * @param pr The {@link PathResolver} for this document |
|
938 | * @param customActions The list of custom actions this digester needs |
|
939 | * to be able to process |
|
940 | */ |
|
941 | private static void addTransitionRules(final String xp, |
|
942 | final ExtendedBaseRules scxmlRules, final String setNextMethod, |
|
943 | final PathResolver pr, final List customActions) { |
|
944 | 648 | scxmlRules.add(xp, new ObjectCreateRule(Transition.class)); |
945 | 648 | scxmlRules.add(xp, new SetPropertiesRule( |
946 | new String[] {"event", "cond", "target"}, |
|
947 | new String[] {"event", "cond", "next"})); |
|
948 | 648 | scxmlRules.add(xp, new SetCurrentNamespacesRule()); |
949 | 648 | scxmlRules.add(xp + XPF_TAR, new SetPropertiesRule()); |
950 | 648 | addActionRules(xp, scxmlRules, pr, customActions); |
951 | 648 | scxmlRules.add(xp + XPF_EXT, new Rule() { |
952 | 648 | public void end(final String namespace, final String name) { |
953 | 0 | Transition t = (Transition) getDigester().peek(1); |
954 | 0 | State exitState = new State(); |
955 | 0 | exitState.setIsFinal(true); |
956 | 0 | t.setTarget(exitState); |
957 | 0 | } |
958 | }); |
|
959 | 648 | scxmlRules.add(xp, new SetNextRule(setNextMethod)); |
960 | 648 | } |
961 | ||
962 | /** |
|
963 | * Add Digester rules for all <onentry> and <onexit> |
|
964 | * elements. |
|
965 | * |
|
966 | * @param xp The Digester style XPath expression of the parent |
|
967 | * XML element |
|
968 | * @param scxmlRules The rule set to be used for digestion |
|
969 | * @param pr The {@link PathResolver} for this document |
|
970 | * @param customActions The list of custom actions this digester needs |
|
971 | * to be able to process |
|
972 | */ |
|
973 | private static void addHandlerRules(final String xp, |
|
974 | final ExtendedBaseRules scxmlRules, final PathResolver pr, |
|
975 | final List customActions) { |
|
976 | 270 | scxmlRules.add(xp + XPF_ONEN, new ObjectCreateRule(OnEntry.class)); |
977 | 270 | addActionRules(xp + XPF_ONEN, scxmlRules, pr, customActions); |
978 | 270 | scxmlRules.add(xp + XPF_ONEN, new SetNextRule("setOnEntry")); |
979 | 270 | scxmlRules.add(xp + XPF_ONEX, new ObjectCreateRule(OnExit.class)); |
980 | 270 | addActionRules(xp + XPF_ONEX, scxmlRules, pr, customActions); |
981 | 270 | scxmlRules.add(xp + XPF_ONEX, new SetNextRule("setOnExit")); |
982 | 270 | } |
983 | ||
984 | /** |
|
985 | * Add Digester rules for all actions ("executable" elements). |
|
986 | * |
|
987 | * @param xp The Digester style XPath expression of the parent |
|
988 | * XML element |
|
989 | * @param scxmlRules The rule set to be used for digestion |
|
990 | * @param pr The {@link PathResolver} for this document |
|
991 | * @param customActions The list of custom actions this digester needs |
|
992 | * to be able to process |
|
993 | */ |
|
994 | private static void addActionRules(final String xp, |
|
995 | final ExtendedBaseRules scxmlRules, final PathResolver pr, |
|
996 | final List customActions) { |
|
997 | 1458 | addActionRulesTuple(xp + XPF_ASN, scxmlRules, Assign.class); |
998 | 1458 | scxmlRules.add(xp + XPF_ASN, new SetPathResolverRule(pr)); |
999 | 1458 | addActionRulesTuple(xp + XPF_VAR, scxmlRules, Var.class); |
1000 | 1458 | addActionRulesTuple(xp + XPF_LOG, scxmlRules, Log.class); |
1001 | 1458 | addSendRulesTuple(xp + XPF_SND, scxmlRules); |
1002 | 1458 | addActionRulesTuple(xp + XPF_CAN, scxmlRules, Cancel.class); |
1003 | 1458 | addActionRulesTuple(xp + XPF_EXT, scxmlRules, Exit.class); |
1004 | //addCustomActionRules(xp, scxmlRules, customActions); |
|
1005 | 1458 | } |
1006 | ||
1007 | /** |
|
1008 | * Add custom action rules, if any custom actions are provided. |
|
1009 | * |
|
1010 | * @param xp The Digester style XPath expression of the parent |
|
1011 | * XML element |
|
1012 | * @param scxmlRules The rule set to be used for digestion |
|
1013 | * @param customActions The list of custom actions this digester needs |
|
1014 | * to be able to process |
|
1015 | */ |
|
1016 | private static void addCustomActionRules(final String xp, |
|
1017 | final ExtendedBaseRules scxmlRules, final List customActions) { |
|
1018 | 270 | if (customActions == null || customActions.size() == 0) { |
1019 | 250 | return; |
1020 | } |
|
1021 | 45 | for (int i = 0; i < customActions.size(); i++) { |
1022 | 25 | Object item = customActions.get(i); |
1023 | 25 | if (item == null || !(item instanceof CustomAction)) { |
1024 | 0 | org.apache.commons.logging.Log log = LogFactory. |
1025 | getLog(SCXMLDigester.class); |
|
1026 | 0 | log.warn(ERR_CUSTOM_ACTION_TYPE); |
1027 | } else { |
|
1028 | 25 | CustomAction ca = (CustomAction) item; |
1029 | 25 | scxmlRules.setNamespaceURI(ca.getNamespaceURI()); |
1030 | 25 | String xpfLocalName = STR_SLASH + ca.getLocalName(); |
1031 | 25 | Class klass = ca.getActionClass(); |
1032 | 25 | if (SCXMLHelper.implementationOf(klass, |
1033 | ExternalContent.class)) { |
|
1034 | 0 | addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules, |
1035 | klass, true); |
|
1036 | } else { |
|
1037 | 25 | addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules, |
1038 | klass, false); |
|
1039 | } |
|
1040 | } |
|
1041 | } |
|
1042 | 20 | scxmlRules.setNamespaceURI(NAMESPACE_SCXML); |
1043 | 20 | } |
1044 | ||
1045 | /** |
|
1046 | * Add Digester rules that are specific to the <send> action |
|
1047 | * element. |
|
1048 | * |
|
1049 | * @param xp The Digester style XPath expression of <send> element |
|
1050 | * @param scxmlRules The rule set to be used for digestion |
|
1051 | */ |
|
1052 | private static void addSendRulesTuple(final String xp, |
|
1053 | final ExtendedBaseRules scxmlRules) { |
|
1054 | 1458 | addActionRulesTuple(xp, scxmlRules, Send.class); |
1055 | try { |
|
1056 | 1458 | scxmlRules.add(xp, new ParseExternalContentRule()); |
1057 | 0 | } catch (ParserConfigurationException pce) { |
1058 | 0 | org.apache.commons.logging.Log log = LogFactory. |
1059 | getLog(SCXMLDigester.class); |
|
1060 | 0 | log.error(ERR_PARSER_CFG_SEND, pce); |
1061 | 1458 | } |
1062 | 1458 | } |
1063 | ||
1064 | /** |
|
1065 | * Add Digester rules for a simple custom action (no body content). |
|
1066 | * |
|
1067 | * @param xp The path to the custom action element |
|
1068 | * @param scxmlRules The rule set to be used for digestion |
|
1069 | * @param klass The <code>Action</code> class implementing the custom |
|
1070 | * action. |
|
1071 | * @param bodyContent Whether the custom rule has body content |
|
1072 | * that should be parsed using |
|
1073 | * <code>NodeCreateRule</code> |
|
1074 | */ |
|
1075 | private static void addCustomActionRulesTuple(final String xp, |
|
1076 | final ExtendedBaseRules scxmlRules, final Class klass, |
|
1077 | final boolean bodyContent) { |
|
1078 | 25 | addActionRulesTuple(xp, scxmlRules, klass); |
1079 | 25 | if (bodyContent) { |
1080 | try { |
|
1081 | 0 | scxmlRules.add(xp, new ParseExternalContentRule()); |
1082 | 0 | } catch (ParserConfigurationException pce) { |
1083 | 0 | org.apache.commons.logging.Log log = LogFactory. |
1084 | getLog(SCXMLDigester.class); |
|
1085 | 0 | log.error(ERR_PARSER_CFG_CUSTOM, pce); |
1086 | 0 | } |
1087 | } |
|
1088 | 25 | } |
1089 | ||
1090 | /** |
|
1091 | * Add Digester rules for all <if> elements. |
|
1092 | * |
|
1093 | * @param xp The Digester style XPath expression of the parent |
|
1094 | * XML element |
|
1095 | * @param scxmlRules The rule set to be used for digestion |
|
1096 | * @param pr The {@link PathResolver} for this document |
|
1097 | * @param customActions The list of custom actions this digester needs |
|
1098 | * to be able to process |
|
1099 | */ |
|
1100 | private static void addIfRules(final String xp, |
|
1101 | final ExtendedBaseRules scxmlRules, final PathResolver pr, |
|
1102 | final List customActions) { |
|
1103 | 54 | addActionRulesTuple(xp, scxmlRules, If.class); |
1104 | 54 | addActionRules(xp, scxmlRules, pr, customActions); |
1105 | 54 | addActionRulesTuple(xp + XPF_EIF, scxmlRules, ElseIf.class); |
1106 | 54 | addActionRulesTuple(xp + XPF_ELS, scxmlRules, Else.class); |
1107 | 54 | } |
1108 | ||
1109 | /** |
|
1110 | * Add Digester rules that are common across all actions elements. |
|
1111 | * |
|
1112 | * @param xp The Digester style XPath expression of the parent |
|
1113 | * XML element |
|
1114 | * @param scxmlRules The rule set to be used for digestion |
|
1115 | * @param klass The class in the Java object model to be instantiated |
|
1116 | * in the ObjectCreateRule for this action |
|
1117 | */ |
|
1118 | private static void addActionRulesTuple(final String xp, |
|
1119 | final ExtendedBaseRules scxmlRules, final Class klass) { |
|
1120 | 8935 | addSimpleRulesTuple(xp, scxmlRules, klass, null, null, "addAction"); |
1121 | 8935 | scxmlRules.add(xp, new SetExecutableParentRule()); |
1122 | 8935 | scxmlRules.add(xp, new SetCurrentNamespacesRule()); |
1123 | 8935 | } |
1124 | ||
1125 | /** |
|
1126 | * Add the run of the mill Digester rules for any element. |
|
1127 | * |
|
1128 | * @param xp The Digester style XPath expression of the parent |
|
1129 | * XML element |
|
1130 | * @param scxmlRules The rule set to be used for digestion |
|
1131 | * @param klass The class in the Java object model to be instantiated |
|
1132 | * in the ObjectCreateRule for this action |
|
1133 | * @param args The attributes to be mapped into the object model |
|
1134 | * @param props The properties that args get mapped to |
|
1135 | * @param addMethod The method that the SetNextRule should call |
|
1136 | */ |
|
1137 | private static void addSimpleRulesTuple(final String xp, |
|
1138 | final ExtendedBaseRules scxmlRules, final Class klass, |
|
1139 | final String[] args, final String[] props, |
|
1140 | final String addMethod) { |
|
1141 | 8989 | scxmlRules.add(xp, new ObjectCreateRule(klass)); |
1142 | 8989 | if (args == null) { |
1143 | 8989 | scxmlRules.add(xp, new SetPropertiesRule()); |
1144 | } else { |
|
1145 | 0 | scxmlRules.add(xp, new SetPropertiesRule(args, props)); |
1146 | } |
|
1147 | 8989 | scxmlRules.add(xp, new SetNextRule(addMethod)); |
1148 | 8989 | } |
1149 | ||
1150 | /** |
|
1151 | * Discourage instantiation since this is a utility class. |
|
1152 | */ |
|
1153 | private SCXMLDigester() { |
|
1154 | 0 | super(); |
1155 | 0 | } |
1156 | ||
1157 | /** |
|
1158 | * Custom digestion rule for establishing necessary associations of this |
|
1159 | * TransitionTarget with the root SCXML object. |
|
1160 | * These include: <br> |
|
1161 | * 1) Updation of the SCXML object's global targets Map <br> |
|
1162 | * 2) Obtaining a handle to the SCXML object's NotificationRegistry <br> |
|
1163 | * |
|
1164 | * @deprecated Will be removed in version 1.0 |
|
1165 | */ |
|
1166 | public static class UpdateModelRule extends Rule { |
|
1167 | ||
1168 | /** |
|
1169 | * The root SCXML object. |
|
1170 | */ |
|
1171 | private SCXML scxml; |
|
1172 | ||
1173 | /** |
|
1174 | * Constructor. |
|
1175 | * @param scxml The root SCXML object |
|
1176 | */ |
|
1177 | public UpdateModelRule(final SCXML scxml) { |
|
1178 | 702 | super(); |
1179 | 702 | this.scxml = scxml; |
1180 | 702 | } |
1181 | ||
1182 | /** |
|
1183 | * @see Rule#end(String, String) |
|
1184 | */ |
|
1185 | public final void end(final String namespace, final String name) { |
|
1186 | 294 | if (scxml == null) { |
1187 | 117 | scxml = (SCXML) getDigester() |
1188 | .peek(getDigester().getCount() - 1); |
|
1189 | } |
|
1190 | 294 | TransitionTarget tt = (TransitionTarget) getDigester().peek(); |
1191 | 294 | scxml.addTarget(tt); |
1192 | 294 | } |
1193 | } |
|
1194 | ||
1195 | /** |
|
1196 | * Custom digestion rule for setting Executable parent of Action elements. |
|
1197 | * |
|
1198 | * @deprecated Will be removed in version 1.0 |
|
1199 | */ |
|
1200 | public static class SetExecutableParentRule extends Rule { |
|
1201 | ||
1202 | /** |
|
1203 | * Constructor. |
|
1204 | */ |
|
1205 | public SetExecutableParentRule() { |
|
1206 | 8935 | super(); |
1207 | 8935 | } |
1208 | ||
1209 | /** |
|
1210 | * @see Rule#end(String, String) |
|
1211 | */ |
|
1212 | public final void end(final String namespace, final String name) { |
|
1213 | 181 | Action child = (Action) getDigester().peek(); |
1214 | 215 | for (int i = 1; i < getDigester().getCount() - 1; i++) { |
1215 | 215 | Object ancestor = getDigester().peek(i); |
1216 | 215 | if (ancestor instanceof Executable) { |
1217 | 181 | child.setParent((Executable) ancestor); |
1218 | 181 | return; |
1219 | } |
|
1220 | } |
|
1221 | 0 | } |
1222 | } |
|
1223 | ||
1224 | /** |
|
1225 | * Custom digestion rule for parsing bodies of |
|
1226 | * <code>ExternalContent</code> elements. |
|
1227 | * |
|
1228 | * @see ExternalContent |
|
1229 | * |
|
1230 | * @deprecated Will be removed in version 1.0 |
|
1231 | */ |
|
1232 | public static class ParseExternalContentRule extends NodeCreateRule { |
|
1233 | /** |
|
1234 | * Constructor. |
|
1235 | * @throws ParserConfigurationException A JAXP configuration error |
|
1236 | */ |
|
1237 | public ParseExternalContentRule() |
|
1238 | throws ParserConfigurationException { |
|
1239 | 1458 | super(); |
1240 | 1458 | } |
1241 | /** |
|
1242 | * @see Rule#end(String, String) |
|
1243 | */ |
|
1244 | public final void end(final String namespace, final String name) { |
|
1245 | 14 | Element bodyElement = (Element) getDigester().pop(); |
1246 | 14 | NodeList childNodes = bodyElement.getChildNodes(); |
1247 | 14 | List externalNodes = ((ExternalContent) getDigester(). |
1248 | peek()).getExternalNodes(); |
|
1249 | 20 | for (int i = 0; i < childNodes.getLength(); i++) { |
1250 | 6 | externalNodes.add(childNodes.item(i)); |
1251 | } |
|
1252 | 14 | } |
1253 | } |
|
1254 | ||
1255 | /** |
|
1256 | * Custom digestion rule for parsing bodies of <data> elements. |
|
1257 | * |
|
1258 | * @deprecated Will be removed in version 1.0 |
|
1259 | */ |
|
1260 | public static class ParseDataRule extends NodeCreateRule { |
|
1261 | ||
1262 | /** |
|
1263 | * The PathResolver used to resolve the src attribute to the |
|
1264 | * SCXML document it points to. |
|
1265 | * @see PathResolver |
|
1266 | */ |
|
1267 | private PathResolver pr; |
|
1268 | ||
1269 | /** |
|
1270 | * The "src" attribute, retained to check if body content is legal. |
|
1271 | */ |
|
1272 | private String src; |
|
1273 | ||
1274 | /** |
|
1275 | * The "expr" attribute, retained to check if body content is legal. |
|
1276 | */ |
|
1277 | private String expr; |
|
1278 | ||
1279 | /** |
|
1280 | * The XML tree for this data, parse as a Node, obtained from |
|
1281 | * either the "src" or the "expr" attributes. |
|
1282 | */ |
|
1283 | private Node attrNode; |
|
1284 | ||
1285 | /** |
|
1286 | * Constructor. |
|
1287 | * |
|
1288 | * @param pr The <code>PathResolver</code> |
|
1289 | * @throws ParserConfigurationException A JAXP configuration error |
|
1290 | */ |
|
1291 | public ParseDataRule(final PathResolver pr) |
|
1292 | throws ParserConfigurationException { |
|
1293 | 270 | super(); |
1294 | 270 | this.pr = pr; |
1295 | 270 | } |
1296 | ||
1297 | /** |
|
1298 | * @see Rule#begin(String, String, Attributes) |
|
1299 | */ |
|
1300 | public final void begin(final String namespace, final String name, |
|
1301 | final Attributes attributes) throws Exception { |
|
1302 | 64 | super.begin(namespace, name, attributes); |
1303 | 64 | src = attributes.getValue("src"); |
1304 | 64 | expr = attributes.getValue("expr"); |
1305 | 64 | if (!SCXMLHelper.isStringEmpty(src)) { |
1306 | 0 | String path = null; |
1307 | 0 | if (pr == null) { |
1308 | 0 | path = src; |
1309 | } else { |
|
1310 | 0 | path = pr.resolvePath(src); |
1311 | } |
|
1312 | try { |
|
1313 | 0 | DocumentBuilderFactory dbFactory = DocumentBuilderFactory. |
1314 | newInstance(); |
|
1315 | 0 | DocumentBuilder db = dbFactory.newDocumentBuilder(); |
1316 | 0 | attrNode = db.parse(path); |
1317 | 0 | } catch (Throwable t) { // you read that correctly |
1318 | 0 | org.apache.commons.logging.Log log = LogFactory. |
1319 | getLog(SCXMLDigester.class); |
|
1320 | 0 | log.error(t.getMessage(), t); |
1321 | 0 | } |
1322 | 0 | return; |
1323 | } |
|
1324 | 64 | } |
1325 | ||
1326 | /** |
|
1327 | * @see Rule#end(String, String) |
|
1328 | */ |
|
1329 | public final void end(final String namespace, final String name) { |
|
1330 | 64 | Node bodyNode = (Node) getDigester().pop(); |
1331 | 64 | Data data = ((Data) getDigester().peek()); |
1332 | // Prefer "src" over "expr", "expr" over child nodes |
|
1333 | // "expr" can only be evaluated at execution time |
|
1334 | 64 | if (!SCXMLHelper.isStringEmpty(src)) { |
1335 | 0 | data.setNode(attrNode); |
1336 | 64 | } else if (SCXMLHelper.isStringEmpty(expr)) { |
1337 | // both "src" and "expr" are empty |
|
1338 | 60 | data.setNode(bodyNode); |
1339 | } |
|
1340 | 64 | } |
1341 | } |
|
1342 | ||
1343 | /** |
|
1344 | * Custom digestion rule for external sources, that is, the src attribute of |
|
1345 | * the <state> element. |
|
1346 | * |
|
1347 | * @deprecated Will be removed in version 1.0 |
|
1348 | */ |
|
1349 | public static class DigestSrcAttributeRule extends Rule { |
|
1350 | ||
1351 | /** |
|
1352 | * The PathResolver used to resolve the src attribute to the |
|
1353 | * SCXML document it points to. |
|
1354 | * @see PathResolver |
|
1355 | */ |
|
1356 | private PathResolver pr; |
|
1357 | ||
1358 | /** |
|
1359 | * The root document. |
|
1360 | */ |
|
1361 | private SCXML root; |
|
1362 | ||
1363 | /** |
|
1364 | * The list of custom actions the parent document is capable of |
|
1365 | * processing (and hence, the child should be, by transitivity). |
|
1366 | * @see CustomAction |
|
1367 | */ |
|
1368 | private List customActions; |
|
1369 | ||
1370 | /** |
|
1371 | * Constructor. |
|
1372 | * @param pr The PathResolver |
|
1373 | * @param customActions The list of custom actions this digester needs |
|
1374 | * to be able to process |
|
1375 | * |
|
1376 | * @see PathResolver |
|
1377 | * @see CustomAction |
|
1378 | * |
|
1379 | * TODO: Remove in v1.0 |
|
1380 | */ |
|
1381 | public DigestSrcAttributeRule(final List customActions, |
|
1382 | final PathResolver pr) { |
|
1383 | 0 | super(); |
1384 | 0 | this.customActions = customActions; |
1385 | 0 | this.pr = pr; |
1386 | 0 | } |
1387 | ||
1388 | /** |
|
1389 | * Constructor. |
|
1390 | * @param root The root document, if this one is src'ed in |
|
1391 | * @param pr The PathResolver |
|
1392 | * @param customActions The list of custom actions this digester needs |
|
1393 | * to be able to process |
|
1394 | * |
|
1395 | * @see PathResolver |
|
1396 | * @see CustomAction |
|
1397 | */ |
|
1398 | public DigestSrcAttributeRule(final SCXML root, |
|
1399 | final List customActions, final PathResolver pr) { |
|
1400 | 648 | super(); |
1401 | 648 | this.root = root; |
1402 | 648 | this.customActions = customActions; |
1403 | 648 | this.pr = pr; |
1404 | 648 | } |
1405 | ||
1406 | /** |
|
1407 | * @see Rule#begin(String, String, Attributes) |
|
1408 | */ |
|
1409 | public final void begin(final String namespace, final String name, |
|
1410 | final Attributes attributes) { |
|
1411 | 289 | String src = attributes.getValue("src"); |
1412 | 289 | if (SCXMLHelper.isStringEmpty(src)) { |
1413 | 286 | return; |
1414 | } |
|
1415 | 3 | Digester digester = getDigester(); |
1416 | 3 | SCXML scxml = (SCXML) digester.peek(digester.getCount() - 1); |
1417 | // 1) Digest the external SCXML file |
|
1418 | 3 | SCXML externalSCXML = null; |
1419 | String path; |
|
1420 | Digester externalSrcDigester; |
|
1421 | 3 | if (pr == null) { |
1422 | 0 | path = src; |
1423 | 0 | if (root != null) { |
1424 | 0 | externalSrcDigester = newInstance(root, null, |
1425 | customActions); |
|
1426 | } else { |
|
1427 | 0 | externalSrcDigester = newInstance(scxml, null, |
1428 | customActions); |
|
1429 | } |
|
1430 | } else { |
|
1431 | 3 | path = pr.resolvePath(src); |
1432 | 3 | if (root != null) { |
1433 | 1 | externalSrcDigester = newInstance(root, |
1434 | pr.getResolver(src), customActions); |
|
1435 | } else { |
|
1436 | 2 | externalSrcDigester = newInstance(scxml, |
1437 | pr.getResolver(src), customActions); |
|
1438 | } |
|
1439 | } |
|
1440 | ||
1441 | try { |
|
1442 | 3 | externalSCXML = (SCXML) externalSrcDigester.parse(path); |
1443 | 0 | } catch (Exception e) { |
1444 | 0 | org.apache.commons.logging.Log log = LogFactory. |
1445 | getLog(SCXMLDigester.class); |
|
1446 | 0 | log.error(e.getMessage(), e); |
1447 | 3 | } |
1448 | // 2) Adopt the children and datamodel |
|
1449 | 3 | if (externalSCXML == null) { |
1450 | 0 | return; |
1451 | } |
|
1452 | 3 | State s = (State) digester.peek(); |
1453 | 3 | Transition t = new Transition(); |
1454 | 3 | t.setNext(externalSCXML.getInitialstate()); |
1455 | 3 | Initial ini = new Initial(); |
1456 | 3 | ini.setTransition(t); |
1457 | 3 | s.setInitial(ini); |
1458 | 3 | Map children = externalSCXML.getStates(); |
1459 | 3 | Object[] ids = children.keySet().toArray(); |
1460 | 8 | for (int i = 0; i < ids.length; i++) { |
1461 | 5 | s.addChild((State) children.get(ids[i])); |
1462 | } |
|
1463 | 3 | s.setDatamodel(externalSCXML.getDatamodel()); |
1464 | 3 | } |
1465 | } |
|
1466 | ||
1467 | /** |
|
1468 | * Custom digestion rule for setting PathResolver for runtime retrieval. |
|
1469 | * |
|
1470 | * @deprecated Will be removed in version 1.0 |
|
1471 | */ |
|
1472 | public static class SetPathResolverRule extends Rule { |
|
1473 | ||
1474 | /** |
|
1475 | * The PathResolver to set. |
|
1476 | * @see PathResolver |
|
1477 | */ |
|
1478 | private PathResolver pr; |
|
1479 | ||
1480 | /** |
|
1481 | * Constructor. |
|
1482 | * @param pr The PathResolver |
|
1483 | * |
|
1484 | * @see PathResolver |
|
1485 | */ |
|
1486 | public SetPathResolverRule(final PathResolver pr) { |
|
1487 | 1674 | super(); |
1488 | 1674 | this.pr = pr; |
1489 | 1674 | } |
1490 | ||
1491 | /** |
|
1492 | * @see Rule#begin(String, String, Attributes) |
|
1493 | */ |
|
1494 | public final void begin(final String namespace, final String name, |
|
1495 | final Attributes attributes) { |
|
1496 | 67 | PathResolverHolder prHolder = (PathResolverHolder) getDigester(). |
1497 | peek(); |
|
1498 | 67 | prHolder.setPathResolver(pr); |
1499 | 67 | } |
1500 | } |
|
1501 | ||
1502 | /** |
|
1503 | * Custom digestion rule for setting state parent of finalize. |
|
1504 | * |
|
1505 | * @deprecated Will be removed in version 1.0 |
|
1506 | */ |
|
1507 | 216 | public static class UpdateFinalizeRule extends Rule { |
1508 | ||
1509 | /** |
|
1510 | * @see Rule#begin(String, String, Attributes) |
|
1511 | */ |
|
1512 | public final void begin(final String namespace, final String name, |
|
1513 | final Attributes attributes) { |
|
1514 | 1 | Finalize finalize = (Finalize) getDigester().peek(); |
1515 | // state/invoke/finalize --> peek(2) |
|
1516 | 1 | TransitionTarget tt = (TransitionTarget) getDigester().peek(2); |
1517 | 1 | finalize.setParent(tt); |
1518 | 1 | } |
1519 | } |
|
1520 | ||
1521 | ||
1522 | /** |
|
1523 | * Custom digestion rule for attaching a snapshot of current namespaces |
|
1524 | * to SCXML actions for deferred XPath evaluation. |
|
1525 | * |
|
1526 | */ |
|
1527 | 20570 | private static class SetCurrentNamespacesRule extends Rule { |
1528 | ||
1529 | /** |
|
1530 | * @see Rule#begin(String, String, Attributes) |
|
1531 | */ |
|
1532 | public final void begin(final String namespace, final String name, |
|
1533 | final Attributes attributes) { |
|
1534 | 488 | NamespacePrefixesHolder nsHolder = |
1535 | (NamespacePrefixesHolder) getDigester().peek(); |
|
1536 | 488 | nsHolder.setNamespaces(getDigester().getCurrentNamespaces()); |
1537 | 488 | } |
1538 | } |
|
1539 | ||
1540 | } |
|
1541 |