0001: /* $Id: Digester.java 467222 2006-10-24 03:17:11Z markt $
0002: *
0003: * Licensed to the Apache Software Foundation (ASF) under one or more
0004: * contributor license agreements. See the NOTICE file distributed with
0005: * this work for additional information regarding copyright ownership.
0006: * The ASF licenses this file to You under the Apache License, Version 2.0
0007: * (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing, software
0013: * distributed under the License is distributed on an "AS IS" BASIS,
0014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: * See the License for the specific language governing permissions and
0016: * limitations under the License.
0017: */
0018:
0019: package org.apache.tomcat.util.digester;
0020:
0021: import java.io.File;
0022: import java.io.FileInputStream;
0023: import java.io.IOException;
0024: import java.io.InputStream;
0025: import java.io.Reader;
0026: import java.lang.reflect.InvocationTargetException;
0027: import java.util.EmptyStackException;
0028: import java.util.HashMap;
0029: import java.util.Iterator;
0030: import java.util.List;
0031: import java.util.Map;
0032: import java.util.Properties;
0033:
0034: import javax.xml.parsers.ParserConfigurationException;
0035: import javax.xml.parsers.SAXParser;
0036: import javax.xml.parsers.SAXParserFactory;
0037:
0038: import org.apache.juli.logging.Log;
0039: import org.apache.juli.logging.LogFactory;
0040: import org.apache.tomcat.util.IntrospectionUtils;
0041: import org.xml.sax.Attributes;
0042: import org.xml.sax.EntityResolver;
0043: import org.xml.sax.ErrorHandler;
0044: import org.xml.sax.InputSource;
0045: import org.xml.sax.Locator;
0046: import org.xml.sax.SAXException;
0047: import org.xml.sax.SAXNotRecognizedException;
0048: import org.xml.sax.SAXNotSupportedException;
0049: import org.xml.sax.SAXParseException;
0050: import org.xml.sax.XMLReader;
0051: import org.xml.sax.helpers.AttributesImpl;
0052: import org.xml.sax.helpers.DefaultHandler;
0053:
0054: /**
0055: * <p>A <strong>Digester</strong> processes an XML input stream by matching a
0056: * series of element nesting patterns to execute Rules that have been added
0057: * prior to the start of parsing. This package was inspired by the
0058: * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
0059: * but is organized somewhat differently.</p>
0060: *
0061: * <p>See the <a href="package-summary.html#package_description">Digester
0062: * Developer Guide</a> for more information.</p>
0063: *
0064: * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
0065: * only be used within the context of a single thread at a time, and a call
0066: * to <code>parse()</code> must be completed before another can be initiated
0067: * even from the same thread.</p>
0068: *
0069: * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents
0070: * the support of XML schema. You need Xerces 2.1/2.3 and up to make
0071: * this class working with XML schema</p>
0072: */
0073:
0074: public class Digester extends DefaultHandler {
0075:
0076: // ---------------------------------------------------------- Static Fields
0077:
0078: private static class SystemPropertySource implements
0079: IntrospectionUtils.PropertySource {
0080: public String getProperty(String key) {
0081: return System.getProperty(key);
0082: }
0083: }
0084:
0085: protected static IntrospectionUtils.PropertySource source[] = new IntrospectionUtils.PropertySource[] { new SystemPropertySource() };
0086:
0087: // --------------------------------------------------------- Constructors
0088:
0089: /**
0090: * Construct a new Digester with default properties.
0091: */
0092: public Digester() {
0093:
0094: super ();
0095:
0096: }
0097:
0098: /**
0099: * Construct a new Digester, allowing a SAXParser to be passed in. This
0100: * allows Digester to be used in environments which are unfriendly to
0101: * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to
0102: * James House (james@interobjective.com). This may help in places where
0103: * you are able to load JAXP 1.1 classes yourself.
0104: */
0105: public Digester(SAXParser parser) {
0106:
0107: super ();
0108:
0109: this .parser = parser;
0110:
0111: }
0112:
0113: /**
0114: * Construct a new Digester, allowing an XMLReader to be passed in. This
0115: * allows Digester to be used in environments which are unfriendly to
0116: * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you
0117: * have to configure namespace and validation support yourself, as these
0118: * properties only affect the SAXParser and emtpy constructor.
0119: */
0120: public Digester(XMLReader reader) {
0121:
0122: super ();
0123:
0124: this .reader = reader;
0125:
0126: }
0127:
0128: // --------------------------------------------------- Instance Variables
0129:
0130: /**
0131: * The body text of the current element.
0132: */
0133: protected StringBuffer bodyText = new StringBuffer();
0134:
0135: /**
0136: * The stack of body text string buffers for surrounding elements.
0137: */
0138: protected ArrayStack bodyTexts = new ArrayStack();
0139:
0140: /**
0141: * Stack whose elements are List objects, each containing a list of
0142: * Rule objects as returned from Rules.getMatch(). As each xml element
0143: * in the input is entered, the matching rules are pushed onto this
0144: * stack. After the end tag is reached, the matches are popped again.
0145: * The depth of is stack is therefore exactly the same as the current
0146: * "nesting" level of the input xml.
0147: *
0148: * @since 1.6
0149: */
0150: protected ArrayStack matches = new ArrayStack(10);
0151:
0152: /**
0153: * The class loader to use for instantiating application objects.
0154: * If not specified, the context class loader, or the class loader
0155: * used to load Digester itself, is used, based on the value of the
0156: * <code>useContextClassLoader</code> variable.
0157: */
0158: protected ClassLoader classLoader = null;
0159:
0160: /**
0161: * Has this Digester been configured yet.
0162: */
0163: protected boolean configured = false;
0164:
0165: /**
0166: * The EntityResolver used by the SAX parser. By default it use this class
0167: */
0168: protected EntityResolver entityResolver;
0169:
0170: /**
0171: * The URLs of entityValidator that have been registered, keyed by the public
0172: * identifier that corresponds.
0173: */
0174: protected HashMap entityValidator = new HashMap();
0175:
0176: /**
0177: * The application-supplied error handler that is notified when parsing
0178: * warnings, errors, or fatal errors occur.
0179: */
0180: protected ErrorHandler errorHandler = null;
0181:
0182: /**
0183: * The SAXParserFactory that is created the first time we need it.
0184: */
0185: protected SAXParserFactory factory = null;
0186:
0187: /**
0188: * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
0189: */
0190: protected String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
0191:
0192: /**
0193: * The Locator associated with our parser.
0194: */
0195: protected Locator locator = null;
0196:
0197: /**
0198: * The current match pattern for nested element processing.
0199: */
0200: protected String match = "";
0201:
0202: /**
0203: * Do we want a "namespace aware" parser.
0204: */
0205: protected boolean namespaceAware = false;
0206:
0207: /**
0208: * Registered namespaces we are currently processing. The key is the
0209: * namespace prefix that was declared in the document. The value is an
0210: * ArrayStack of the namespace URIs this prefix has been mapped to --
0211: * the top Stack element is the most current one. (This architecture
0212: * is required because documents can declare nested uses of the same
0213: * prefix for different Namespace URIs).
0214: */
0215: protected HashMap namespaces = new HashMap();
0216:
0217: /**
0218: * The parameters stack being utilized by CallMethodRule and
0219: * CallParamRule rules.
0220: */
0221: protected ArrayStack params = new ArrayStack();
0222:
0223: /**
0224: * The SAXParser we will use to parse the input stream.
0225: */
0226: protected SAXParser parser = null;
0227:
0228: /**
0229: * The public identifier of the DTD we are currently parsing under
0230: * (if any).
0231: */
0232: protected String publicId = null;
0233:
0234: /**
0235: * The XMLReader used to parse digester rules.
0236: */
0237: protected XMLReader reader = null;
0238:
0239: /**
0240: * The "root" element of the stack (in other words, the last object
0241: * that was popped.
0242: */
0243: protected Object root = null;
0244:
0245: /**
0246: * The <code>Rules</code> implementation containing our collection of
0247: * <code>Rule</code> instances and associated matching policy. If not
0248: * established before the first rule is added, a default implementation
0249: * will be provided.
0250: */
0251: protected Rules rules = null;
0252:
0253: /**
0254: * The XML schema language to use for validating an XML instance. By
0255: * default this value is set to <code>W3C_XML_SCHEMA</code>
0256: */
0257: protected String schemaLanguage = W3C_XML_SCHEMA;
0258:
0259: /**
0260: * The XML schema to use for validating an XML instance.
0261: */
0262: protected String schemaLocation = null;
0263:
0264: /**
0265: * The object stack being constructed.
0266: */
0267: protected ArrayStack stack = new ArrayStack();
0268:
0269: /**
0270: * Do we want to use the Context ClassLoader when loading classes
0271: * for instantiating new objects. Default is <code>false</code>.
0272: */
0273: protected boolean useContextClassLoader = false;
0274:
0275: /**
0276: * Do we want to use a validating parser.
0277: */
0278: protected boolean validating = false;
0279:
0280: /**
0281: * The Log to which most logging calls will be made.
0282: */
0283: protected Log log = LogFactory
0284: .getLog("org.apache.commons.digester.Digester");
0285:
0286: /**
0287: * The Log to which all SAX event related logging calls will be made.
0288: */
0289: protected Log saxLog = LogFactory
0290: .getLog("org.apache.commons.digester.Digester.sax");
0291:
0292: /**
0293: * The schema language supported. By default, we use this one.
0294: */
0295: protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
0296:
0297: /** Stacks used for interrule communication, indexed by name String */
0298: private HashMap stacksByName = new HashMap();
0299:
0300: // ------------------------------------------------------------- Properties
0301:
0302: /**
0303: * Return the currently mapped namespace URI for the specified prefix,
0304: * if any; otherwise return <code>null</code>. These mappings come and
0305: * go dynamically as the document is parsed.
0306: *
0307: * @param prefix Prefix to look up
0308: */
0309: public String findNamespaceURI(String prefix) {
0310:
0311: ArrayStack stack = (ArrayStack) namespaces.get(prefix);
0312: if (stack == null) {
0313: return (null);
0314: }
0315: try {
0316: return ((String) stack.peek());
0317: } catch (EmptyStackException e) {
0318: return (null);
0319: }
0320:
0321: }
0322:
0323: /**
0324: * Return the class loader to be used for instantiating application objects
0325: * when required. This is determined based upon the following rules:
0326: * <ul>
0327: * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
0328: * <li>The thread context class loader, if it exists and the
0329: * <code>useContextClassLoader</code> property is set to true</li>
0330: * <li>The class loader used to load the Digester class itself.
0331: * </ul>
0332: */
0333: public ClassLoader getClassLoader() {
0334:
0335: if (this .classLoader != null) {
0336: return (this .classLoader);
0337: }
0338: if (this .useContextClassLoader) {
0339: ClassLoader classLoader = Thread.currentThread()
0340: .getContextClassLoader();
0341: if (classLoader != null) {
0342: return (classLoader);
0343: }
0344: }
0345: return (this .getClass().getClassLoader());
0346:
0347: }
0348:
0349: /**
0350: * Set the class loader to be used for instantiating application objects
0351: * when required.
0352: *
0353: * @param classLoader The new class loader to use, or <code>null</code>
0354: * to revert to the standard rules
0355: */
0356: public void setClassLoader(ClassLoader classLoader) {
0357:
0358: this .classLoader = classLoader;
0359:
0360: }
0361:
0362: /**
0363: * Return the current depth of the element stack.
0364: */
0365: public int getCount() {
0366:
0367: return (stack.size());
0368:
0369: }
0370:
0371: /**
0372: * Return the name of the XML element that is currently being processed.
0373: */
0374: public String getCurrentElementName() {
0375:
0376: String elementName = match;
0377: int lastSlash = elementName.lastIndexOf('/');
0378: if (lastSlash >= 0) {
0379: elementName = elementName.substring(lastSlash + 1);
0380: }
0381: return (elementName);
0382:
0383: }
0384:
0385: /**
0386: * Return the debugging detail level of our currently enabled logger.
0387: *
0388: * @deprecated This method now always returns 0. Digester uses the apache
0389: * jakarta commons-logging library; see the documentation for that library
0390: * for more information.
0391: */
0392: public int getDebug() {
0393:
0394: return (0);
0395:
0396: }
0397:
0398: /**
0399: * Set the debugging detail level of our currently enabled logger.
0400: *
0401: * @param debug New debugging detail level (0=off, increasing integers
0402: * for more detail)
0403: *
0404: * @deprecated This method now has no effect at all. Digester uses
0405: * the apache jakarta comons-logging library; see the documentation
0406: * for that library for more information.
0407: */
0408: public void setDebug(int debug) {
0409:
0410: ; // No action is taken
0411:
0412: }
0413:
0414: /**
0415: * Return the error handler for this Digester.
0416: */
0417: public ErrorHandler getErrorHandler() {
0418:
0419: return (this .errorHandler);
0420:
0421: }
0422:
0423: /**
0424: * Set the error handler for this Digester.
0425: *
0426: * @param errorHandler The new error handler
0427: */
0428: public void setErrorHandler(ErrorHandler errorHandler) {
0429:
0430: this .errorHandler = errorHandler;
0431:
0432: }
0433:
0434: /**
0435: * Return the SAXParserFactory we will use, creating one if necessary.
0436: */
0437: public SAXParserFactory getFactory() {
0438:
0439: if (factory == null) {
0440: factory = SAXParserFactory.newInstance();
0441: factory.setNamespaceAware(namespaceAware);
0442: factory.setValidating(validating);
0443: }
0444: return (factory);
0445:
0446: }
0447:
0448: /**
0449: * Returns a flag indicating whether the requested feature is supported
0450: * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
0451: * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
0452: * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
0453: * for information about the standard SAX2 feature flags.
0454: *
0455: * @param feature Name of the feature to inquire about
0456: *
0457: * @exception ParserConfigurationException if a parser configuration error
0458: * occurs
0459: * @exception SAXNotRecognizedException if the property name is
0460: * not recognized
0461: * @exception SAXNotSupportedException if the property name is
0462: * recognized but not supported
0463: */
0464: public boolean getFeature(String feature)
0465: throws ParserConfigurationException,
0466: SAXNotRecognizedException, SAXNotSupportedException {
0467:
0468: return (getFactory().getFeature(feature));
0469:
0470: }
0471:
0472: /**
0473: * Sets a flag indicating whether the requested feature is supported
0474: * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
0475: * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
0476: * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
0477: * for information about the standard SAX2 feature flags. In order to be
0478: * effective, this method must be called <strong>before</strong> the
0479: * <code>getParser()</code> method is called for the first time, either
0480: * directly or indirectly.
0481: *
0482: * @param feature Name of the feature to set the status for
0483: * @param value The new value for this feature
0484: *
0485: * @exception ParserConfigurationException if a parser configuration error
0486: * occurs
0487: * @exception SAXNotRecognizedException if the property name is
0488: * not recognized
0489: * @exception SAXNotSupportedException if the property name is
0490: * recognized but not supported
0491: */
0492: public void setFeature(String feature, boolean value)
0493: throws ParserConfigurationException,
0494: SAXNotRecognizedException, SAXNotSupportedException {
0495:
0496: getFactory().setFeature(feature, value);
0497:
0498: }
0499:
0500: /**
0501: * Return the current Logger associated with this instance of the Digester
0502: */
0503: public Log getLogger() {
0504:
0505: return log;
0506:
0507: }
0508:
0509: /**
0510: * Set the current logger for this Digester.
0511: */
0512: public void setLogger(Log log) {
0513:
0514: this .log = log;
0515:
0516: }
0517:
0518: /**
0519: * Gets the logger used for logging SAX-related information.
0520: * <strong>Note</strong> the output is finely grained.
0521: *
0522: * @since 1.6
0523: */
0524: public Log getSAXLogger() {
0525:
0526: return saxLog;
0527: }
0528:
0529: /**
0530: * Sets the logger used for logging SAX-related information.
0531: * <strong>Note</strong> the output is finely grained.
0532: * @param saxLog Log, not null
0533: *
0534: * @since 1.6
0535: */
0536: public void setSAXLogger(Log saxLog) {
0537:
0538: this .saxLog = saxLog;
0539: }
0540:
0541: /**
0542: * Return the current rule match path
0543: */
0544: public String getMatch() {
0545:
0546: return match;
0547:
0548: }
0549:
0550: /**
0551: * Return the "namespace aware" flag for parsers we create.
0552: */
0553: public boolean getNamespaceAware() {
0554:
0555: return (this .namespaceAware);
0556:
0557: }
0558:
0559: /**
0560: * Set the "namespace aware" flag for parsers we create.
0561: *
0562: * @param namespaceAware The new "namespace aware" flag
0563: */
0564: public void setNamespaceAware(boolean namespaceAware) {
0565:
0566: this .namespaceAware = namespaceAware;
0567:
0568: }
0569:
0570: /**
0571: * Set the publid id of the current file being parse.
0572: * @param publicId the DTD/Schema public's id.
0573: */
0574: public void setPublicId(String publicId) {
0575: this .publicId = publicId;
0576: }
0577:
0578: /**
0579: * Return the public identifier of the DTD we are currently
0580: * parsing under, if any.
0581: */
0582: public String getPublicId() {
0583:
0584: return (this .publicId);
0585:
0586: }
0587:
0588: /**
0589: * Return the namespace URI that will be applied to all subsequently
0590: * added <code>Rule</code> objects.
0591: */
0592: public String getRuleNamespaceURI() {
0593:
0594: return (getRules().getNamespaceURI());
0595:
0596: }
0597:
0598: /**
0599: * Set the namespace URI that will be applied to all subsequently
0600: * added <code>Rule</code> objects.
0601: *
0602: * @param ruleNamespaceURI Namespace URI that must match on all
0603: * subsequently added rules, or <code>null</code> for matching
0604: * regardless of the current namespace URI
0605: */
0606: public void setRuleNamespaceURI(String ruleNamespaceURI) {
0607:
0608: getRules().setNamespaceURI(ruleNamespaceURI);
0609:
0610: }
0611:
0612: /**
0613: * Return the SAXParser we will use to parse the input stream. If there
0614: * is a problem creating the parser, return <code>null</code>.
0615: */
0616: public SAXParser getParser() {
0617:
0618: // Return the parser we already created (if any)
0619: if (parser != null) {
0620: return (parser);
0621: }
0622:
0623: // Create a new parser
0624: try {
0625: if (validating) {
0626: Properties properties = new Properties();
0627: properties.put("SAXParserFactory", getFactory());
0628: if (schemaLocation != null) {
0629: properties.put("schemaLocation", schemaLocation);
0630: properties.put("schemaLanguage", schemaLanguage);
0631: }
0632: parser = ParserFeatureSetterFactory
0633: .newSAXParser(properties);
0634: } else {
0635: parser = getFactory().newSAXParser();
0636: }
0637: } catch (Exception e) {
0638: log.error("Digester.getParser: ", e);
0639: return (null);
0640: }
0641:
0642: return (parser);
0643:
0644: }
0645:
0646: /**
0647: * Return the current value of the specified property for the underlying
0648: * <code>XMLReader</code> implementation.
0649: * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
0650: * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
0651: * for information about the standard SAX2 properties.
0652: *
0653: * @param property Property name to be retrieved
0654: *
0655: * @exception SAXNotRecognizedException if the property name is
0656: * not recognized
0657: * @exception SAXNotSupportedException if the property name is
0658: * recognized but not supported
0659: */
0660: public Object getProperty(String property)
0661: throws SAXNotRecognizedException, SAXNotSupportedException {
0662:
0663: return (getParser().getProperty(property));
0664:
0665: }
0666:
0667: /**
0668: * Set the current value of the specified property for the underlying
0669: * <code>XMLReader</code> implementation.
0670: * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
0671: * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
0672: * for information about the standard SAX2 properties.
0673: *
0674: * @param property Property name to be set
0675: * @param value Property value to be set
0676: *
0677: * @exception SAXNotRecognizedException if the property name is
0678: * not recognized
0679: * @exception SAXNotSupportedException if the property name is
0680: * recognized but not supported
0681: */
0682: public void setProperty(String property, Object value)
0683: throws SAXNotRecognizedException, SAXNotSupportedException {
0684:
0685: getParser().setProperty(property, value);
0686:
0687: }
0688:
0689: /**
0690: * By setting the reader in the constructor, you can bypass JAXP and
0691: * be able to use digester in Weblogic 6.0.
0692: *
0693: * @deprecated Use getXMLReader() instead, which can throw a
0694: * SAXException if the reader cannot be instantiated
0695: */
0696: public XMLReader getReader() {
0697:
0698: try {
0699: return (getXMLReader());
0700: } catch (SAXException e) {
0701: log.error("Cannot get XMLReader", e);
0702: return (null);
0703: }
0704:
0705: }
0706:
0707: /**
0708: * Return the <code>Rules</code> implementation object containing our
0709: * rules collection and associated matching policy. If none has been
0710: * established, a default implementation will be created and returned.
0711: */
0712: public Rules getRules() {
0713:
0714: if (this .rules == null) {
0715: this .rules = new RulesBase();
0716: this .rules.setDigester(this );
0717: }
0718: return (this .rules);
0719:
0720: }
0721:
0722: /**
0723: * Set the <code>Rules</code> implementation object containing our
0724: * rules collection and associated matching policy.
0725: *
0726: * @param rules New Rules implementation
0727: */
0728: public void setRules(Rules rules) {
0729:
0730: this .rules = rules;
0731: this .rules.setDigester(this );
0732:
0733: }
0734:
0735: /**
0736: * Return the XML Schema URI used for validating an XML instance.
0737: */
0738: public String getSchema() {
0739:
0740: return (this .schemaLocation);
0741:
0742: }
0743:
0744: /**
0745: * Set the XML Schema URI used for validating a XML Instance.
0746: *
0747: * @param schemaLocation a URI to the schema.
0748: */
0749: public void setSchema(String schemaLocation) {
0750:
0751: this .schemaLocation = schemaLocation;
0752:
0753: }
0754:
0755: /**
0756: * Return the XML Schema language used when parsing.
0757: */
0758: public String getSchemaLanguage() {
0759:
0760: return (this .schemaLanguage);
0761:
0762: }
0763:
0764: /**
0765: * Set the XML Schema language used when parsing. By default, we use W3C.
0766: *
0767: * @param schemaLanguage a URI to the schema language.
0768: */
0769: public void setSchemaLanguage(String schemaLanguage) {
0770:
0771: this .schemaLanguage = schemaLanguage;
0772:
0773: }
0774:
0775: /**
0776: * Return the boolean as to whether the context classloader should be used.
0777: */
0778: public boolean getUseContextClassLoader() {
0779:
0780: return useContextClassLoader;
0781:
0782: }
0783:
0784: /**
0785: * Determine whether to use the Context ClassLoader (the one found by
0786: * calling <code>Thread.currentThread().getContextClassLoader()</code>)
0787: * to resolve/load classes that are defined in various rules. If not
0788: * using Context ClassLoader, then the class-loading defaults to
0789: * using the calling-class' ClassLoader.
0790: *
0791: * @param use determines whether to use Context ClassLoader.
0792: */
0793: public void setUseContextClassLoader(boolean use) {
0794:
0795: useContextClassLoader = use;
0796:
0797: }
0798:
0799: /**
0800: * Return the validating parser flag.
0801: */
0802: public boolean getValidating() {
0803:
0804: return (this .validating);
0805:
0806: }
0807:
0808: /**
0809: * Set the validating parser flag. This must be called before
0810: * <code>parse()</code> is called the first time.
0811: *
0812: * @param validating The new validating parser flag.
0813: */
0814: public void setValidating(boolean validating) {
0815:
0816: this .validating = validating;
0817:
0818: }
0819:
0820: /**
0821: * Return the XMLReader to be used for parsing the input document.
0822: *
0823: * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a
0824: * parser that contains a schema with a DTD.
0825: * @exception SAXException if no XMLReader can be instantiated
0826: */
0827: public XMLReader getXMLReader() throws SAXException {
0828: if (reader == null) {
0829: reader = getParser().getXMLReader();
0830: }
0831:
0832: reader.setDTDHandler(this );
0833: reader.setContentHandler(this );
0834:
0835: if (entityResolver == null) {
0836: reader.setEntityResolver(this );
0837: } else {
0838: reader.setEntityResolver(entityResolver);
0839: }
0840:
0841: reader.setErrorHandler(this );
0842: return reader;
0843: }
0844:
0845: // ------------------------------------------------- ContentHandler Methods
0846:
0847: /**
0848: * Process notification of character data received from the body of
0849: * an XML element.
0850: *
0851: * @param buffer The characters from the XML document
0852: * @param start Starting offset into the buffer
0853: * @param length Number of characters from the buffer
0854: *
0855: * @exception SAXException if a parsing error is to be reported
0856: */
0857: public void characters(char buffer[], int start, int length)
0858: throws SAXException {
0859:
0860: if (saxLog.isDebugEnabled()) {
0861: saxLog.debug("characters("
0862: + new String(buffer, start, length) + ")");
0863: }
0864:
0865: bodyText.append(buffer, start, length);
0866:
0867: }
0868:
0869: /**
0870: * Process notification of the end of the document being reached.
0871: *
0872: * @exception SAXException if a parsing error is to be reported
0873: */
0874: public void endDocument() throws SAXException {
0875:
0876: if (saxLog.isDebugEnabled()) {
0877: if (getCount() > 1) {
0878: saxLog.debug("endDocument(): " + getCount()
0879: + " elements left");
0880: } else {
0881: saxLog.debug("endDocument()");
0882: }
0883: }
0884:
0885: while (getCount() > 1) {
0886: pop();
0887: }
0888:
0889: // Fire "finish" events for all defined rules
0890: Iterator rules = getRules().rules().iterator();
0891: while (rules.hasNext()) {
0892: Rule rule = (Rule) rules.next();
0893: try {
0894: rule.finish();
0895: } catch (Exception e) {
0896: log.error("Finish event threw exception", e);
0897: throw createSAXException(e);
0898: } catch (Error e) {
0899: log.error("Finish event threw error", e);
0900: throw e;
0901: }
0902: }
0903:
0904: // Perform final cleanup
0905: clear();
0906:
0907: }
0908:
0909: /**
0910: * Process notification of the end of an XML element being reached.
0911: *
0912: * @param namespaceURI - The Namespace URI, or the empty string if the
0913: * element has no Namespace URI or if Namespace processing is not
0914: * being performed.
0915: * @param localName - The local name (without prefix), or the empty
0916: * string if Namespace processing is not being performed.
0917: * @param qName - The qualified XML 1.0 name (with prefix), or the
0918: * empty string if qualified names are not available.
0919: * @exception SAXException if a parsing error is to be reported
0920: */
0921: public void endElement(String namespaceURI, String localName,
0922: String qName) throws SAXException {
0923:
0924: boolean debug = log.isDebugEnabled();
0925:
0926: if (debug) {
0927: if (saxLog.isDebugEnabled()) {
0928: saxLog.debug("endElement(" + namespaceURI + ","
0929: + localName + "," + qName + ")");
0930: }
0931: log.debug(" match='" + match + "'");
0932: log.debug(" bodyText='" + bodyText + "'");
0933: }
0934:
0935: // Parse system properties
0936: bodyText = updateBodyText(bodyText);
0937:
0938: // the actual element name is either in localName or qName, depending
0939: // on whether the parser is namespace aware
0940: String name = localName;
0941: if ((name == null) || (name.length() < 1)) {
0942: name = qName;
0943: }
0944:
0945: // Fire "body" events for all relevant rules
0946: List rules = (List) matches.pop();
0947: if ((rules != null) && (rules.size() > 0)) {
0948: String bodyText = this .bodyText.toString();
0949: for (int i = 0; i < rules.size(); i++) {
0950: try {
0951: Rule rule = (Rule) rules.get(i);
0952: if (debug) {
0953: log.debug(" Fire body() for " + rule);
0954: }
0955: rule.body(namespaceURI, name, bodyText);
0956: } catch (Exception e) {
0957: log.error("Body event threw exception", e);
0958: throw createSAXException(e);
0959: } catch (Error e) {
0960: log.error("Body event threw error", e);
0961: throw e;
0962: }
0963: }
0964: } else {
0965: if (debug) {
0966: log.debug(" No rules found matching '" + match + "'.");
0967: }
0968: }
0969:
0970: // Recover the body text from the surrounding element
0971: bodyText = (StringBuffer) bodyTexts.pop();
0972: if (debug) {
0973: log.debug(" Popping body text '" + bodyText.toString()
0974: + "'");
0975: }
0976:
0977: // Fire "end" events for all relevant rules in reverse order
0978: if (rules != null) {
0979: for (int i = 0; i < rules.size(); i++) {
0980: int j = (rules.size() - i) - 1;
0981: try {
0982: Rule rule = (Rule) rules.get(j);
0983: if (debug) {
0984: log.debug(" Fire end() for " + rule);
0985: }
0986: rule.end(namespaceURI, name);
0987: } catch (Exception e) {
0988: log.error("End event threw exception", e);
0989: throw createSAXException(e);
0990: } catch (Error e) {
0991: log.error("End event threw error", e);
0992: throw e;
0993: }
0994: }
0995: }
0996:
0997: // Recover the previous match expression
0998: int slash = match.lastIndexOf('/');
0999: if (slash >= 0) {
1000: match = match.substring(0, slash);
1001: } else {
1002: match = "";
1003: }
1004:
1005: }
1006:
1007: /**
1008: * Process notification that a namespace prefix is going out of scope.
1009: *
1010: * @param prefix Prefix that is going out of scope
1011: *
1012: * @exception SAXException if a parsing error is to be reported
1013: */
1014: public void endPrefixMapping(String prefix) throws SAXException {
1015:
1016: if (saxLog.isDebugEnabled()) {
1017: saxLog.debug("endPrefixMapping(" + prefix + ")");
1018: }
1019:
1020: // Deregister this prefix mapping
1021: ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1022: if (stack == null) {
1023: return;
1024: }
1025: try {
1026: stack.pop();
1027: if (stack.empty())
1028: namespaces.remove(prefix);
1029: } catch (EmptyStackException e) {
1030: throw createSAXException("endPrefixMapping popped too many times");
1031: }
1032:
1033: }
1034:
1035: /**
1036: * Process notification of ignorable whitespace received from the body of
1037: * an XML element.
1038: *
1039: * @param buffer The characters from the XML document
1040: * @param start Starting offset into the buffer
1041: * @param len Number of characters from the buffer
1042: *
1043: * @exception SAXException if a parsing error is to be reported
1044: */
1045: public void ignorableWhitespace(char buffer[], int start, int len)
1046: throws SAXException {
1047:
1048: if (saxLog.isDebugEnabled()) {
1049: saxLog.debug("ignorableWhitespace("
1050: + new String(buffer, start, len) + ")");
1051: }
1052:
1053: ; // No processing required
1054:
1055: }
1056:
1057: /**
1058: * Process notification of a processing instruction that was encountered.
1059: *
1060: * @param target The processing instruction target
1061: * @param data The processing instruction data (if any)
1062: *
1063: * @exception SAXException if a parsing error is to be reported
1064: */
1065: public void processingInstruction(String target, String data)
1066: throws SAXException {
1067:
1068: if (saxLog.isDebugEnabled()) {
1069: saxLog.debug("processingInstruction('" + target + "','"
1070: + data + "')");
1071: }
1072:
1073: ; // No processing is required
1074:
1075: }
1076:
1077: /**
1078: * Gets the document locator associated with our parser.
1079: *
1080: * @return the Locator supplied by the document parser
1081: */
1082: public Locator getDocumentLocator() {
1083:
1084: return locator;
1085:
1086: }
1087:
1088: /**
1089: * Sets the document locator associated with our parser.
1090: *
1091: * @param locator The new locator
1092: */
1093: public void setDocumentLocator(Locator locator) {
1094:
1095: if (saxLog.isDebugEnabled()) {
1096: saxLog.debug("setDocumentLocator(" + locator + ")");
1097: }
1098:
1099: this .locator = locator;
1100:
1101: }
1102:
1103: /**
1104: * Process notification of a skipped entity.
1105: *
1106: * @param name Name of the skipped entity
1107: *
1108: * @exception SAXException if a parsing error is to be reported
1109: */
1110: public void skippedEntity(String name) throws SAXException {
1111:
1112: if (saxLog.isDebugEnabled()) {
1113: saxLog.debug("skippedEntity(" + name + ")");
1114: }
1115:
1116: ; // No processing required
1117:
1118: }
1119:
1120: /**
1121: * Process notification of the beginning of the document being reached.
1122: *
1123: * @exception SAXException if a parsing error is to be reported
1124: */
1125: public void startDocument() throws SAXException {
1126:
1127: if (saxLog.isDebugEnabled()) {
1128: saxLog.debug("startDocument()");
1129: }
1130:
1131: // ensure that the digester is properly configured, as
1132: // the digester could be used as a SAX ContentHandler
1133: // rather than via the parse() methods.
1134: configure();
1135: }
1136:
1137: /**
1138: * Process notification of the start of an XML element being reached.
1139: *
1140: * @param namespaceURI The Namespace URI, or the empty string if the element
1141: * has no Namespace URI or if Namespace processing is not being performed.
1142: * @param localName The local name (without prefix), or the empty
1143: * string if Namespace processing is not being performed.
1144: * @param qName The qualified name (with prefix), or the empty
1145: * string if qualified names are not available.\
1146: * @param list The attributes attached to the element. If there are
1147: * no attributes, it shall be an empty Attributes object.
1148: * @exception SAXException if a parsing error is to be reported
1149: */
1150: public void startElement(String namespaceURI, String localName,
1151: String qName, Attributes list) throws SAXException {
1152: boolean debug = log.isDebugEnabled();
1153:
1154: if (saxLog.isDebugEnabled()) {
1155: saxLog.debug("startElement(" + namespaceURI + ","
1156: + localName + "," + qName + ")");
1157: }
1158:
1159: // Parse system properties
1160: list = updateAttributes(list);
1161:
1162: // Save the body text accumulated for our surrounding element
1163: bodyTexts.push(bodyText);
1164: if (debug) {
1165: log.debug(" Pushing body text '" + bodyText.toString()
1166: + "'");
1167: }
1168: bodyText = new StringBuffer();
1169:
1170: // the actual element name is either in localName or qName, depending
1171: // on whether the parser is namespace aware
1172: String name = localName;
1173: if ((name == null) || (name.length() < 1)) {
1174: name = qName;
1175: }
1176:
1177: // Compute the current matching rule
1178: StringBuffer sb = new StringBuffer(match);
1179: if (match.length() > 0) {
1180: sb.append('/');
1181: }
1182: sb.append(name);
1183: match = sb.toString();
1184: if (debug) {
1185: log.debug(" New match='" + match + "'");
1186: }
1187:
1188: // Fire "begin" events for all relevant rules
1189: List rules = getRules().match(namespaceURI, match);
1190: matches.push(rules);
1191: if ((rules != null) && (rules.size() > 0)) {
1192: for (int i = 0; i < rules.size(); i++) {
1193: try {
1194: Rule rule = (Rule) rules.get(i);
1195: if (debug) {
1196: log.debug(" Fire begin() for " + rule);
1197: }
1198: rule.begin(namespaceURI, name, list);
1199: } catch (Exception e) {
1200: log.error("Begin event threw exception", e);
1201: throw createSAXException(e);
1202: } catch (Error e) {
1203: log.error("Begin event threw error", e);
1204: throw e;
1205: }
1206: }
1207: } else {
1208: if (debug) {
1209: log.debug(" No rules found matching '" + match + "'.");
1210: }
1211: }
1212:
1213: }
1214:
1215: /**
1216: * Process notification that a namespace prefix is coming in to scope.
1217: *
1218: * @param prefix Prefix that is being declared
1219: * @param namespaceURI Corresponding namespace URI being mapped to
1220: *
1221: * @exception SAXException if a parsing error is to be reported
1222: */
1223: public void startPrefixMapping(String prefix, String namespaceURI)
1224: throws SAXException {
1225:
1226: if (saxLog.isDebugEnabled()) {
1227: saxLog.debug("startPrefixMapping(" + prefix + ","
1228: + namespaceURI + ")");
1229: }
1230:
1231: // Register this prefix mapping
1232: ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1233: if (stack == null) {
1234: stack = new ArrayStack();
1235: namespaces.put(prefix, stack);
1236: }
1237: stack.push(namespaceURI);
1238:
1239: }
1240:
1241: // ----------------------------------------------------- DTDHandler Methods
1242:
1243: /**
1244: * Receive notification of a notation declaration event.
1245: *
1246: * @param name The notation name
1247: * @param publicId The public identifier (if any)
1248: * @param systemId The system identifier (if any)
1249: */
1250: public void notationDecl(String name, String publicId,
1251: String systemId) {
1252:
1253: if (saxLog.isDebugEnabled()) {
1254: saxLog.debug("notationDecl(" + name + "," + publicId + ","
1255: + systemId + ")");
1256: }
1257:
1258: }
1259:
1260: /**
1261: * Receive notification of an unparsed entity declaration event.
1262: *
1263: * @param name The unparsed entity name
1264: * @param publicId The public identifier (if any)
1265: * @param systemId The system identifier (if any)
1266: * @param notation The name of the associated notation
1267: */
1268: public void unparsedEntityDecl(String name, String publicId,
1269: String systemId, String notation) {
1270:
1271: if (saxLog.isDebugEnabled()) {
1272: saxLog.debug("unparsedEntityDecl(" + name + "," + publicId
1273: + "," + systemId + "," + notation + ")");
1274: }
1275:
1276: }
1277:
1278: // ----------------------------------------------- EntityResolver Methods
1279:
1280: /**
1281: * Set the <code>EntityResolver</code> used by SAX when resolving
1282: * public id and system id.
1283: * This must be called before the first call to <code>parse()</code>.
1284: * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
1285: */
1286: public void setEntityResolver(EntityResolver entityResolver) {
1287: this .entityResolver = entityResolver;
1288: }
1289:
1290: /**
1291: * Return the Entity Resolver used by the SAX parser.
1292: * @return Return the Entity Resolver used by the SAX parser.
1293: */
1294: public EntityResolver getEntityResolver() {
1295: return entityResolver;
1296: }
1297:
1298: /**
1299: * Resolve the requested external entity.
1300: *
1301: * @param publicId The public identifier of the entity being referenced
1302: * @param systemId The system identifier of the entity being referenced
1303: *
1304: * @exception SAXException if a parsing exception occurs
1305: *
1306: */
1307: public InputSource resolveEntity(String publicId, String systemId)
1308: throws SAXException {
1309:
1310: if (saxLog.isDebugEnabled()) {
1311: saxLog.debug("resolveEntity('" + publicId + "', '"
1312: + systemId + "')");
1313: }
1314:
1315: if (publicId != null)
1316: this .publicId = publicId;
1317:
1318: // Has this system identifier been registered?
1319: String entityURL = null;
1320: if (publicId != null) {
1321: entityURL = (String) entityValidator.get(publicId);
1322: }
1323:
1324: // Redirect the schema location to a local destination
1325: if (schemaLocation != null && entityURL == null
1326: && systemId != null) {
1327: entityURL = (String) entityValidator.get(systemId);
1328: }
1329:
1330: if (entityURL == null) {
1331: if (systemId == null) {
1332: // cannot resolve
1333: if (log.isDebugEnabled()) {
1334: log.debug(" Cannot resolve entity: '" + entityURL
1335: + "'");
1336: }
1337: return (null);
1338:
1339: } else {
1340: // try to resolve using system ID
1341: if (log.isDebugEnabled()) {
1342: log.debug(" Trying to resolve using system ID '"
1343: + systemId + "'");
1344: }
1345: entityURL = systemId;
1346: }
1347: }
1348:
1349: // Return an input source to our alternative URL
1350: if (log.isDebugEnabled()) {
1351: log
1352: .debug(" Resolving to alternate DTD '" + entityURL
1353: + "'");
1354: }
1355:
1356: try {
1357: return (new InputSource(entityURL));
1358: } catch (Exception e) {
1359: throw createSAXException(e);
1360: }
1361: }
1362:
1363: // ------------------------------------------------- ErrorHandler Methods
1364:
1365: /**
1366: * Forward notification of a parsing error to the application supplied
1367: * error handler (if any).
1368: *
1369: * @param exception The error information
1370: *
1371: * @exception SAXException if a parsing exception occurs
1372: */
1373: public void error(SAXParseException exception) throws SAXException {
1374:
1375: log.error("Parse Error at line " + exception.getLineNumber()
1376: + " column " + exception.getColumnNumber() + ": "
1377: + exception.getMessage(), exception);
1378: if (errorHandler != null) {
1379: errorHandler.error(exception);
1380: }
1381:
1382: }
1383:
1384: /**
1385: * Forward notification of a fatal parsing error to the application
1386: * supplied error handler (if any).
1387: *
1388: * @param exception The fatal error information
1389: *
1390: * @exception SAXException if a parsing exception occurs
1391: */
1392: public void fatalError(SAXParseException exception)
1393: throws SAXException {
1394:
1395: log.error("Parse Fatal Error at line "
1396: + exception.getLineNumber() + " column "
1397: + exception.getColumnNumber() + ": "
1398: + exception.getMessage(), exception);
1399: if (errorHandler != null) {
1400: errorHandler.fatalError(exception);
1401: }
1402:
1403: }
1404:
1405: /**
1406: * Forward notification of a parse warning to the application supplied
1407: * error handler (if any).
1408: *
1409: * @param exception The warning information
1410: *
1411: * @exception SAXException if a parsing exception occurs
1412: */
1413: public void warning(SAXParseException exception)
1414: throws SAXException {
1415: if (errorHandler != null) {
1416: log.warn("Parse Warning Error at line "
1417: + exception.getLineNumber() + " column "
1418: + exception.getColumnNumber() + ": "
1419: + exception.getMessage(), exception);
1420:
1421: errorHandler.warning(exception);
1422: }
1423:
1424: }
1425:
1426: // ------------------------------------------------------- Public Methods
1427:
1428: /**
1429: * Log a message to our associated logger.
1430: *
1431: * @param message The message to be logged
1432: * @deprecated Call getLogger() and use it's logging methods
1433: */
1434: public void log(String message) {
1435:
1436: log.info(message);
1437:
1438: }
1439:
1440: /**
1441: * Log a message and exception to our associated logger.
1442: *
1443: * @param message The message to be logged
1444: * @deprecated Call getLogger() and use it's logging methods
1445: */
1446: public void log(String message, Throwable exception) {
1447:
1448: log.error(message, exception);
1449:
1450: }
1451:
1452: /**
1453: * Parse the content of the specified file using this Digester. Returns
1454: * the root element from the object stack (if any).
1455: *
1456: * @param file File containing the XML data to be parsed
1457: *
1458: * @exception IOException if an input/output error occurs
1459: * @exception SAXException if a parsing exception occurs
1460: */
1461: public Object parse(File file) throws IOException, SAXException {
1462:
1463: configure();
1464: InputSource input = new InputSource(new FileInputStream(file));
1465: input.setSystemId("file://" + file.getAbsolutePath());
1466: getXMLReader().parse(input);
1467: return (root);
1468:
1469: }
1470:
1471: /**
1472: * Parse the content of the specified input source using this Digester.
1473: * Returns the root element from the object stack (if any).
1474: *
1475: * @param input Input source containing the XML data to be parsed
1476: *
1477: * @exception IOException if an input/output error occurs
1478: * @exception SAXException if a parsing exception occurs
1479: */
1480: public Object parse(InputSource input) throws IOException,
1481: SAXException {
1482:
1483: configure();
1484: getXMLReader().parse(input);
1485: return (root);
1486:
1487: }
1488:
1489: /**
1490: * Parse the content of the specified input stream using this Digester.
1491: * Returns the root element from the object stack (if any).
1492: *
1493: * @param input Input stream containing the XML data to be parsed
1494: *
1495: * @exception IOException if an input/output error occurs
1496: * @exception SAXException if a parsing exception occurs
1497: */
1498: public Object parse(InputStream input) throws IOException,
1499: SAXException {
1500:
1501: configure();
1502: InputSource is = new InputSource(input);
1503: getXMLReader().parse(is);
1504: return (root);
1505:
1506: }
1507:
1508: /**
1509: * Parse the content of the specified reader using this Digester.
1510: * Returns the root element from the object stack (if any).
1511: *
1512: * @param reader Reader containing the XML data to be parsed
1513: *
1514: * @exception IOException if an input/output error occurs
1515: * @exception SAXException if a parsing exception occurs
1516: */
1517: public Object parse(Reader reader) throws IOException, SAXException {
1518:
1519: configure();
1520: InputSource is = new InputSource(reader);
1521: getXMLReader().parse(is);
1522: return (root);
1523:
1524: }
1525:
1526: /**
1527: * Parse the content of the specified URI using this Digester.
1528: * Returns the root element from the object stack (if any).
1529: *
1530: * @param uri URI containing the XML data to be parsed
1531: *
1532: * @exception IOException if an input/output error occurs
1533: * @exception SAXException if a parsing exception occurs
1534: */
1535: public Object parse(String uri) throws IOException, SAXException {
1536:
1537: configure();
1538: InputSource is = new InputSource(uri);
1539: getXMLReader().parse(is);
1540: return (root);
1541:
1542: }
1543:
1544: /**
1545: * <p>Register the specified DTD URL for the specified public identifier.
1546: * This must be called before the first call to <code>parse()</code>.
1547: * </p><p>
1548: * <code>Digester</code> contains an internal <code>EntityResolver</code>
1549: * implementation. This maps <code>PUBLICID</code>'s to URLs
1550: * (from which the resource will be loaded). A common use case for this
1551: * method is to register local URLs (possibly computed at runtime by a
1552: * classloader) for DTDs. This allows the performance advantage of using
1553: * a local version without having to ensure every <code>SYSTEM</code>
1554: * URI on every processed xml document is local. This implementation provides
1555: * only basic functionality. If more sophisticated features are required,
1556: * using {@link #setEntityResolver} to set a custom resolver is recommended.
1557: * </p><p>
1558: * <strong>Note:</strong> This method will have no effect when a custom
1559: * <code>EntityResolver</code> has been set. (Setting a custom
1560: * <code>EntityResolver</code> overrides the internal implementation.)
1561: * </p>
1562: * @param publicId Public identifier of the DTD to be resolved
1563: * @param entityURL The URL to use for reading this DTD
1564: */
1565: public void register(String publicId, String entityURL) {
1566:
1567: if (log.isDebugEnabled()) {
1568: log.debug("register('" + publicId + "', '" + entityURL
1569: + "'");
1570: }
1571: entityValidator.put(publicId, entityURL);
1572:
1573: }
1574:
1575: // --------------------------------------------------------- Rule Methods
1576:
1577: /**
1578: * <p>Register a new Rule matching the specified pattern.
1579: * This method sets the <code>Digester</code> property on the rule.</p>
1580: *
1581: * @param pattern Element matching pattern
1582: * @param rule Rule to be registered
1583: */
1584: public void addRule(String pattern, Rule rule) {
1585:
1586: rule.setDigester(this );
1587: getRules().add(pattern, rule);
1588:
1589: }
1590:
1591: /**
1592: * Register a set of Rule instances defined in a RuleSet.
1593: *
1594: * @param ruleSet The RuleSet instance to configure from
1595: */
1596: public void addRuleSet(RuleSet ruleSet) {
1597:
1598: String oldNamespaceURI = getRuleNamespaceURI();
1599: String newNamespaceURI = ruleSet.getNamespaceURI();
1600: if (log.isDebugEnabled()) {
1601: if (newNamespaceURI == null) {
1602: log.debug("addRuleSet() with no namespace URI");
1603: } else {
1604: log.debug("addRuleSet() with namespace URI "
1605: + newNamespaceURI);
1606: }
1607: }
1608: setRuleNamespaceURI(newNamespaceURI);
1609: ruleSet.addRuleInstances(this );
1610: setRuleNamespaceURI(oldNamespaceURI);
1611:
1612: }
1613:
1614: /**
1615: * Add an "call method" rule for a method which accepts no arguments.
1616: *
1617: * @param pattern Element matching pattern
1618: * @param methodName Method name to be called
1619: * @see CallMethodRule
1620: */
1621: public void addCallMethod(String pattern, String methodName) {
1622:
1623: addRule(pattern, new CallMethodRule(methodName));
1624:
1625: }
1626:
1627: /**
1628: * Add an "call method" rule for the specified parameters.
1629: *
1630: * @param pattern Element matching pattern
1631: * @param methodName Method name to be called
1632: * @param paramCount Number of expected parameters (or zero
1633: * for a single parameter from the body of this element)
1634: * @see CallMethodRule
1635: */
1636: public void addCallMethod(String pattern, String methodName,
1637: int paramCount) {
1638:
1639: addRule(pattern, new CallMethodRule(methodName, paramCount));
1640:
1641: }
1642:
1643: /**
1644: * Add an "call method" rule for the specified parameters.
1645: * If <code>paramCount</code> is set to zero the rule will use
1646: * the body of the matched element as the single argument of the
1647: * method, unless <code>paramTypes</code> is null or empty, in this
1648: * case the rule will call the specified method with no arguments.
1649: *
1650: * @param pattern Element matching pattern
1651: * @param methodName Method name to be called
1652: * @param paramCount Number of expected parameters (or zero
1653: * for a single parameter from the body of this element)
1654: * @param paramTypes Set of Java class names for the types
1655: * of the expected parameters
1656: * (if you wish to use a primitive type, specify the corresonding
1657: * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1658: * for a <code>boolean</code> parameter)
1659: * @see CallMethodRule
1660: */
1661: public void addCallMethod(String pattern, String methodName,
1662: int paramCount, String paramTypes[]) {
1663:
1664: addRule(pattern, new CallMethodRule(methodName, paramCount,
1665: paramTypes));
1666:
1667: }
1668:
1669: /**
1670: * Add an "call method" rule for the specified parameters.
1671: * If <code>paramCount</code> is set to zero the rule will use
1672: * the body of the matched element as the single argument of the
1673: * method, unless <code>paramTypes</code> is null or empty, in this
1674: * case the rule will call the specified method with no arguments.
1675: *
1676: * @param pattern Element matching pattern
1677: * @param methodName Method name to be called
1678: * @param paramCount Number of expected parameters (or zero
1679: * for a single parameter from the body of this element)
1680: * @param paramTypes The Java class names of the arguments
1681: * (if you wish to use a primitive type, specify the corresonding
1682: * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1683: * for a <code>boolean</code> parameter)
1684: * @see CallMethodRule
1685: */
1686: public void addCallMethod(String pattern, String methodName,
1687: int paramCount, Class paramTypes[]) {
1688:
1689: addRule(pattern, new CallMethodRule(methodName, paramCount,
1690: paramTypes));
1691:
1692: }
1693:
1694: /**
1695: * Add a "call parameter" rule for the specified parameters.
1696: *
1697: * @param pattern Element matching pattern
1698: * @param paramIndex Zero-relative parameter index to set
1699: * (from the body of this element)
1700: * @see CallParamRule
1701: */
1702: public void addCallParam(String pattern, int paramIndex) {
1703:
1704: addRule(pattern, new CallParamRule(paramIndex));
1705:
1706: }
1707:
1708: /**
1709: * Add a "call parameter" rule for the specified parameters.
1710: *
1711: * @param pattern Element matching pattern
1712: * @param paramIndex Zero-relative parameter index to set
1713: * (from the specified attribute)
1714: * @param attributeName Attribute whose value is used as the
1715: * parameter value
1716: * @see CallParamRule
1717: */
1718: public void addCallParam(String pattern, int paramIndex,
1719: String attributeName) {
1720:
1721: addRule(pattern, new CallParamRule(paramIndex, attributeName));
1722:
1723: }
1724:
1725: /**
1726: * Add a "call parameter" rule.
1727: * This will either take a parameter from the stack
1728: * or from the current element body text.
1729: *
1730: * @param paramIndex The zero-relative parameter number
1731: * @param fromStack Should the call parameter be taken from the top of the stack?
1732: * @see CallParamRule
1733: */
1734: public void addCallParam(String pattern, int paramIndex,
1735: boolean fromStack) {
1736:
1737: addRule(pattern, new CallParamRule(paramIndex, fromStack));
1738:
1739: }
1740:
1741: /**
1742: * Add a "call parameter" rule that sets a parameter from the stack.
1743: * This takes a parameter from the given position on the stack.
1744: *
1745: * @param paramIndex The zero-relative parameter number
1746: * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
1747: * where 0 is the top of the stack, 1 the next element down and so on
1748: * @see CallMethodRule
1749: */
1750: public void addCallParam(String pattern, int paramIndex,
1751: int stackIndex) {
1752:
1753: addRule(pattern, new CallParamRule(paramIndex, stackIndex));
1754:
1755: }
1756:
1757: /**
1758: * Add a "call parameter" rule that sets a parameter from the current
1759: * <code>Digester</code> matching path.
1760: * This is sometimes useful when using rules that support wildcards.
1761: *
1762: * @param pattern the pattern that this rule should match
1763: * @param paramIndex The zero-relative parameter number
1764: * @see CallMethodRule
1765: */
1766: public void addCallParamPath(String pattern, int paramIndex) {
1767: addRule(pattern, new PathCallParamRule(paramIndex));
1768: }
1769:
1770: /**
1771: * Add a "call parameter" rule that sets a parameter from a
1772: * caller-provided object. This can be used to pass constants such as
1773: * strings to methods; it can also be used to pass mutable objects,
1774: * providing ways for objects to do things like "register" themselves
1775: * with some shared object.
1776: * <p>
1777: * Note that when attempting to locate a matching method to invoke,
1778: * the true type of the paramObj is used, so that despite the paramObj
1779: * being passed in here as type Object, the target method can declare
1780: * its parameters as being the true type of the object (or some ancestor
1781: * type, according to the usual type-conversion rules).
1782: *
1783: * @param paramIndex The zero-relative parameter number
1784: * @param paramObj Any arbitrary object to be passed to the target
1785: * method.
1786: * @see CallMethodRule
1787: *
1788: * @since 1.6
1789: */
1790: public void addObjectParam(String pattern, int paramIndex,
1791: Object paramObj) {
1792:
1793: addRule(pattern, new ObjectParamRule(paramIndex, paramObj));
1794:
1795: }
1796:
1797: /**
1798: * Add a "factory create" rule for the specified parameters.
1799: * Exceptions thrown during the object creation process will be propagated.
1800: *
1801: * @param pattern Element matching pattern
1802: * @param className Java class name of the object creation factory class
1803: * @see FactoryCreateRule
1804: */
1805: public void addFactoryCreate(String pattern, String className) {
1806:
1807: addFactoryCreate(pattern, className, false);
1808:
1809: }
1810:
1811: /**
1812: * Add a "factory create" rule for the specified parameters.
1813: * Exceptions thrown during the object creation process will be propagated.
1814: *
1815: * @param pattern Element matching pattern
1816: * @param clazz Java class of the object creation factory class
1817: * @see FactoryCreateRule
1818: */
1819: public void addFactoryCreate(String pattern, Class clazz) {
1820:
1821: addFactoryCreate(pattern, clazz, false);
1822:
1823: }
1824:
1825: /**
1826: * Add a "factory create" rule for the specified parameters.
1827: * Exceptions thrown during the object creation process will be propagated.
1828: *
1829: * @param pattern Element matching pattern
1830: * @param className Java class name of the object creation factory class
1831: * @param attributeName Attribute name which, if present, overrides the
1832: * value specified by <code>className</code>
1833: * @see FactoryCreateRule
1834: */
1835: public void addFactoryCreate(String pattern, String className,
1836: String attributeName) {
1837:
1838: addFactoryCreate(pattern, className, attributeName, false);
1839:
1840: }
1841:
1842: /**
1843: * Add a "factory create" rule for the specified parameters.
1844: * Exceptions thrown during the object creation process will be propagated.
1845: *
1846: * @param pattern Element matching pattern
1847: * @param clazz Java class of the object creation factory class
1848: * @param attributeName Attribute name which, if present, overrides the
1849: * value specified by <code>className</code>
1850: * @see FactoryCreateRule
1851: */
1852: public void addFactoryCreate(String pattern, Class clazz,
1853: String attributeName) {
1854:
1855: addFactoryCreate(pattern, clazz, attributeName, false);
1856:
1857: }
1858:
1859: /**
1860: * Add a "factory create" rule for the specified parameters.
1861: * Exceptions thrown during the object creation process will be propagated.
1862: *
1863: * @param pattern Element matching pattern
1864: * @param creationFactory Previously instantiated ObjectCreationFactory
1865: * to be utilized
1866: * @see FactoryCreateRule
1867: */
1868: public void addFactoryCreate(String pattern,
1869: ObjectCreationFactory creationFactory) {
1870:
1871: addFactoryCreate(pattern, creationFactory, false);
1872:
1873: }
1874:
1875: /**
1876: * Add a "factory create" rule for the specified parameters.
1877: *
1878: * @param pattern Element matching pattern
1879: * @param className Java class name of the object creation factory class
1880: * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1881: * object creation will be ignored.
1882: * @see FactoryCreateRule
1883: */
1884: public void addFactoryCreate(String pattern, String className,
1885: boolean ignoreCreateExceptions) {
1886:
1887: addRule(pattern, new FactoryCreateRule(className,
1888: ignoreCreateExceptions));
1889:
1890: }
1891:
1892: /**
1893: * Add a "factory create" rule for the specified parameters.
1894: *
1895: * @param pattern Element matching pattern
1896: * @param clazz Java class of the object creation factory class
1897: * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1898: * object creation will be ignored.
1899: * @see FactoryCreateRule
1900: */
1901: public void addFactoryCreate(String pattern, Class clazz,
1902: boolean ignoreCreateExceptions) {
1903:
1904: addRule(pattern, new FactoryCreateRule(clazz,
1905: ignoreCreateExceptions));
1906:
1907: }
1908:
1909: /**
1910: * Add a "factory create" rule for the specified parameters.
1911: *
1912: * @param pattern Element matching pattern
1913: * @param className Java class name of the object creation factory class
1914: * @param attributeName Attribute name which, if present, overrides the
1915: * value specified by <code>className</code>
1916: * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1917: * object creation will be ignored.
1918: * @see FactoryCreateRule
1919: */
1920: public void addFactoryCreate(String pattern, String className,
1921: String attributeName, boolean ignoreCreateExceptions) {
1922:
1923: addRule(pattern, new FactoryCreateRule(className,
1924: attributeName, ignoreCreateExceptions));
1925:
1926: }
1927:
1928: /**
1929: * Add a "factory create" rule for the specified parameters.
1930: *
1931: * @param pattern Element matching pattern
1932: * @param clazz Java class of the object creation factory class
1933: * @param attributeName Attribute name which, if present, overrides the
1934: * value specified by <code>className</code>
1935: * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1936: * object creation will be ignored.
1937: * @see FactoryCreateRule
1938: */
1939: public void addFactoryCreate(String pattern, Class clazz,
1940: String attributeName, boolean ignoreCreateExceptions) {
1941:
1942: addRule(pattern, new FactoryCreateRule(clazz, attributeName,
1943: ignoreCreateExceptions));
1944:
1945: }
1946:
1947: /**
1948: * Add a "factory create" rule for the specified parameters.
1949: *
1950: * @param pattern Element matching pattern
1951: * @param creationFactory Previously instantiated ObjectCreationFactory
1952: * to be utilized
1953: * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1954: * object creation will be ignored.
1955: * @see FactoryCreateRule
1956: */
1957: public void addFactoryCreate(String pattern,
1958: ObjectCreationFactory creationFactory,
1959: boolean ignoreCreateExceptions) {
1960:
1961: creationFactory.setDigester(this );
1962: addRule(pattern, new FactoryCreateRule(creationFactory,
1963: ignoreCreateExceptions));
1964:
1965: }
1966:
1967: /**
1968: * Add an "object create" rule for the specified parameters.
1969: *
1970: * @param pattern Element matching pattern
1971: * @param className Java class name to be created
1972: * @see ObjectCreateRule
1973: */
1974: public void addObjectCreate(String pattern, String className) {
1975:
1976: addRule(pattern, new ObjectCreateRule(className));
1977:
1978: }
1979:
1980: /**
1981: * Add an "object create" rule for the specified parameters.
1982: *
1983: * @param pattern Element matching pattern
1984: * @param clazz Java class to be created
1985: * @see ObjectCreateRule
1986: */
1987: public void addObjectCreate(String pattern, Class clazz) {
1988:
1989: addRule(pattern, new ObjectCreateRule(clazz));
1990:
1991: }
1992:
1993: /**
1994: * Add an "object create" rule for the specified parameters.
1995: *
1996: * @param pattern Element matching pattern
1997: * @param className Default Java class name to be created
1998: * @param attributeName Attribute name that optionally overrides
1999: * the default Java class name to be created
2000: * @see ObjectCreateRule
2001: */
2002: public void addObjectCreate(String pattern, String className,
2003: String attributeName) {
2004:
2005: addRule(pattern, new ObjectCreateRule(className, attributeName));
2006:
2007: }
2008:
2009: /**
2010: * Add an "object create" rule for the specified parameters.
2011: *
2012: * @param pattern Element matching pattern
2013: * @param attributeName Attribute name that optionally overrides
2014: * @param clazz Default Java class to be created
2015: * the default Java class name to be created
2016: * @see ObjectCreateRule
2017: */
2018: public void addObjectCreate(String pattern, String attributeName,
2019: Class clazz) {
2020:
2021: addRule(pattern, new ObjectCreateRule(attributeName, clazz));
2022:
2023: }
2024:
2025: /**
2026: * Add a "set next" rule for the specified parameters.
2027: *
2028: * @param pattern Element matching pattern
2029: * @param methodName Method name to call on the parent element
2030: * @see SetNextRule
2031: */
2032: public void addSetNext(String pattern, String methodName) {
2033:
2034: addRule(pattern, new SetNextRule(methodName));
2035:
2036: }
2037:
2038: /**
2039: * Add a "set next" rule for the specified parameters.
2040: *
2041: * @param pattern Element matching pattern
2042: * @param methodName Method name to call on the parent element
2043: * @param paramType Java class name of the expected parameter type
2044: * (if you wish to use a primitive type, specify the corresonding
2045: * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2046: * for a <code>boolean</code> parameter)
2047: * @see SetNextRule
2048: */
2049: public void addSetNext(String pattern, String methodName,
2050: String paramType) {
2051:
2052: addRule(pattern, new SetNextRule(methodName, paramType));
2053:
2054: }
2055:
2056: /**
2057: * Add {@link SetRootRule} with the specified parameters.
2058: *
2059: * @param pattern Element matching pattern
2060: * @param methodName Method name to call on the root object
2061: * @see SetRootRule
2062: */
2063: public void addSetRoot(String pattern, String methodName) {
2064:
2065: addRule(pattern, new SetRootRule(methodName));
2066:
2067: }
2068:
2069: /**
2070: * Add {@link SetRootRule} with the specified parameters.
2071: *
2072: * @param pattern Element matching pattern
2073: * @param methodName Method name to call on the root object
2074: * @param paramType Java class name of the expected parameter type
2075: * @see SetRootRule
2076: */
2077: public void addSetRoot(String pattern, String methodName,
2078: String paramType) {
2079:
2080: addRule(pattern, new SetRootRule(methodName, paramType));
2081:
2082: }
2083:
2084: /**
2085: * Add a "set properties" rule for the specified parameters.
2086: *
2087: * @param pattern Element matching pattern
2088: * @see SetPropertiesRule
2089: */
2090: public void addSetProperties(String pattern) {
2091:
2092: addRule(pattern, new SetPropertiesRule());
2093:
2094: }
2095:
2096: /**
2097: * Add a "set properties" rule with a single overridden parameter.
2098: * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
2099: *
2100: * @param pattern Element matching pattern
2101: * @param attributeName map this attribute
2102: * @param propertyName to this property
2103: * @see SetPropertiesRule
2104: */
2105: public void addSetProperties(String pattern, String attributeName,
2106: String propertyName) {
2107:
2108: addRule(pattern, new SetPropertiesRule(attributeName,
2109: propertyName));
2110:
2111: }
2112:
2113: /**
2114: * Add a "set properties" rule with overridden parameters.
2115: * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
2116: *
2117: * @param pattern Element matching pattern
2118: * @param attributeNames names of attributes with custom mappings
2119: * @param propertyNames property names these attributes map to
2120: * @see SetPropertiesRule
2121: */
2122: public void addSetProperties(String pattern,
2123: String[] attributeNames, String[] propertyNames) {
2124:
2125: addRule(pattern, new SetPropertiesRule(attributeNames,
2126: propertyNames));
2127:
2128: }
2129:
2130: /**
2131: * Add a "set property" rule for the specified parameters.
2132: *
2133: * @param pattern Element matching pattern
2134: * @param name Attribute name containing the property name to be set
2135: * @param value Attribute name containing the property value to set
2136: * @see SetPropertyRule
2137: */
2138: public void addSetProperty(String pattern, String name, String value) {
2139:
2140: addRule(pattern, new SetPropertyRule(name, value));
2141:
2142: }
2143:
2144: /**
2145: * Add a "set top" rule for the specified parameters.
2146: *
2147: * @param pattern Element matching pattern
2148: * @param methodName Method name to call on the parent element
2149: * @see SetTopRule
2150: */
2151: public void addSetTop(String pattern, String methodName) {
2152:
2153: addRule(pattern, new SetTopRule(methodName));
2154:
2155: }
2156:
2157: /**
2158: * Add a "set top" rule for the specified parameters.
2159: *
2160: * @param pattern Element matching pattern
2161: * @param methodName Method name to call on the parent element
2162: * @param paramType Java class name of the expected parameter type
2163: * (if you wish to use a primitive type, specify the corresonding
2164: * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2165: * for a <code>boolean</code> parameter)
2166: * @see SetTopRule
2167: */
2168: public void addSetTop(String pattern, String methodName,
2169: String paramType) {
2170:
2171: addRule(pattern, new SetTopRule(methodName, paramType));
2172:
2173: }
2174:
2175: // --------------------------------------------------- Object Stack Methods
2176:
2177: /**
2178: * Clear the current contents of the object stack.
2179: * <p>
2180: * Calling this method <i>might</i> allow another document of the same type
2181: * to be correctly parsed. However this method was not intended for this
2182: * purpose. In general, a separate Digester object should be created for
2183: * each document to be parsed.
2184: */
2185: public void clear() {
2186:
2187: match = "";
2188: bodyTexts.clear();
2189: params.clear();
2190: publicId = null;
2191: stack.clear();
2192: log = null;
2193: saxLog = null;
2194: configured = false;
2195:
2196: }
2197:
2198: public void reset() {
2199: root = null;
2200: setErrorHandler(null);
2201: clear();
2202: }
2203:
2204: /**
2205: * Return the top object on the stack without removing it. If there are
2206: * no objects on the stack, return <code>null</code>.
2207: */
2208: public Object peek() {
2209:
2210: try {
2211: return (stack.peek());
2212: } catch (EmptyStackException e) {
2213: log.warn("Empty stack (returning null)");
2214: return (null);
2215: }
2216:
2217: }
2218:
2219: /**
2220: * Return the n'th object down the stack, where 0 is the top element
2221: * and [getCount()-1] is the bottom element. If the specified index
2222: * is out of range, return <code>null</code>.
2223: *
2224: * @param n Index of the desired element, where 0 is the top of the stack,
2225: * 1 is the next element down, and so on.
2226: */
2227: public Object peek(int n) {
2228:
2229: try {
2230: return (stack.peek(n));
2231: } catch (EmptyStackException e) {
2232: log.warn("Empty stack (returning null)");
2233: return (null);
2234: }
2235:
2236: }
2237:
2238: /**
2239: * Pop the top object off of the stack, and return it. If there are
2240: * no objects on the stack, return <code>null</code>.
2241: */
2242: public Object pop() {
2243:
2244: try {
2245: return (stack.pop());
2246: } catch (EmptyStackException e) {
2247: log.warn("Empty stack (returning null)");
2248: return (null);
2249: }
2250:
2251: }
2252:
2253: /**
2254: * Push a new object onto the top of the object stack.
2255: *
2256: * @param object The new object
2257: */
2258: public void push(Object object) {
2259:
2260: if (stack.size() == 0) {
2261: root = object;
2262: }
2263: stack.push(object);
2264:
2265: }
2266:
2267: /**
2268: * Pushes the given object onto the stack with the given name.
2269: * If no stack already exists with the given name then one will be created.
2270: *
2271: * @param stackName the name of the stack onto which the object should be pushed
2272: * @param value the Object to be pushed onto the named stack.
2273: *
2274: * @since 1.6
2275: */
2276: public void push(String stackName, Object value) {
2277: ArrayStack namedStack = (ArrayStack) stacksByName
2278: .get(stackName);
2279: if (namedStack == null) {
2280: namedStack = new ArrayStack();
2281: stacksByName.put(stackName, namedStack);
2282: }
2283: namedStack.push(value);
2284: }
2285:
2286: /**
2287: * <p>Pops (gets and removes) the top object from the stack with the given name.</p>
2288: *
2289: * <p><strong>Note:</strong> a stack is considered empty
2290: * if no objects have been pushed onto it yet.</p>
2291: *
2292: * @param stackName the name of the stack from which the top value is to be popped
2293: * @return the top <code>Object</code> on the stack or or null if the stack is either
2294: * empty or has not been created yet
2295: * @throws EmptyStackException if the named stack is empty
2296: *
2297: * @since 1.6
2298: */
2299: public Object pop(String stackName) {
2300: Object result = null;
2301: ArrayStack namedStack = (ArrayStack) stacksByName
2302: .get(stackName);
2303: if (namedStack == null) {
2304: if (log.isDebugEnabled()) {
2305: log.debug("Stack '" + stackName + "' is empty");
2306: }
2307: throw new EmptyStackException();
2308:
2309: } else {
2310:
2311: result = namedStack.pop();
2312: }
2313: return result;
2314: }
2315:
2316: /**
2317: * <p>Gets the top object from the stack with the given name.
2318: * This method does not remove the object from the stack.
2319: * </p>
2320: * <p><strong>Note:</strong> a stack is considered empty
2321: * if no objects have been pushed onto it yet.</p>
2322: *
2323: * @param stackName the name of the stack to be peeked
2324: * @return the top <code>Object</code> on the stack or null if the stack is either
2325: * empty or has not been created yet
2326: * @throws EmptyStackException if the named stack is empty
2327: *
2328: * @since 1.6
2329: */
2330: public Object peek(String stackName) {
2331: Object result = null;
2332: ArrayStack namedStack = (ArrayStack) stacksByName
2333: .get(stackName);
2334: if (namedStack == null) {
2335: if (log.isDebugEnabled()) {
2336: log.debug("Stack '" + stackName + "' is empty");
2337: }
2338: throw new EmptyStackException();
2339:
2340: } else {
2341:
2342: result = namedStack.peek();
2343: }
2344: return result;
2345: }
2346:
2347: /**
2348: * <p>Is the stack with the given name empty?</p>
2349: * <p><strong>Note:</strong> a stack is considered empty
2350: * if no objects have been pushed onto it yet.</p>
2351: * @param stackName the name of the stack whose emptiness
2352: * should be evaluated
2353: * @return true if the given stack if empty
2354: *
2355: * @since 1.6
2356: */
2357: public boolean isEmpty(String stackName) {
2358: boolean result = true;
2359: ArrayStack namedStack = (ArrayStack) stacksByName
2360: .get(stackName);
2361: if (namedStack != null) {
2362: result = namedStack.isEmpty();
2363: }
2364: return result;
2365: }
2366:
2367: /**
2368: * When the Digester is being used as a SAXContentHandler,
2369: * this method allows you to access the root object that has been
2370: * created after parsing.
2371: *
2372: * @return the root object that has been created after parsing
2373: * or null if the digester has not parsed any XML yet.
2374: */
2375: public Object getRoot() {
2376: return root;
2377: }
2378:
2379: // ------------------------------------------------ Parameter Stack Methods
2380:
2381: // ------------------------------------------------------ Protected Methods
2382:
2383: /**
2384: * <p>
2385: * Provide a hook for lazy configuration of this <code>Digester</code>
2386: * instance. The default implementation does nothing, but subclasses
2387: * can override as needed.
2388: * </p>
2389: *
2390: * <p>
2391: * <strong>Note</strong> This method may be called more than once.
2392: * Once only initialization code should be placed in {@link #initialize}
2393: * or the code should take responsibility by checking and setting the
2394: * {@link #configured} flag.
2395: * </p>
2396: */
2397: protected void configure() {
2398:
2399: // Do not configure more than once
2400: if (configured) {
2401: return;
2402: }
2403:
2404: log = LogFactory.getLog("org.apache.commons.digester.Digester");
2405: saxLog = LogFactory
2406: .getLog("org.apache.commons.digester.Digester.sax");
2407:
2408: // Perform lazy configuration as needed
2409: initialize(); // call hook method for subclasses that want to be initialized once only
2410: // Nothing else required by default
2411:
2412: // Set the configuration flag to avoid repeating
2413: configured = true;
2414:
2415: }
2416:
2417: /**
2418: * <p>
2419: * Provides a hook for lazy initialization of this <code>Digester</code>
2420: * instance.
2421: * The default implementation does nothing, but subclasses
2422: * can override as needed.
2423: * Digester (by default) only calls this method once.
2424: * </p>
2425: *
2426: * <p>
2427: * <strong>Note</strong> This method will be called by {@link #configure}
2428: * only when the {@link #configured} flag is false.
2429: * Subclasses that override <code>configure</code> or who set <code>configured</code>
2430: * may find that this method may be called more than once.
2431: * </p>
2432: *
2433: * @since 1.6
2434: */
2435: protected void initialize() {
2436:
2437: // Perform lazy initialization as needed
2438: ; // Nothing required by default
2439:
2440: }
2441:
2442: // -------------------------------------------------------- Package Methods
2443:
2444: /**
2445: * Return the set of DTD URL registrations, keyed by public identifier.
2446: */
2447: Map getRegistrations() {
2448:
2449: return (entityValidator);
2450:
2451: }
2452:
2453: /**
2454: * Return the set of rules that apply to the specified match position.
2455: * The selected rules are those that match exactly, or those rules
2456: * that specify a suffix match and the tail of the rule matches the
2457: * current match position. Exact matches have precedence over
2458: * suffix matches, then (among suffix matches) the longest match
2459: * is preferred.
2460: *
2461: * @param match The current match position
2462: *
2463: * @deprecated Call <code>match()</code> on the <code>Rules</code>
2464: * implementation returned by <code>getRules()</code>
2465: */
2466: List getRules(String match) {
2467:
2468: return (getRules().match(match));
2469:
2470: }
2471:
2472: /**
2473: * <p>Return the top object on the parameters stack without removing it. If there are
2474: * no objects on the stack, return <code>null</code>.</p>
2475: *
2476: * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2477: * See {@link #params}.</p>
2478: */
2479: public Object peekParams() {
2480:
2481: try {
2482: return (params.peek());
2483: } catch (EmptyStackException e) {
2484: log.warn("Empty stack (returning null)");
2485: return (null);
2486: }
2487:
2488: }
2489:
2490: /**
2491: * <p>Return the n'th object down the parameters stack, where 0 is the top element
2492: * and [getCount()-1] is the bottom element. If the specified index
2493: * is out of range, return <code>null</code>.</p>
2494: *
2495: * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2496: * See {@link #params}.</p>
2497: *
2498: * @param n Index of the desired element, where 0 is the top of the stack,
2499: * 1 is the next element down, and so on.
2500: */
2501: public Object peekParams(int n) {
2502:
2503: try {
2504: return (params.peek(n));
2505: } catch (EmptyStackException e) {
2506: log.warn("Empty stack (returning null)");
2507: return (null);
2508: }
2509:
2510: }
2511:
2512: /**
2513: * <p>Pop the top object off of the parameters stack, and return it. If there are
2514: * no objects on the stack, return <code>null</code>.</p>
2515: *
2516: * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2517: * See {@link #params}.</p>
2518: */
2519: public Object popParams() {
2520:
2521: try {
2522: if (log.isTraceEnabled()) {
2523: log.trace("Popping params");
2524: }
2525: return (params.pop());
2526: } catch (EmptyStackException e) {
2527: log.warn("Empty stack (returning null)");
2528: return (null);
2529: }
2530:
2531: }
2532:
2533: /**
2534: * <p>Push a new object onto the top of the parameters stack.</p>
2535: *
2536: * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2537: * See {@link #params}.</p>
2538: *
2539: * @param object The new object
2540: */
2541: public void pushParams(Object object) {
2542: if (log.isTraceEnabled()) {
2543: log.trace("Pushing params");
2544: }
2545: params.push(object);
2546:
2547: }
2548:
2549: /**
2550: * Create a SAX exception which also understands about the location in
2551: * the digester file where the exception occurs
2552: *
2553: * @return the new exception
2554: */
2555: public SAXException createSAXException(String message, Exception e) {
2556: if ((e != null) && (e instanceof InvocationTargetException)) {
2557: Throwable t = ((InvocationTargetException) e)
2558: .getTargetException();
2559: if ((t != null) && (t instanceof Exception)) {
2560: e = (Exception) t;
2561: }
2562: }
2563: if (locator != null) {
2564: String error = "Error at (" + locator.getLineNumber()
2565: + ", " + locator.getColumnNumber() + ": " + message;
2566: if (e != null) {
2567: return new SAXParseException(error, locator, e);
2568: } else {
2569: return new SAXParseException(error, locator);
2570: }
2571: }
2572: log.error("No Locator!");
2573: if (e != null) {
2574: return new SAXException(message, e);
2575: } else {
2576: return new SAXException(message);
2577: }
2578: }
2579:
2580: /**
2581: * Create a SAX exception which also understands about the location in
2582: * the digester file where the exception occurs
2583: *
2584: * @return the new exception
2585: */
2586: public SAXException createSAXException(Exception e) {
2587: if (e instanceof InvocationTargetException) {
2588: Throwable t = ((InvocationTargetException) e)
2589: .getTargetException();
2590: if ((t != null) && (t instanceof Exception)) {
2591: e = (Exception) t;
2592: }
2593: }
2594: return createSAXException(e.getMessage(), e);
2595: }
2596:
2597: /**
2598: * Create a SAX exception which also understands about the location in
2599: * the digester file where the exception occurs
2600: *
2601: * @return the new exception
2602: */
2603: public SAXException createSAXException(String message) {
2604: return createSAXException(message, null);
2605: }
2606:
2607: // ------------------------------------------------------- Private Methods
2608:
2609: /**
2610: * Returns an attributes list which contains all the attributes
2611: * passed in, with any text of form "${xxx}" in an attribute value
2612: * replaced by the appropriate value from the system property.
2613: */
2614: private Attributes updateAttributes(Attributes list) {
2615:
2616: if (list.getLength() == 0) {
2617: return list;
2618: }
2619:
2620: AttributesImpl newAttrs = new AttributesImpl(list);
2621: int nAttributes = newAttrs.getLength();
2622: for (int i = 0; i < nAttributes; ++i) {
2623: String value = newAttrs.getValue(i);
2624: try {
2625: String newValue = IntrospectionUtils.replaceProperties(
2626: value, null, source);
2627: if (value != newValue) {
2628: newAttrs.setValue(i, newValue);
2629: }
2630: } catch (Exception e) {
2631: // ignore - let the attribute have its original value
2632: }
2633: }
2634:
2635: return newAttrs;
2636:
2637: }
2638:
2639: /**
2640: * Return a new StringBuffer containing the same contents as the
2641: * input buffer, except that data of form ${varname} have been
2642: * replaced by the value of that var as defined in the system property.
2643: */
2644: private StringBuffer updateBodyText(StringBuffer bodyText) {
2645: String in = bodyText.toString();
2646: String out;
2647: try {
2648: out = IntrospectionUtils
2649: .replaceProperties(in, null, source);
2650: } catch (Exception e) {
2651: return bodyText; // return unchanged data
2652: }
2653:
2654: if (out == in) {
2655: // No substitutions required. Don't waste memory creating
2656: // a new buffer
2657: return bodyText;
2658: } else {
2659: return new StringBuffer(out);
2660: }
2661: }
2662:
2663: }
|