e.g. Calendar Search Help
You must enter a value before pressing Search
tomcat

Class: org.apache.tomcat.util.digester.Digester   ©

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

            
All Examples in File:
Example
Line
Rating (found
useful by...)
1545 100% of 2
1561 0% of 0
1580 0% of 0
1599 0% of 0
1618 0% of 0