0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.commons.scxml.io;
0018:
0019: import java.io.IOException;
0020: import java.net.URL;
0021: import java.text.MessageFormat;
0022: import java.util.List;
0023: import java.util.Map;
0024:
0025: import javax.xml.parsers.DocumentBuilder;
0026: import javax.xml.parsers.DocumentBuilderFactory;
0027: import javax.xml.parsers.ParserConfigurationException;
0028:
0029: import org.apache.commons.digester.Digester;
0030: import org.apache.commons.digester.ExtendedBaseRules;
0031: import org.apache.commons.digester.NodeCreateRule;
0032: import org.apache.commons.digester.ObjectCreateRule;
0033: import org.apache.commons.digester.Rule;
0034: import org.apache.commons.digester.SetNextRule;
0035: import org.apache.commons.digester.SetPropertiesRule;
0036: import org.apache.commons.logging.LogFactory;
0037:
0038: import org.apache.commons.scxml.PathResolver;
0039: import org.apache.commons.scxml.SCXMLHelper;
0040: import org.apache.commons.scxml.env.URLResolver;
0041: import org.apache.commons.scxml.model.Action;
0042: import org.apache.commons.scxml.model.Assign;
0043: import org.apache.commons.scxml.model.Cancel;
0044: import org.apache.commons.scxml.model.CustomAction;
0045: import org.apache.commons.scxml.model.Data;
0046: import org.apache.commons.scxml.model.Datamodel;
0047: import org.apache.commons.scxml.model.Else;
0048: import org.apache.commons.scxml.model.ElseIf;
0049: import org.apache.commons.scxml.model.Executable;
0050: import org.apache.commons.scxml.model.Exit;
0051: import org.apache.commons.scxml.model.ExternalContent;
0052: import org.apache.commons.scxml.model.Finalize;
0053: import org.apache.commons.scxml.model.History;
0054: import org.apache.commons.scxml.model.If;
0055: import org.apache.commons.scxml.model.Initial;
0056: import org.apache.commons.scxml.model.Invoke;
0057: import org.apache.commons.scxml.model.Log;
0058: import org.apache.commons.scxml.model.ModelException;
0059: import org.apache.commons.scxml.model.NamespacePrefixesHolder;
0060: import org.apache.commons.scxml.model.OnEntry;
0061: import org.apache.commons.scxml.model.OnExit;
0062: import org.apache.commons.scxml.model.Parallel;
0063: import org.apache.commons.scxml.model.Param;
0064: import org.apache.commons.scxml.model.PathResolverHolder;
0065: import org.apache.commons.scxml.model.SCXML;
0066: import org.apache.commons.scxml.model.Send;
0067: import org.apache.commons.scxml.model.State;
0068: import org.apache.commons.scxml.model.Transition;
0069: import org.apache.commons.scxml.model.TransitionTarget;
0070: import org.apache.commons.scxml.model.Var;
0071:
0072: import org.w3c.dom.Element;
0073: import org.w3c.dom.Node;
0074: import org.w3c.dom.NodeList;
0075:
0076: import org.xml.sax.Attributes;
0077: import org.xml.sax.ErrorHandler;
0078: import org.xml.sax.InputSource;
0079: import org.xml.sax.SAXException;
0080:
0081: /**
0082: * <p>The SCXMLDigester provides the ability to digest a SCXML document into
0083: * the Java object model provided in the model package.</p>
0084: * <p>The SCXMLDigester can be used for:</p>
0085: * <ol>
0086: * <li>Digest a SCXML file into the Commons SCXML Java object model.</li>
0087: * <li>Obtain a SCXML Digester for further customization of the default
0088: * ruleset.</li>
0089: * </ol>
0090: */
0091: public final class SCXMLDigester {
0092:
0093: /**
0094: * The SCXML namespace that this Digester is built for. Any document
0095: * that is intended to be parsed by this digester <b>must</b>
0096: * bind the SCXML elements to this namespace.
0097: */
0098: private static final String NAMESPACE_SCXML = "http://www.w3.org/2005/07/scxml";
0099:
0100: //---------------------- PUBLIC METHODS ----------------------//
0101: /**
0102: * <p>API for standalone usage where the SCXML document is a URL.</p>
0103: *
0104: * @param scxmlURL
0105: * a canonical absolute URL to parse (relative URLs within the
0106: * top level document are to be resovled against this URL).
0107: * @param errHandler
0108: * The SAX ErrorHandler
0109: *
0110: * @return SCXML The SCXML object corresponding to the file argument
0111: *
0112: * @throws IOException Underlying Digester parsing threw an IOException
0113: * @throws SAXException Underlying Digester parsing threw a SAXException
0114: * @throws ModelException If the resulting document model has flaws
0115: *
0116: * @see ErrorHandler
0117: * @see PathResolver
0118: */
0119: public static SCXML digest(final URL scxmlURL,
0120: final ErrorHandler errHandler) throws IOException,
0121: SAXException, ModelException {
0122:
0123: if (scxmlURL == null) {
0124: throw new IllegalArgumentException(ERR_NULL_URL);
0125: }
0126:
0127: return digest(scxmlURL, errHandler, null);
0128:
0129: }
0130:
0131: /**
0132: * <p>API for standalone usage where the SCXML document is a URI.
0133: * A PathResolver must be provided.</p>
0134: *
0135: * @param pathResolver
0136: * The PathResolver for this context
0137: * @param documentRealPath
0138: * The String pointing to the absolute (real) path of the
0139: * SCXML document
0140: * @param errHandler
0141: * The SAX ErrorHandler
0142: *
0143: * @return SCXML The SCXML object corresponding to the file argument
0144: *
0145: * @throws IOException Underlying Digester parsing threw an IOException
0146: * @throws SAXException Underlying Digester parsing threw a SAXException
0147: * @throws ModelException If the resulting document model has flaws
0148: *
0149: * @see ErrorHandler
0150: * @see PathResolver
0151: */
0152: public static SCXML digest(final String documentRealPath,
0153: final ErrorHandler errHandler,
0154: final PathResolver pathResolver) throws IOException,
0155: SAXException, ModelException {
0156:
0157: return digest(documentRealPath, errHandler, pathResolver, null);
0158:
0159: }
0160:
0161: /**
0162: * <p>API for standalone usage where the SCXML document is an
0163: * InputSource. This method may be used when the SCXML document is
0164: * packaged in a Java archive, or part of a compound document
0165: * where the SCXML root is available as a
0166: * <code>org.w3c.dom.Element</code> or via a <code>java.io.Reader</code>.
0167: * </p>
0168: *
0169: * <p><em>Note:</em> Since there is no path resolution, the SCXML document
0170: * must not have external state sources.</p>
0171: *
0172: * @param documentInputSource
0173: * The InputSource for the SCXML document
0174: * @param errHandler
0175: * The SAX ErrorHandler
0176: *
0177: * @return SCXML The SCXML object corresponding to the file argument
0178: *
0179: * @throws IOException Underlying Digester parsing threw an IOException
0180: * @throws SAXException Underlying Digester parsing threw a SAXException
0181: * @throws ModelException If the resulting document model has flaws
0182: *
0183: * @see ErrorHandler
0184: */
0185: public static SCXML digest(final InputSource documentInputSource,
0186: final ErrorHandler errHandler) throws IOException,
0187: SAXException, ModelException {
0188:
0189: if (documentInputSource == null) {
0190: throw new IllegalArgumentException(ERR_NULL_ISRC);
0191: }
0192:
0193: return digest(documentInputSource, errHandler, null);
0194:
0195: }
0196:
0197: /**
0198: * <p>API for standalone usage where the SCXML document is a URL, and
0199: * the document uses custom actions.</p>
0200: *
0201: * @param scxmlURL
0202: * a canonical absolute URL to parse (relative URLs within the
0203: * top level document are to be resovled against this URL).
0204: * @param errHandler
0205: * The SAX ErrorHandler
0206: * @param customActions
0207: * The list of {@link CustomAction}s this digester
0208: * instance will process, can be null or empty
0209: *
0210: * @return SCXML The SCXML object corresponding to the file argument
0211: *
0212: * @throws IOException Underlying Digester parsing threw an IOException
0213: * @throws SAXException Underlying Digester parsing threw a SAXException
0214: * @throws ModelException If the resulting document model has flaws
0215: *
0216: * @see ErrorHandler
0217: * @see PathResolver
0218: */
0219: public static SCXML digest(final URL scxmlURL,
0220: final ErrorHandler errHandler, final List customActions)
0221: throws IOException, SAXException, ModelException {
0222:
0223: SCXML scxml = null;
0224: Digester scxmlDigester = SCXMLDigester.newInstance(null,
0225: new URLResolver(scxmlURL), customActions);
0226: scxmlDigester.setErrorHandler(errHandler);
0227:
0228: try {
0229: scxml = (SCXML) scxmlDigester.parse(scxmlURL.toString());
0230: } catch (RuntimeException rte) {
0231: // Intercept runtime exceptions, only to log them with a
0232: // sensible error message about failure in document parsing
0233: MessageFormat msgFormat = new MessageFormat(
0234: ERR_DOC_PARSE_FAIL);
0235: String errMsg = msgFormat.format(new Object[] {
0236: String.valueOf(scxmlURL), rte.getMessage() });
0237: org.apache.commons.logging.Log log = LogFactory
0238: .getLog(SCXMLDigester.class);
0239: log.error(errMsg, rte);
0240: throw rte;
0241: }
0242:
0243: if (scxml != null) {
0244: ModelUpdater.updateSCXML(scxml);
0245: }
0246:
0247: return scxml;
0248:
0249: }
0250:
0251: /**
0252: * <p>API for standalone usage where the SCXML document is a URI.
0253: * A PathResolver must be provided.</p>
0254: *
0255: * @param pathResolver
0256: * The PathResolver for this context
0257: * @param documentRealPath
0258: * The String pointing to the absolute (real) path of the
0259: * SCXML document
0260: * @param errHandler
0261: * The SAX ErrorHandler
0262: * @param customActions
0263: * The list of {@link CustomAction}s this digester
0264: * instance will process, can be null or empty
0265: *
0266: * @return SCXML The SCXML object corresponding to the file argument
0267: *
0268: * @throws IOException Underlying Digester parsing threw an IOException
0269: * @throws SAXException Underlying Digester parsing threw a SAXException
0270: * @throws ModelException If the resulting document model has flaws
0271: *
0272: * @see ErrorHandler
0273: * @see PathResolver
0274: */
0275: public static SCXML digest(final String documentRealPath,
0276: final ErrorHandler errHandler,
0277: final PathResolver pathResolver, final List customActions)
0278: throws IOException, SAXException, ModelException {
0279:
0280: if (documentRealPath == null) {
0281: throw new IllegalArgumentException(ERR_NULL_PATH);
0282: }
0283:
0284: SCXML scxml = null;
0285: Digester scxmlDigester = SCXMLDigester.newInstance(null,
0286: pathResolver, customActions);
0287: scxmlDigester.setErrorHandler(errHandler);
0288:
0289: try {
0290: scxml = (SCXML) scxmlDigester.parse(documentRealPath);
0291: } catch (RuntimeException rte) {
0292: // Intercept runtime exceptions, only to log them with a
0293: // sensible error message about failure in document parsing
0294: MessageFormat msgFormat = new MessageFormat(
0295: ERR_DOC_PARSE_FAIL);
0296: String errMsg = msgFormat.format(new Object[] {
0297: documentRealPath, rte.getMessage() });
0298: org.apache.commons.logging.Log log = LogFactory
0299: .getLog(SCXMLDigester.class);
0300: log.error(errMsg, rte);
0301: throw rte;
0302: }
0303:
0304: if (scxml != null) {
0305: ModelUpdater.updateSCXML(scxml);
0306: }
0307:
0308: return scxml;
0309:
0310: }
0311:
0312: /**
0313: * <p>API for standalone usage where the SCXML document is an
0314: * InputSource. This method may be used when the SCXML document is
0315: * packaged in a Java archive, or part of a compound document
0316: * where the SCXML root is available as a
0317: * <code>org.w3c.dom.Element</code> or via a <code>java.io.Reader</code>.
0318: * </p>
0319: *
0320: * <p><em>Note:</em> Since there is no path resolution, the SCXML document
0321: * must not have external state sources.</p>
0322: *
0323: * @param documentInputSource
0324: * The InputSource for the SCXML document
0325: * @param errHandler
0326: * The SAX ErrorHandler
0327: * @param customActions
0328: * The list of {@link CustomAction}s this digester
0329: * instance will process, can be null or empty
0330: *
0331: * @return SCXML The SCXML object corresponding to the file argument
0332: *
0333: * @throws IOException Underlying Digester parsing threw an IOException
0334: * @throws SAXException Underlying Digester parsing threw a SAXException
0335: * @throws ModelException If the resulting document model has flaws
0336: *
0337: * @see ErrorHandler
0338: */
0339: public static SCXML digest(final InputSource documentInputSource,
0340: final ErrorHandler errHandler, final List customActions)
0341: throws IOException, SAXException, ModelException {
0342:
0343: Digester scxmlDigester = SCXMLDigester.newInstance(null, null,
0344: customActions);
0345: scxmlDigester.setErrorHandler(errHandler);
0346:
0347: SCXML scxml = null;
0348: try {
0349: scxml = (SCXML) scxmlDigester.parse(documentInputSource);
0350: } catch (RuntimeException rte) {
0351: // Intercept runtime exceptions, only to log them with a
0352: // sensible error message about failure in document parsing
0353: org.apache.commons.logging.Log log = LogFactory
0354: .getLog(SCXMLDigester.class);
0355: log.error(ERR_ISRC_PARSE_FAIL, rte);
0356: throw rte;
0357: }
0358:
0359: if (scxml != null) {
0360: ModelUpdater.updateSCXML(scxml);
0361: }
0362:
0363: return scxml;
0364:
0365: }
0366:
0367: /**
0368: * <p>Obtain a SCXML digester instance for further customization.</p>
0369: * <b>API Notes:</b>
0370: * <ul>
0371: * <li>Use the digest() convenience methods if you do not
0372: * need a custom digester.</li>
0373: * <li>After the SCXML document is parsed by the customized digester,
0374: * the object model <b>must</b> be made executor-ready by calling
0375: * <code>updateSCXML(SCXML)</code> method in this class.</li>
0376: * </ul>
0377: *
0378: * @return Digester A newly configured SCXML digester instance
0379: *
0380: * @see SCXMLDigester#updateSCXML(SCXML)
0381: */
0382: public static Digester newInstance() {
0383:
0384: return newInstance(null, null, null);
0385:
0386: }
0387:
0388: /**
0389: * <p>Obtain a SCXML digester instance for further customization.</p>
0390: * <b>API Notes:</b>
0391: * <ul>
0392: * <li>Use the digest() convenience methods if you do not
0393: * need a custom digester.</li>
0394: * <li>After the SCXML document is parsed by the customized digester,
0395: * the object model <b>must</b> be made executor-ready by calling
0396: * <code>updateSCXML(SCXML)</code> method in this class.</li>
0397: * </ul>
0398: *
0399: * @param pr The PathResolver, may be null for standalone documents
0400: * @return Digester A newly configured SCXML digester instance
0401: *
0402: * @see SCXMLDigester#updateSCXML(SCXML)
0403: */
0404: public static Digester newInstance(final PathResolver pr) {
0405:
0406: return newInstance(null, pr, null);
0407:
0408: }
0409:
0410: /**
0411: * <p>Obtain a SCXML digester instance for further customization.</p>
0412: * <b>API Notes:</b>
0413: * <ul>
0414: * <li>Use the digest() convenience methods if you do not
0415: * need a custom digester.</li>
0416: * <li>After the SCXML document is parsed by the customized digester,
0417: * the object model <b>must</b> be made executor-ready by calling
0418: * <code>updateSCXML(SCXML)</code> method in this class.</li>
0419: * </ul>
0420: *
0421: * @param scxml The parent SCXML document if there is one (in case of
0422: * state templates for example), null otherwise
0423: * @param pr The PathResolver, may be null for standalone documents
0424: * @return Digester A newly configured SCXML digester instance
0425: *
0426: * @see SCXMLDigester#updateSCXML(SCXML)
0427: */
0428: public static Digester newInstance(final SCXML scxml,
0429: final PathResolver pr) {
0430:
0431: return newInstance(scxml, pr, null);
0432:
0433: }
0434:
0435: /**
0436: * <p>Obtain a SCXML digester instance for further customization.</p>
0437: * <b>API Notes:</b>
0438: * <ul>
0439: * <li>Use the digest() convenience methods if you do not
0440: * need a custom digester.</li>
0441: * <li>After the SCXML document is parsed by the customized digester,
0442: * the object model <b>must</b> be made executor-ready by calling
0443: * <code>updateSCXML(SCXML)</code> method in this class.</li>
0444: * </ul>
0445: *
0446: * @param scxml The parent SCXML document if there is one (in case of
0447: * state templates for example), null otherwise
0448: * @param pr The PathResolver, may be null for standalone documents
0449: * @param customActions The list of {@link CustomAction}s this digester
0450: * instance will process, can be null or empty
0451: * @return Digester A newly configured SCXML digester instance
0452: *
0453: * @see SCXMLDigester#updateSCXML(SCXML)
0454: */
0455: public static Digester newInstance(final SCXML scxml,
0456: final PathResolver pr, final List customActions) {
0457:
0458: Digester digester = new Digester();
0459: digester.setNamespaceAware(true);
0460: //Uncomment next line after SCXML DTD is available
0461: //digester.setValidating(true);
0462: digester.setRules(initRules(scxml, pr, customActions));
0463: return digester;
0464: }
0465:
0466: /**
0467: * <p>Update the SCXML object model and make it SCXMLExecutor ready.
0468: * This is part of post-digester processing, and sets up the necessary
0469: * object references throughtout the SCXML object model for the parsed
0470: * document. Should be used only if a customized digester obtained
0471: * using the <code>newInstance()</code> methods is needed.</p>
0472: *
0473: * @param scxml The SCXML object (output from Digester)
0474: * @throws ModelException If the document model has flaws
0475: */
0476: public static void updateSCXML(final SCXML scxml)
0477: throws ModelException {
0478: ModelUpdater.updateSCXML(scxml);
0479: }
0480:
0481: //---------------------- PRIVATE CONSTANTS ----------------------//
0482: //// Patterns to get the digestion going, prefixed by XP_
0483: /** Root <scxml> element. */
0484: private static final String XP_SM = "scxml";
0485:
0486: /** <state> children of root <scxml> element. */
0487: private static final String XP_SM_ST = "scxml/state";
0488:
0489: //// Universal matches, prefixed by XPU_
0490: // State
0491: /** <state> children of <state> elements. */
0492: private static final String XPU_ST_ST = "!*/state/state";
0493:
0494: /** <state> children of <parallel> elements. */
0495: private static final String XPU_PAR_ST = "!*/parallel/state";
0496:
0497: /** <state> children of transition <target> elements. */
0498: private static final String XPU_TR_TAR_ST = "!*/transition/target/state";
0499:
0500: //private static final String XPU_ST_TAR_ST = "!*/state/target/state";
0501:
0502: // Parallel
0503: /** <parallel> child of <state> elements. */
0504: private static final String XPU_ST_PAR = "!*/state/parallel";
0505:
0506: // If
0507: /** <if> element. */
0508: private static final String XPU_IF = "!*/if";
0509:
0510: // Executables, next three patterns useful when adding custom actions
0511: /** <onentry> element. */
0512: private static final String XPU_ONEN = "!*/onentry";
0513:
0514: /** <onexit> element. */
0515: private static final String XPU_ONEX = "!*/onexit";
0516:
0517: /** <transition> element. */
0518: private static final String XPU_TR = "!*/transition";
0519:
0520: /** <finalize> element. */
0521: private static final String XPU_FIN = "!*/finalize";
0522:
0523: //// Path Fragments, constants prefixed by XPF_
0524: // Onentries and Onexits
0525: /** <onentry> child element. */
0526: private static final String XPF_ONEN = "/onentry";
0527:
0528: /** <onexit> child element. */
0529: private static final String XPF_ONEX = "/onexit";
0530:
0531: // Datamodel section
0532: /** <datamodel> child element. */
0533: private static final String XPF_DM = "/datamodel";
0534:
0535: /** Individual <data> elements. */
0536: private static final String XPF_DATA = "/data";
0537:
0538: // Initial
0539: /** <initial> child element. */
0540: private static final String XPF_INI = "/initial";
0541:
0542: // Invoke, param and finalize
0543: /** <invoke> child element of <state>. */
0544: private static final String XPF_INV = "/invoke";
0545:
0546: /** <param> child element of <invoke>. */
0547: private static final String XPF_PRM = "/param";
0548:
0549: /** <finalize> child element of <invoke>. */
0550: private static final String XPF_FIN = "/finalize";
0551:
0552: // History
0553: /** <history> child element. */
0554: private static final String XPF_HIST = "/history";
0555:
0556: // Transition, target and exit
0557: /** <transition> child element. */
0558: private static final String XPF_TR = "/transition";
0559:
0560: /** <target> child element. */
0561: private static final String XPF_TAR = "/target";
0562:
0563: /** <exit> child element. */
0564: private static final String XPF_EXT = "/exit";
0565:
0566: // Actions
0567: /** <var> child element. */
0568: private static final String XPF_VAR = "/var";
0569:
0570: /** <assign> child element. */
0571: private static final String XPF_ASN = "/assign";
0572:
0573: /** <log> child element. */
0574: private static final String XPF_LOG = "/log";
0575:
0576: /** <send> child element. */
0577: private static final String XPF_SND = "/send";
0578:
0579: /** <cancel> child element. */
0580: private static final String XPF_CAN = "/cancel";
0581:
0582: /** <elseif> child element. */
0583: private static final String XPF_EIF = "/elseif";
0584:
0585: /** <else> child element. */
0586: private static final String XPF_ELS = "/else";
0587:
0588: //// Other constants
0589: // Error messages
0590: /**
0591: * Null URL passed as argument.
0592: */
0593: private static final String ERR_NULL_URL = "Cannot parse null URL";
0594:
0595: /**
0596: * Null path passed as argument.
0597: */
0598: private static final String ERR_NULL_PATH = "Cannot parse null URL";
0599:
0600: /**
0601: * Null InputSource passed as argument.
0602: */
0603: private static final String ERR_NULL_ISRC = "Cannot parse null URL";
0604:
0605: /**
0606: * Parsing SCXML document has failed.
0607: */
0608: private static final String ERR_DOC_PARSE_FAIL = "Error parsing "
0609: + "SCXML document: \"{0}\", with message: \"{1}\"\n";
0610:
0611: /**
0612: * Parsing SCXML document InputSource has failed.
0613: */
0614: private static final String ERR_ISRC_PARSE_FAIL = "Could not parse SCXML InputSource";
0615:
0616: /**
0617: * Parser configuration error while registering data rule.
0618: */
0619: private static final String ERR_PARSER_CFG_DATA = "XML Parser "
0620: + "misconfiguration, error registering <data> element rule";
0621:
0622: /**
0623: * Parser configuration error while registering send rule.
0624: */
0625: private static final String ERR_PARSER_CFG_SEND = "XML Parser "
0626: + "misconfiguration, error registering <send> element rule";
0627:
0628: /**
0629: * Parser configuration error while registering body content rule for
0630: * custom action.
0631: */
0632: private static final String ERR_PARSER_CFG_CUSTOM = "XML Parser "
0633: + "misconfiguration, error registering custom action rules";
0634:
0635: /**
0636: * Error message while attempting to define a custom action which does
0637: * not extend the Commons SCXML Action base class.
0638: */
0639: private static final String ERR_CUSTOM_ACTION_TYPE = "Custom actions list"
0640: + " contained unknown object (not a Commons SCXML Action subtype)";
0641:
0642: // String constants
0643: /** Slash. */
0644: private static final String STR_SLASH = "/";
0645:
0646: //---------------------- PRIVATE UTILITY METHODS ----------------------//
0647: /*
0648: * Private utility functions for configuring digester rule base for SCXML.
0649: */
0650: /**
0651: * Initialize the Digester rules for the current document.
0652: *
0653: * @param scxml The parent SCXML document (or null)
0654: * @param pr The PathResolver
0655: * @param customActions The list of custom actions this digester needs
0656: * to be able to process
0657: *
0658: * @return scxmlRules The rule set to be used for digestion
0659: */
0660: private static ExtendedBaseRules initRules(final SCXML scxml,
0661: final PathResolver pr, final List customActions) {
0662:
0663: ExtendedBaseRules scxmlRules = new ExtendedBaseRules();
0664: scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
0665:
0666: //// SCXML
0667: scxmlRules.add(XP_SM, new ObjectCreateRule(SCXML.class));
0668: scxmlRules.add(XP_SM, new SetPropertiesRule());
0669:
0670: //// Datamodel at document root i.e. <scxml> datamodel
0671: addDatamodelRules(XP_SM + XPF_DM, scxmlRules, scxml, pr);
0672:
0673: //// States
0674: // Level one states
0675: addStateRules(XP_SM_ST, scxmlRules, customActions, scxml, pr, 0);
0676: scxmlRules.add(XP_SM_ST, new SetNextRule("addState"));
0677: // Nested states
0678: addStateRules(XPU_ST_ST, scxmlRules, customActions, scxml, pr,
0679: 1);
0680: scxmlRules.add(XPU_ST_ST, new SetNextRule("addChild"));
0681:
0682: // Parallel states
0683: addStateRules(XPU_PAR_ST, scxmlRules, customActions, scxml, pr,
0684: 1);
0685: scxmlRules.add(XPU_PAR_ST, new SetNextRule("addState"));
0686: // Target states
0687: addStateRules(XPU_TR_TAR_ST, scxmlRules, customActions, scxml,
0688: pr, 2);
0689: scxmlRules.add(XPU_TR_TAR_ST, new SetNextRule("setTarget"));
0690:
0691: //// Parallels
0692: addParallelRules(XPU_ST_PAR, scxmlRules, pr, customActions,
0693: scxml);
0694:
0695: //// Ifs
0696: addIfRules(XPU_IF, scxmlRules, pr, customActions);
0697:
0698: //// Custom actions
0699: addCustomActionRules(XPU_ONEN, scxmlRules, customActions);
0700: addCustomActionRules(XPU_ONEX, scxmlRules, customActions);
0701: addCustomActionRules(XPU_TR, scxmlRules, customActions);
0702: addCustomActionRules(XPU_IF, scxmlRules, customActions);
0703: addCustomActionRules(XPU_FIN, scxmlRules, customActions);
0704:
0705: return scxmlRules;
0706:
0707: }
0708:
0709: /**
0710: * Add Digester rules for all <state> elements.
0711: *
0712: * @param xp The Digester style XPath expression of the parent
0713: * XML element
0714: * @param scxmlRules The rule set to be used for digestion
0715: * @param customActions The list of custom actions this digester needs
0716: * to be able to process
0717: * @param scxml The parent SCXML document (or null)
0718: * @param pr The PathResolver
0719: * @param parent The distance between this state and its parent
0720: * state on the Digester stack
0721: */
0722: private static void addStateRules(final String xp,
0723: final ExtendedBaseRules scxmlRules,
0724: final List customActions, final SCXML scxml,
0725: final PathResolver pr, final int parent) {
0726: scxmlRules.add(xp, new ObjectCreateRule(State.class));
0727: addStatePropertiesRules(xp, scxmlRules, customActions, pr,
0728: scxml);
0729: addDatamodelRules(xp + XPF_DM, scxmlRules, scxml, pr);
0730: addInvokeRules(xp + XPF_INV, scxmlRules, customActions, pr,
0731: scxml);
0732: addInitialRules(xp + XPF_INI, scxmlRules, customActions, pr,
0733: scxml);
0734: addHistoryRules(xp + XPF_HIST, scxmlRules, customActions, pr,
0735: scxml);
0736: addParentRule(xp, scxmlRules, parent);
0737: addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition",
0738: pr, customActions);
0739: addHandlerRules(xp, scxmlRules, pr, customActions);
0740: scxmlRules.add(xp, new UpdateModelRule(scxml));
0741: }
0742:
0743: /**
0744: * Add Digester rules for all <parallel> elements.
0745: *
0746: * @param xp The Digester style XPath expression of the parent
0747: * XML element
0748: * @param scxmlRules The rule set to be used for digestion
0749: * @param customActions The list of custom actions this digester needs
0750: * to be able to process
0751: * @param pr The {@link PathResolver} for this document
0752: * @param scxml The parent SCXML document (or null)
0753: */
0754: private static void addParallelRules(final String xp,
0755: final ExtendedBaseRules scxmlRules, final PathResolver pr,
0756: final List customActions, final SCXML scxml) {
0757: addSimpleRulesTuple(xp, scxmlRules, Parallel.class, null, null,
0758: "setParallel");
0759: addHandlerRules(xp, scxmlRules, pr, customActions);
0760: addParentRule(xp, scxmlRules, 1);
0761: scxmlRules.add(xp, new UpdateModelRule(scxml));
0762: }
0763:
0764: /**
0765: * Add Digester rules for all <state> element attributes.
0766: *
0767: * @param xp The Digester style XPath expression of the parent
0768: * XML element
0769: * @param scxmlRules The rule set to be used for digestion
0770: * @param customActions The list of custom actions this digester needs
0771: * to be able to process
0772: * @param pr The PathResolver
0773: * @param scxml The root document, if this one is src'ed in
0774: */
0775: private static void addStatePropertiesRules(final String xp,
0776: final ExtendedBaseRules scxmlRules,
0777: final List customActions, final PathResolver pr,
0778: final SCXML scxml) {
0779: scxmlRules.add(xp, new SetPropertiesRule(new String[] { "id",
0780: "final" }, new String[] { "id", "isFinal" }));
0781: scxmlRules.add(xp, new DigestSrcAttributeRule(scxml,
0782: customActions, pr));
0783: }
0784:
0785: /**
0786: * Add Digester rules for all <datamodel> elements.
0787: *
0788: * @param xp The Digester style XPath expression of the parent
0789: * XML element
0790: * @param scxmlRules The rule set to be used for digestion
0791: * @param pr The PathResolver
0792: * @param scxml The parent SCXML document (or null)
0793: */
0794: private static void addDatamodelRules(final String xp,
0795: final ExtendedBaseRules scxmlRules, final SCXML scxml,
0796: final PathResolver pr) {
0797: scxmlRules.add(xp, new ObjectCreateRule(Datamodel.class));
0798: scxmlRules.add(xp + XPF_DATA, new ObjectCreateRule(Data.class));
0799: scxmlRules.add(xp + XPF_DATA, new SetPropertiesRule());
0800: scxmlRules.add(xp + XPF_DATA, new SetCurrentNamespacesRule());
0801: scxmlRules.add(xp + XPF_DATA, new SetNextRule("addData"));
0802: try {
0803: scxmlRules.add(xp + XPF_DATA, new ParseDataRule(pr));
0804: } catch (ParserConfigurationException pce) {
0805: org.apache.commons.logging.Log log = LogFactory
0806: .getLog(SCXMLDigester.class);
0807: log.error(ERR_PARSER_CFG_DATA, pce);
0808: }
0809: scxmlRules.add(xp, new SetNextRule("setDatamodel"));
0810: }
0811:
0812: /**
0813: * Add Digester rules for all <invoke> elements.
0814: *
0815: * @param xp The Digester style XPath expression of the parent
0816: * XML element
0817: * @param scxmlRules The rule set to be used for digestion
0818: * @param customActions The list of {@link CustomAction}s this digester
0819: * instance will process, can be null or empty
0820: * @param pr The PathResolver
0821: * @param scxml The parent SCXML document (or null)
0822: */
0823: private static void addInvokeRules(final String xp,
0824: final ExtendedBaseRules scxmlRules,
0825: final List customActions, final PathResolver pr,
0826: final SCXML scxml) {
0827: scxmlRules.add(xp, new ObjectCreateRule(Invoke.class));
0828: scxmlRules.add(xp, new SetPropertiesRule());
0829: scxmlRules.add(xp, new SetCurrentNamespacesRule());
0830: scxmlRules.add(xp, new SetPathResolverRule(pr));
0831: scxmlRules.add(xp + XPF_PRM, new ObjectCreateRule(Param.class));
0832: scxmlRules.add(xp + XPF_PRM, new SetPropertiesRule());
0833: scxmlRules.add(xp + XPF_PRM, new SetCurrentNamespacesRule());
0834: scxmlRules.add(xp + XPF_PRM, new SetNextRule("addParam"));
0835: scxmlRules.add(xp + XPF_FIN, new ObjectCreateRule(
0836: Finalize.class));
0837: scxmlRules.add(xp + XPF_FIN, new UpdateFinalizeRule());
0838: addActionRules(xp + XPF_FIN, scxmlRules, pr, customActions);
0839: scxmlRules.add(xp + XPF_FIN, new SetNextRule("setFinalize"));
0840: scxmlRules.add(xp, new SetNextRule("setInvoke"));
0841: }
0842:
0843: /**
0844: * Add Digester rules for all <initial> elements.
0845: *
0846: * @param xp The Digester style XPath expression of the parent
0847: * XML element
0848: * @param scxmlRules The rule set to be used for digestion
0849: * @param customActions The list of custom actions this digester needs
0850: * to be able to process
0851: * @param pr The PathResolver
0852: * @param scxml The parent SCXML document (or null)
0853: */
0854: private static void addInitialRules(final String xp,
0855: final ExtendedBaseRules scxmlRules,
0856: final List customActions, final PathResolver pr,
0857: final SCXML scxml) {
0858: scxmlRules.add(xp, new ObjectCreateRule(Initial.class));
0859: addPseudoStatePropertiesRules(xp, scxmlRules, customActions,
0860: pr, scxml);
0861: scxmlRules.add(xp, new UpdateModelRule(scxml));
0862: addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
0863: pr, customActions);
0864: scxmlRules.add(xp, new SetNextRule("setInitial"));
0865: }
0866:
0867: /**
0868: * Add Digester rules for all <history> elements.
0869: *
0870: * @param xp The Digester style XPath expression of the parent
0871: * XML element
0872: * @param scxmlRules The rule set to be used for digestion
0873: * @param customActions The list of custom actions this digester needs
0874: * to be able to process
0875: * @param pr The PathResolver
0876: * @param scxml The parent SCXML document (or null)
0877: */
0878: private static void addHistoryRules(final String xp,
0879: final ExtendedBaseRules scxmlRules,
0880: final List customActions, final PathResolver pr,
0881: final SCXML scxml) {
0882: scxmlRules.add(xp, new ObjectCreateRule(History.class));
0883: addPseudoStatePropertiesRules(xp, scxmlRules, customActions,
0884: pr, scxml);
0885: scxmlRules.add(xp, new UpdateModelRule(scxml));
0886: scxmlRules.add(xp, new SetPropertiesRule(
0887: new String[] { "type" }, new String[] { "type" }));
0888: addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
0889: pr, customActions);
0890: scxmlRules.add(xp, new SetNextRule("addHistory"));
0891: }
0892:
0893: /**
0894: * Add Digester rules for all pseudo state (initial, history) element
0895: * attributes.
0896: *
0897: * @param xp The Digester style XPath expression of the parent
0898: * XML element
0899: * @param scxmlRules The rule set to be used for digestion
0900: * @param customActions The list of custom actions this digester needs
0901: * to be able to process
0902: * @param pr The PathResolver
0903: * @param scxml The root document, if this one is src'ed in
0904: */
0905: private static void addPseudoStatePropertiesRules(final String xp,
0906: final ExtendedBaseRules scxmlRules,
0907: final List customActions, final PathResolver pr,
0908: final SCXML scxml) {
0909: scxmlRules.add(xp, new SetPropertiesRule(new String[] { "id" },
0910: new String[] { "id" }));
0911: scxmlRules.add(xp, new DigestSrcAttributeRule(scxml,
0912: customActions, pr));
0913: addParentRule(xp, scxmlRules, 1);
0914: }
0915:
0916: /**
0917: * Add Digester rule for all setting parent state.
0918: *
0919: * @param xp The Digester style XPath expression of the parent
0920: * XML element
0921: * @param scxmlRules The rule set to be used for digestion
0922: * @param parent The distance between this state and its parent
0923: * state on the Digester stack
0924: */
0925: private static void addParentRule(final String xp,
0926: final ExtendedBaseRules scxmlRules, final int parent) {
0927: if (parent < 1) {
0928: return;
0929: }
0930: scxmlRules.add(xp, new Rule() {
0931: // A generic version of setTopRule
0932: public void body(final String namespace, final String name,
0933: final String text) throws Exception {
0934: TransitionTarget t = (TransitionTarget) getDigester()
0935: .peek();
0936: TransitionTarget p = (TransitionTarget) getDigester()
0937: .peek(parent);
0938: // CHANGE - Moved parent property to TransitionTarget
0939: t.setParent(p);
0940: }
0941: });
0942: }
0943:
0944: /**
0945: * Add Digester rules for all <transition> elements.
0946: *
0947: * @param xp The Digester style XPath expression of the parent
0948: * XML element
0949: * @param scxmlRules The rule set to be used for digestion
0950: * @param setNextMethod The method name for adding this transition
0951: * to its parent (defined by the SCXML Java object model).
0952: * @param pr The {@link PathResolver} for this document
0953: * @param customActions The list of custom actions this digester needs
0954: * to be able to process
0955: */
0956: private static void addTransitionRules(final String xp,
0957: final ExtendedBaseRules scxmlRules,
0958: final String setNextMethod, final PathResolver pr,
0959: final List customActions) {
0960: scxmlRules.add(xp, new ObjectCreateRule(Transition.class));
0961: scxmlRules.add(xp, new SetPropertiesRule(new String[] {
0962: "event", "cond", "target" }, new String[] { "event",
0963: "cond", "next" }));
0964: scxmlRules.add(xp, new SetCurrentNamespacesRule());
0965: scxmlRules.add(xp + XPF_TAR, new SetPropertiesRule());
0966: addActionRules(xp, scxmlRules, pr, customActions);
0967: scxmlRules.add(xp + XPF_EXT, new Rule() {
0968: public void end(final String namespace, final String name) {
0969: Transition t = (Transition) getDigester().peek(1);
0970: State exitState = new State();
0971: exitState.setIsFinal(true);
0972: t.setTarget(exitState);
0973: }
0974: });
0975: scxmlRules.add(xp, new SetNextRule(setNextMethod));
0976: }
0977:
0978: /**
0979: * Add Digester rules for all <onentry> and <onexit>
0980: * elements.
0981: *
0982: * @param xp The Digester style XPath expression of the parent
0983: * XML element
0984: * @param scxmlRules The rule set to be used for digestion
0985: * @param pr The {@link PathResolver} for this document
0986: * @param customActions The list of custom actions this digester needs
0987: * to be able to process
0988: */
0989: private static void addHandlerRules(final String xp,
0990: final ExtendedBaseRules scxmlRules, final PathResolver pr,
0991: final List customActions) {
0992: scxmlRules.add(xp + XPF_ONEN, new ObjectCreateRule(
0993: OnEntry.class));
0994: addActionRules(xp + XPF_ONEN, scxmlRules, pr, customActions);
0995: scxmlRules.add(xp + XPF_ONEN, new SetNextRule("setOnEntry"));
0996: scxmlRules.add(xp + XPF_ONEX,
0997: new ObjectCreateRule(OnExit.class));
0998: addActionRules(xp + XPF_ONEX, scxmlRules, pr, customActions);
0999: scxmlRules.add(xp + XPF_ONEX, new SetNextRule("setOnExit"));
1000: }
1001:
1002: /**
1003: * Add Digester rules for all actions ("executable" elements).
1004: *
1005: * @param xp The Digester style XPath expression of the parent
1006: * XML element
1007: * @param scxmlRules The rule set to be used for digestion
1008: * @param pr The {@link PathResolver} for this document
1009: * @param customActions The list of custom actions this digester needs
1010: * to be able to process
1011: */
1012: private static void addActionRules(final String xp,
1013: final ExtendedBaseRules scxmlRules, final PathResolver pr,
1014: final List customActions) {
1015: addActionRulesTuple(xp + XPF_ASN, scxmlRules, Assign.class);
1016: scxmlRules.add(xp + XPF_ASN, new SetPathResolverRule(pr));
1017: addActionRulesTuple(xp + XPF_VAR, scxmlRules, Var.class);
1018: addActionRulesTuple(xp + XPF_LOG, scxmlRules, Log.class);
1019: addSendRulesTuple(xp + XPF_SND, scxmlRules);
1020: addActionRulesTuple(xp + XPF_CAN, scxmlRules, Cancel.class);
1021: addActionRulesTuple(xp + XPF_EXT, scxmlRules, Exit.class);
1022: //addCustomActionRules(xp, scxmlRules, customActions);
1023: }
1024:
1025: /**
1026: * Add custom action rules, if any custom actions are provided.
1027: *
1028: * @param xp The Digester style XPath expression of the parent
1029: * XML element
1030: * @param scxmlRules The rule set to be used for digestion
1031: * @param customActions The list of custom actions this digester needs
1032: * to be able to process
1033: */
1034: private static void addCustomActionRules(final String xp,
1035: final ExtendedBaseRules scxmlRules, final List customActions) {
1036: if (customActions == null || customActions.size() == 0) {
1037: return;
1038: }
1039: for (int i = 0; i < customActions.size(); i++) {
1040: Object item = customActions.get(i);
1041: if (item == null || !(item instanceof CustomAction)) {
1042: org.apache.commons.logging.Log log = LogFactory
1043: .getLog(SCXMLDigester.class);
1044: log.warn(ERR_CUSTOM_ACTION_TYPE);
1045: } else {
1046: CustomAction ca = (CustomAction) item;
1047: scxmlRules.setNamespaceURI(ca.getNamespaceURI());
1048: String xpfLocalName = STR_SLASH + ca.getLocalName();
1049: Class klass = ca.getActionClass();
1050: if (SCXMLHelper.implementationOf(klass,
1051: ExternalContent.class)) {
1052: addCustomActionRulesTuple(xp + xpfLocalName,
1053: scxmlRules, klass, true);
1054: } else {
1055: addCustomActionRulesTuple(xp + xpfLocalName,
1056: scxmlRules, klass, false);
1057: }
1058: }
1059: }
1060: scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
1061: }
1062:
1063: /**
1064: * Add Digester rules that are specific to the <send> action
1065: * element.
1066: *
1067: * @param xp The Digester style XPath expression of <send> element
1068: * @param scxmlRules The rule set to be used for digestion
1069: */
1070: private static void addSendRulesTuple(final String xp,
1071: final ExtendedBaseRules scxmlRules) {
1072: addActionRulesTuple(xp, scxmlRules, Send.class);
1073: try {
1074: scxmlRules.add(xp, new ParseExternalContentRule());
1075: } catch (ParserConfigurationException pce) {
1076: org.apache.commons.logging.Log log = LogFactory
1077: .getLog(SCXMLDigester.class);
1078: log.error(ERR_PARSER_CFG_SEND, pce);
1079: }
1080: }
1081:
1082: /**
1083: * Add Digester rules for a simple custom action (no body content).
1084: *
1085: * @param xp The path to the custom action element
1086: * @param scxmlRules The rule set to be used for digestion
1087: * @param klass The <code>Action</code> class implementing the custom
1088: * action.
1089: * @param bodyContent Whether the custom rule has body content
1090: * that should be parsed using
1091: * <code>NodeCreateRule</code>
1092: */
1093: private static void addCustomActionRulesTuple(final String xp,
1094: final ExtendedBaseRules scxmlRules, final Class klass,
1095: final boolean bodyContent) {
1096: addActionRulesTuple(xp, scxmlRules, klass);
1097: if (bodyContent) {
1098: try {
1099: scxmlRules.add(xp, new ParseExternalContentRule());
1100: } catch (ParserConfigurationException pce) {
1101: org.apache.commons.logging.Log log = LogFactory
1102: .getLog(SCXMLDigester.class);
1103: log.error(ERR_PARSER_CFG_CUSTOM, pce);
1104: }
1105: }
1106: }
1107:
1108: /**
1109: * Add Digester rules for all <if> elements.
1110: *
1111: * @param xp The Digester style XPath expression of the parent
1112: * XML element
1113: * @param scxmlRules The rule set to be used for digestion
1114: * @param pr The {@link PathResolver} for this document
1115: * @param customActions The list of custom actions this digester needs
1116: * to be able to process
1117: */
1118: private static void addIfRules(final String xp,
1119: final ExtendedBaseRules scxmlRules, final PathResolver pr,
1120: final List customActions) {
1121: addActionRulesTuple(xp, scxmlRules, If.class);
1122: addActionRules(xp, scxmlRules, pr, customActions);
1123: addActionRulesTuple(xp + XPF_EIF, scxmlRules, ElseIf.class);
1124: addActionRulesTuple(xp + XPF_ELS, scxmlRules, Else.class);
1125: }
1126:
1127: /**
1128: * Add Digester rules that are common across all actions elements.
1129: *
1130: * @param xp The Digester style XPath expression of the parent
1131: * XML element
1132: * @param scxmlRules The rule set to be used for digestion
1133: * @param klass The class in the Java object model to be instantiated
1134: * in the ObjectCreateRule for this action
1135: */
1136: private static void addActionRulesTuple(final String xp,
1137: final ExtendedBaseRules scxmlRules, final Class klass) {
1138: addSimpleRulesTuple(xp, scxmlRules, klass, null, null,
1139: "addAction");
1140: scxmlRules.add(xp, new SetExecutableParentRule());
1141: scxmlRules.add(xp, new SetCurrentNamespacesRule());
1142: }
1143:
1144: /**
1145: * Add the run of the mill Digester rules for any element.
1146: *
1147: * @param xp The Digester style XPath expression of the parent
1148: * XML element
1149: * @param scxmlRules The rule set to be used for digestion
1150: * @param klass The class in the Java object model to be instantiated
1151: * in the ObjectCreateRule for this action
1152: * @param args The attributes to be mapped into the object model
1153: * @param props The properties that args get mapped to
1154: * @param addMethod The method that the SetNextRule should call
1155: */
1156: private static void addSimpleRulesTuple(final String xp,
1157: final ExtendedBaseRules scxmlRules, final Class klass,
1158: final String[] args, final String[] props,
1159: final String addMethod) {
1160: scxmlRules.add(xp, new ObjectCreateRule(klass));
1161: if (args == null) {
1162: scxmlRules.add(xp, new SetPropertiesRule());
1163: } else {
1164: scxmlRules.add(xp, new SetPropertiesRule(args, props));
1165: }
1166: scxmlRules.add(xp, new SetNextRule(addMethod));
1167: }
1168:
1169: /**
1170: * Discourage instantiation since this is a utility class.
1171: */
1172: private SCXMLDigester() {
1173: super ();
1174: }
1175:
1176: /**
1177: * Custom digestion rule for establishing necessary associations of this
1178: * TransitionTarget with the root SCXML object.
1179: * These include: <br>
1180: * 1) Updation of the SCXML object's global targets Map <br>
1181: * 2) Obtaining a handle to the SCXML object's NotificationRegistry <br>
1182: *
1183: * @deprecated Will be removed in version 1.0
1184: */
1185: public static class UpdateModelRule extends Rule {
1186:
1187: /**
1188: * The root SCXML object.
1189: */
1190: private SCXML scxml;
1191:
1192: /**
1193: * Constructor.
1194: * @param scxml The root SCXML object
1195: */
1196: public UpdateModelRule(final SCXML scxml) {
1197: super ();
1198: this .scxml = scxml;
1199: }
1200:
1201: /**
1202: * @see Rule#end(String, String)
1203: */
1204: public final void end(final String namespace, final String name) {
1205: if (scxml == null) {
1206: scxml = (SCXML) getDigester().peek(
1207: getDigester().getCount() - 1);
1208: }
1209: TransitionTarget tt = (TransitionTarget) getDigester()
1210: .peek();
1211: scxml.addTarget(tt);
1212: }
1213: }
1214:
1215: /**
1216: * Custom digestion rule for setting Executable parent of Action elements.
1217: *
1218: * @deprecated Will be removed in version 1.0
1219: */
1220: public static class SetExecutableParentRule extends Rule {
1221:
1222: /**
1223: * Constructor.
1224: */
1225: public SetExecutableParentRule() {
1226: super ();
1227: }
1228:
1229: /**
1230: * @see Rule#end(String, String)
1231: */
1232: public final void end(final String namespace, final String name) {
1233: Action child = (Action) getDigester().peek();
1234: for (int i = 1; i < getDigester().getCount() - 1; i++) {
1235: Object ancestor = getDigester().peek(i);
1236: if (ancestor instanceof Executable) {
1237: child.setParent((Executable) ancestor);
1238: return;
1239: }
1240: }
1241: }
1242: }
1243:
1244: /**
1245: * Custom digestion rule for parsing bodies of
1246: * <code>ExternalContent</code> elements.
1247: *
1248: * @see ExternalContent
1249: *
1250: * @deprecated Will be removed in version 1.0
1251: */
1252: public static class ParseExternalContentRule extends NodeCreateRule {
1253: /**
1254: * Constructor.
1255: * @throws ParserConfigurationException A JAXP configuration error
1256: */
1257: public ParseExternalContentRule()
1258: throws ParserConfigurationException {
1259: super ();
1260: }
1261:
1262: /**
1263: * @see Rule#end(String, String)
1264: */
1265: public final void end(final String namespace, final String name) {
1266: Element bodyElement = (Element) getDigester().pop();
1267: NodeList childNodes = bodyElement.getChildNodes();
1268: List externalNodes = ((ExternalContent) getDigester()
1269: .peek()).getExternalNodes();
1270: for (int i = 0; i < childNodes.getLength(); i++) {
1271: externalNodes.add(childNodes.item(i));
1272: }
1273: }
1274: }
1275:
1276: /**
1277: * Custom digestion rule for parsing bodies of <data> elements.
1278: *
1279: * @deprecated Will be removed in version 1.0
1280: */
1281: public static class ParseDataRule extends NodeCreateRule {
1282:
1283: /**
1284: * The PathResolver used to resolve the src attribute to the
1285: * SCXML document it points to.
1286: * @see PathResolver
1287: */
1288: private PathResolver pr;
1289:
1290: /**
1291: * The "src" attribute, retained to check if body content is legal.
1292: */
1293: private String src;
1294:
1295: /**
1296: * The "expr" attribute, retained to check if body content is legal.
1297: */
1298: private String expr;
1299:
1300: /**
1301: * The XML tree for this data, parse as a Node, obtained from
1302: * either the "src" or the "expr" attributes.
1303: */
1304: private Node attrNode;
1305:
1306: /**
1307: * Constructor.
1308: *
1309: * @param pr The <code>PathResolver</code>
1310: * @throws ParserConfigurationException A JAXP configuration error
1311: */
1312: public ParseDataRule(final PathResolver pr)
1313: throws ParserConfigurationException {
1314: super ();
1315: this .pr = pr;
1316: }
1317:
1318: /**
1319: * @see Rule#begin(String, String, Attributes)
1320: */
1321: public final void begin(final String namespace,
1322: final String name, final Attributes attributes)
1323: throws Exception {
1324: super .begin(namespace, name, attributes);
1325: src = attributes.getValue("src");
1326: expr = attributes.getValue("expr");
1327: if (!SCXMLHelper.isStringEmpty(src)) {
1328: String path = null;
1329: if (pr == null) {
1330: path = src;
1331: } else {
1332: path = pr.resolvePath(src);
1333: }
1334: try {
1335: DocumentBuilderFactory dbFactory = DocumentBuilderFactory
1336: .newInstance();
1337: DocumentBuilder db = dbFactory.newDocumentBuilder();
1338: attrNode = db.parse(path);
1339: } catch (Throwable t) { // you read that correctly
1340: org.apache.commons.logging.Log log = LogFactory
1341: .getLog(SCXMLDigester.class);
1342: log.error(t.getMessage(), t);
1343: }
1344: return;
1345: }
1346: }
1347:
1348: /**
1349: * @see Rule#end(String, String)
1350: */
1351: public final void end(final String namespace, final String name) {
1352: Node bodyNode = (Node) getDigester().pop();
1353: Data data = ((Data) getDigester().peek());
1354: // Prefer "src" over "expr", "expr" over child nodes
1355: // "expr" can only be evaluated at execution time
1356: if (!SCXMLHelper.isStringEmpty(src)) {
1357: data.setNode(attrNode);
1358: } else if (SCXMLHelper.isStringEmpty(expr)) {
1359: // both "src" and "expr" are empty
1360: data.setNode(bodyNode);
1361: }
1362: }
1363: }
1364:
1365: /**
1366: * Custom digestion rule for external sources, that is, the src attribute of
1367: * the <state> element.
1368: *
1369: * @deprecated Will be removed in version 1.0
1370: */
1371: public static class DigestSrcAttributeRule extends Rule {
1372:
1373: /**
1374: * The PathResolver used to resolve the src attribute to the
1375: * SCXML document it points to.
1376: * @see PathResolver
1377: */
1378: private PathResolver pr;
1379:
1380: /**
1381: * The root document.
1382: */
1383: private SCXML root;
1384:
1385: /**
1386: * The list of custom actions the parent document is capable of
1387: * processing (and hence, the child should be, by transitivity).
1388: * @see CustomAction
1389: */
1390: private List customActions;
1391:
1392: /**
1393: * Constructor.
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: * TODO: Remove in v1.0
1402: */
1403: public DigestSrcAttributeRule(final List customActions,
1404: final PathResolver pr) {
1405: super ();
1406: this .customActions = customActions;
1407: this .pr = pr;
1408: }
1409:
1410: /**
1411: * Constructor.
1412: * @param root The root document, if this one is src'ed in
1413: * @param pr The PathResolver
1414: * @param customActions The list of custom actions this digester needs
1415: * to be able to process
1416: *
1417: * @see PathResolver
1418: * @see CustomAction
1419: */
1420: public DigestSrcAttributeRule(final SCXML root,
1421: final List customActions, final PathResolver pr) {
1422: super ();
1423: this .root = root;
1424: this .customActions = customActions;
1425: this .pr = pr;
1426: }
1427:
1428: /**
1429: * @see Rule#begin(String, String, Attributes)
1430: */
1431: public final void begin(final String namespace,
1432: final String name, final Attributes attributes) {
1433: String src = attributes.getValue("src");
1434: if (SCXMLHelper.isStringEmpty(src)) {
1435: return;
1436: }
1437: Digester digester = getDigester();
1438: SCXML scxml = (SCXML) digester
1439: .peek(digester.getCount() - 1);
1440: // 1) Digest the external SCXML file
1441: SCXML externalSCXML = null;
1442: String path;
1443: Digester externalSrcDigester;
1444: if (pr == null) {
1445: path = src;
1446: if (root != null) {
1447: externalSrcDigester = newInstance(root, null,
1448: customActions);
1449: } else {
1450: externalSrcDigester = newInstance(scxml, null,
1451: customActions);
1452: }
1453: } else {
1454: path = pr.resolvePath(src);
1455: if (root != null) {
1456: externalSrcDigester = newInstance(root, pr
1457: .getResolver(src), customActions);
1458: } else {
1459: externalSrcDigester = newInstance(scxml, pr
1460: .getResolver(src), customActions);
1461: }
1462: }
1463:
1464: try {
1465: externalSCXML = (SCXML) externalSrcDigester.parse(path);
1466: } catch (Exception e) {
1467: org.apache.commons.logging.Log log = LogFactory
1468: .getLog(SCXMLDigester.class);
1469: log.error(e.getMessage(), e);
1470: }
1471: // 2) Adopt the children and datamodel
1472: if (externalSCXML == null) {
1473: return;
1474: }
1475: State s = (State) digester.peek();
1476: Transition t = new Transition();
1477: t.setNext(externalSCXML.getInitialstate());
1478: Initial ini = new Initial();
1479: ini.setTransition(t);
1480: s.setInitial(ini);
1481: Map children = externalSCXML.getStates();
1482: Object[] ids = children.keySet().toArray();
1483: for (int i = 0; i < ids.length; i++) {
1484: s.addChild((State) children.get(ids[i]));
1485: }
1486: s.setDatamodel(externalSCXML.getDatamodel());
1487: }
1488: }
1489:
1490: /**
1491: * Custom digestion rule for setting PathResolver for runtime retrieval.
1492: *
1493: * @deprecated Will be removed in version 1.0
1494: */
1495: public static class SetPathResolverRule extends Rule {
1496:
1497: /**
1498: * The PathResolver to set.
1499: * @see PathResolver
1500: */
1501: private PathResolver pr;
1502:
1503: /**
1504: * Constructor.
1505: * @param pr The PathResolver
1506: *
1507: * @see PathResolver
1508: */
1509: public SetPathResolverRule(final PathResolver pr) {
1510: super ();
1511: this .pr = pr;
1512: }
1513:
1514: /**
1515: * @see Rule#begin(String, String, Attributes)
1516: */
1517: public final void begin(final String namespace,
1518: final String name, final Attributes attributes) {
1519: PathResolverHolder prHolder = (PathResolverHolder) getDigester()
1520: .peek();
1521: prHolder.setPathResolver(pr);
1522: }
1523: }
1524:
1525: /**
1526: * Custom digestion rule for setting state parent of finalize.
1527: *
1528: * @deprecated Will be removed in version 1.0
1529: */
1530: public static class UpdateFinalizeRule extends Rule {
1531:
1532: /**
1533: * @see Rule#begin(String, String, Attributes)
1534: */
1535: public final void begin(final String namespace,
1536: final String name, final Attributes attributes) {
1537: Finalize finalize = (Finalize) getDigester().peek();
1538: // state/invoke/finalize --> peek(2)
1539: TransitionTarget tt = (TransitionTarget) getDigester()
1540: .peek(2);
1541: finalize.setParent(tt);
1542: }
1543: }
1544:
1545: /**
1546: * Custom digestion rule for attaching a snapshot of current namespaces
1547: * to SCXML actions for deferred XPath evaluation.
1548: *
1549: */
1550: private static class SetCurrentNamespacesRule extends Rule {
1551:
1552: /**
1553: * @see Rule#begin(String, String, Attributes)
1554: */
1555: public final void begin(final String namespace,
1556: final String name, final Attributes attributes) {
1557: NamespacePrefixesHolder nsHolder = (NamespacePrefixesHolder) getDigester()
1558: .peek();
1559: nsHolder
1560: .setNamespaces(getDigester().getCurrentNamespaces());
1561: }
1562: }
1563:
1564: }
|