View Javadoc

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