Save This Page
Home » commons-digester-1.8-src » org.apache.commons » digester » [javadoc | source]
    1   /* $Id: Digester.java 480169 2006-11-28 19:37:36Z rahul $
    2    *
    3    * Licensed to the Apache Software Foundation (ASF) under one or more
    4    * contributor license agreements.  See the NOTICE file distributed with
    5    * this work for additional information regarding copyright ownership.
    6    * The ASF licenses this file to You under the Apache License, Version 2.0
    7    * (the "License"); you may not use this file except in compliance with
    8    * the License.  You may obtain a copy of the License at
    9    * 
   10    *      http://www.apache.org/licenses/LICENSE-2.0
   11    * 
   12    * Unless required by applicable law or agreed to in writing, software
   13    * distributed under the License is distributed on an "AS IS" BASIS,
   14    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15    * See the License for the specific language governing permissions and
   16    * limitations under the License.
   17    */ 
   18   
   19   package org.apache.commons.digester;
   20   
   21   
   22   import java.io.File;
   23   import java.io.FileInputStream;
   24   import java.io.IOException;
   25   import java.io.InputStream;
   26   import java.io.Reader;
   27   import java.lang.reflect.InvocationTargetException;
   28   import java.net.URL;
   29   import java.net.URLConnection;
   30   import java.net.MalformedURLException;
   31   import java.util.ArrayList;
   32   import java.util.EmptyStackException;
   33   import java.util.HashMap;
   34   import java.util.Iterator;
   35   import java.util.List;
   36   import java.util.Map;
   37   import java.util.Properties;
   38   
   39   import javax.xml.parsers.ParserConfigurationException;
   40   import javax.xml.parsers.SAXParser;
   41   import javax.xml.parsers.SAXParserFactory;
   42   
   43   import org.apache.commons.logging.Log;
   44   import org.apache.commons.logging.LogFactory;
   45   import org.apache.commons.collections.ArrayStack;
   46   
   47   import org.xml.sax.Attributes;
   48   import org.xml.sax.ContentHandler;
   49   import org.xml.sax.EntityResolver;
   50   import org.xml.sax.ErrorHandler;
   51   import org.xml.sax.InputSource;
   52   import org.xml.sax.Locator;
   53   import org.xml.sax.SAXException;
   54   import org.xml.sax.SAXNotRecognizedException;
   55   import org.xml.sax.SAXNotSupportedException;
   56   import org.xml.sax.SAXParseException;
   57   import org.xml.sax.XMLReader;
   58   import org.xml.sax.helpers.DefaultHandler;
   59   
   60   
   61   
   62   
   63   /**
   64    * <p>A <strong>Digester</strong> processes an XML input stream by matching a
   65    * series of element nesting patterns to execute Rules that have been added
   66    * prior to the start of parsing.  This package was inspired by the
   67    * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
   68    * but is organized somewhat differently.</p>
   69    *
   70    * <p>See the <a href="package-summary.html#package_description">Digester
   71    * Developer Guide</a> for more information.</p>
   72    *
   73    * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
   74    * only be used within the context of a single thread at a time, and a call
   75    * to <code>parse()</code> must be completed before another can be initiated
   76    * even from the same thread.</p>
   77    *
   78    * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents
   79    * the support of XML schema. You need Xerces 2.1/2.3 and up to make
   80    * this class working with XML schema</p>
   81    */
   82   
   83   public class Digester extends DefaultHandler {
   84   
   85   
   86       // --------------------------------------------------------- Constructors
   87   
   88   
   89       /**
   90        * Construct a new Digester with default properties.
   91        */
   92       public Digester() {
   93   
   94           super();
   95   
   96       }
   97   
   98   
   99       /**
  100        * Construct a new Digester, allowing a SAXParser to be passed in.  This
  101        * allows Digester to be used in environments which are unfriendly to
  102        * JAXP1.1 (such as WebLogic 6.0). This may help in places where
  103        * you are able to load JAXP 1.1 classes yourself.
  104        */
  105       public Digester(SAXParser parser) {
  106   
  107           super();
  108   
  109           this.parser = parser;
  110   
  111       }
  112   
  113   
  114       /**
  115        * Construct a new Digester, allowing an XMLReader to be passed in.  This
  116        * allows Digester to be used in environments which are unfriendly to
  117        * JAXP1.1 (such as WebLogic 6.0).  Note that if you use this option you
  118        * have to configure namespace and validation support yourself, as these
  119        * properties only affect the SAXParser and emtpy constructor.
  120        */
  121       public Digester(XMLReader reader) {
  122   
  123           super();
  124   
  125           this.reader = reader;
  126   
  127       }
  128   
  129   
  130       // --------------------------------------------------- Instance Variables
  131   
  132   
  133       /**
  134        * The body text of the current element.
  135        */
  136       protected StringBuffer bodyText = new StringBuffer();
  137   
  138   
  139       /**
  140        * The stack of body text string buffers for surrounding elements.
  141        */
  142       protected ArrayStack bodyTexts = new ArrayStack();
  143   
  144   
  145       /**
  146        * Stack whose elements are List objects, each containing a list of
  147        * Rule objects as returned from Rules.getMatch(). As each xml element
  148        * in the input is entered, the matching rules are pushed onto this
  149        * stack. After the end tag is reached, the matches are popped again.
  150        * The depth of is stack is therefore exactly the same as the current
  151        * "nesting" level of the input xml. 
  152        *
  153        * @since 1.6
  154        */
  155       protected ArrayStack matches = new ArrayStack(10);
  156       
  157       /**
  158        * The class loader to use for instantiating application objects.
  159        * If not specified, the context class loader, or the class loader
  160        * used to load Digester itself, is used, based on the value of the
  161        * <code>useContextClassLoader</code> variable.
  162        */
  163       protected ClassLoader classLoader = null;
  164   
  165   
  166       /**
  167        * Has this Digester been configured yet.
  168        */
  169       protected boolean configured = false;
  170   
  171   
  172       /**
  173        * The EntityResolver used by the SAX parser. By default it use this class
  174        */
  175       protected EntityResolver entityResolver;
  176       
  177       /**
  178        * The URLs of entityValidator that have been registered, keyed by the public
  179        * identifier that corresponds.
  180        */
  181       protected HashMap entityValidator = new HashMap();
  182   
  183   
  184       /**
  185        * The application-supplied error handler that is notified when parsing
  186        * warnings, errors, or fatal errors occur.
  187        */
  188       protected ErrorHandler errorHandler = null;
  189   
  190   
  191       /**
  192        * The SAXParserFactory that is created the first time we need it.
  193        */
  194       protected SAXParserFactory factory = null;
  195   
  196       /**
  197        * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
  198        */
  199       protected String JAXP_SCHEMA_LANGUAGE =
  200           "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
  201       
  202       
  203       /**
  204        * The Locator associated with our parser.
  205        */
  206       protected Locator locator = null;
  207   
  208   
  209       /**
  210        * The current match pattern for nested element processing.
  211        */
  212       protected String match = "";
  213   
  214   
  215       /**
  216        * Do we want a "namespace aware" parser.
  217        */
  218       protected boolean namespaceAware = false;
  219   
  220   
  221       /**
  222        * Registered namespaces we are currently processing.  The key is the
  223        * namespace prefix that was declared in the document.  The value is an
  224        * ArrayStack of the namespace URIs this prefix has been mapped to --
  225        * the top Stack element is the most current one.  (This architecture
  226        * is required because documents can declare nested uses of the same
  227        * prefix for different Namespace URIs).
  228        */
  229       protected HashMap namespaces = new HashMap();
  230   
  231   
  232       /**
  233        * The parameters stack being utilized by CallMethodRule and
  234        * CallParamRule rules.
  235        */
  236       protected ArrayStack params = new ArrayStack();
  237   
  238   
  239       /**
  240        * The SAXParser we will use to parse the input stream.
  241        */
  242       protected SAXParser parser = null;
  243   
  244   
  245       /**
  246        * The public identifier of the DTD we are currently parsing under
  247        * (if any).
  248        */
  249       protected String publicId = null;
  250   
  251   
  252       /**
  253        * The XMLReader used to parse digester rules.
  254        */
  255       protected XMLReader reader = null;
  256   
  257   
  258       /**
  259        * The "root" element of the stack (in other words, the last object
  260        * that was popped.
  261        */
  262       protected Object root = null;
  263   
  264   
  265       /**
  266        * The <code>Rules</code> implementation containing our collection of
  267        * <code>Rule</code> instances and associated matching policy.  If not
  268        * established before the first rule is added, a default implementation
  269        * will be provided.
  270        */
  271       protected Rules rules = null;
  272   
  273      /**
  274        * The XML schema language to use for validating an XML instance. By
  275        * default this value is set to <code>W3C_XML_SCHEMA</code>
  276        */
  277       protected String schemaLanguage = W3C_XML_SCHEMA;
  278       
  279           
  280       /**
  281        * The XML schema to use for validating an XML instance.
  282        */
  283       protected String schemaLocation = null;
  284       
  285       
  286       /**
  287        * The object stack being constructed.
  288        */
  289       protected ArrayStack stack = new ArrayStack();
  290   
  291   
  292       /**
  293        * Do we want to use the Context ClassLoader when loading classes
  294        * for instantiating new objects.  Default is <code>false</code>.
  295        */
  296       protected boolean useContextClassLoader = false;
  297   
  298   
  299       /**
  300        * Do we want to use a validating parser.
  301        */
  302       protected boolean validating = false;
  303   
  304   
  305       /**
  306        * The Log to which most logging calls will be made.
  307        */
  308       protected Log log =
  309           LogFactory.getLog("org.apache.commons.digester.Digester");
  310   
  311   
  312       /**
  313        * The Log to which all SAX event related logging calls will be made.
  314        */
  315       protected Log saxLog =
  316           LogFactory.getLog("org.apache.commons.digester.Digester.sax");
  317       
  318           
  319       /**
  320        * The schema language supported. By default, we use this one.
  321        */
  322       protected static final String W3C_XML_SCHEMA =
  323           "http://www.w3.org/2001/XMLSchema";
  324       
  325       /**
  326        * An optional class that substitutes values in attributes and body text.
  327        * This may be null and so a null check is always required before use.
  328        */
  329       protected Substitutor substitutor;
  330       
  331       /** Stacks used for interrule communication, indexed by name String */
  332       private HashMap stacksByName = new HashMap();
  333       
  334       /**
  335        * If not null, then calls by the parser to this object's characters, 
  336        * startElement, endElement and processingInstruction methods are 
  337        * forwarded to the specified object. This is intended to allow rules
  338        * to temporarily "take control" of the sax events. In particular, 
  339        * this is used by NodeCreateRule.
  340        * <p>
  341        * See setCustomContentHandler.
  342        */
  343       private ContentHandler customContentHandler = null;
  344   
  345       /**
  346        * Object which will receive callbacks for every pop/push action
  347        * on the default stack or named stacks. 
  348        */
  349       private StackAction stackAction = null;
  350   
  351       // ------------------------------------------------------------- Properties
  352   
  353       /**
  354        * Return the currently mapped namespace URI for the specified prefix,
  355        * if any; otherwise return <code>null</code>.  These mappings come and
  356        * go dynamically as the document is parsed.
  357        *
  358        * @param prefix Prefix to look up
  359        */
  360       public String findNamespaceURI(String prefix) {
  361           
  362           ArrayStack nsStack = (ArrayStack) namespaces.get(prefix);
  363           if (nsStack == null) {
  364               return null;
  365           }
  366           try {
  367               return ((String) nsStack.peek());
  368           } catch (EmptyStackException e) {
  369               return null;
  370           }
  371   
  372       }
  373   
  374   
  375       /**
  376        * Return the class loader to be used for instantiating application objects
  377        * when required.  This is determined based upon the following rules:
  378        * <ul>
  379        * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
  380        * <li>The thread context class loader, if it exists and the
  381        *     <code>useContextClassLoader</code> property is set to true</li>
  382        * <li>The class loader used to load the Digester class itself.
  383        * </ul>
  384        */
  385       public ClassLoader getClassLoader() {
  386   
  387           if (this.classLoader != null) {
  388               return (this.classLoader);
  389           }
  390           if (this.useContextClassLoader) {
  391               ClassLoader classLoader =
  392                       Thread.currentThread().getContextClassLoader();
  393               if (classLoader != null) {
  394                   return (classLoader);
  395               }
  396           }
  397           return (this.getClass().getClassLoader());
  398   
  399       }
  400   
  401   
  402       /**
  403        * Set the class loader to be used for instantiating application objects
  404        * when required.
  405        *
  406        * @param classLoader The new class loader to use, or <code>null</code>
  407        *  to revert to the standard rules
  408        */
  409       public void setClassLoader(ClassLoader classLoader) {
  410   
  411           this.classLoader = classLoader;
  412   
  413       }
  414   
  415   
  416       /**
  417        * Return the current depth of the element stack.
  418        */
  419       public int getCount() {
  420   
  421           return (stack.size());
  422   
  423       }
  424   
  425   
  426       /**
  427        * Return the name of the XML element that is currently being processed.
  428        */
  429       public String getCurrentElementName() {
  430   
  431           String elementName = match;
  432           int lastSlash = elementName.lastIndexOf('/');
  433           if (lastSlash >= 0) {
  434               elementName = elementName.substring(lastSlash + 1);
  435           }
  436           return (elementName);
  437   
  438       }
  439   
  440   
  441       /**
  442        * Return the debugging detail level of our currently enabled logger.
  443        *
  444        * @deprecated This method now always returns 0. Digester uses the apache
  445        * jakarta commons-logging library; see the documentation for that library
  446        * for more information.
  447        */
  448       public int getDebug() {
  449   
  450           return (0);
  451   
  452       }
  453   
  454   
  455       /**
  456        * Set the debugging detail level of our currently enabled logger.
  457        *
  458        * @param debug New debugging detail level (0=off, increasing integers
  459        *  for more detail)
  460        *
  461        * @deprecated This method now has no effect at all. Digester uses
  462        * the apache jakarta comons-logging library; see the documentation
  463        * for that library for more information.
  464        */
  465       public void setDebug(int debug) {
  466   
  467           ; // No action is taken
  468   
  469       }
  470   
  471   
  472       /**
  473        * Return the error handler for this Digester.
  474        */
  475       public ErrorHandler getErrorHandler() {
  476   
  477           return (this.errorHandler);
  478   
  479       }
  480   
  481   
  482       /**
  483        * Set the error handler for this Digester.
  484        *
  485        * @param errorHandler The new error handler
  486        */
  487       public void setErrorHandler(ErrorHandler errorHandler) {
  488   
  489           this.errorHandler = errorHandler;
  490   
  491       }
  492   
  493   
  494       /**
  495        * Return the SAXParserFactory we will use, creating one if necessary.
  496        */
  497       public SAXParserFactory getFactory() {
  498   
  499           if (factory == null) {
  500               factory = SAXParserFactory.newInstance();
  501               factory.setNamespaceAware(namespaceAware);
  502               factory.setValidating(validating);
  503           }
  504           return (factory);
  505   
  506       }
  507   
  508   
  509       /**
  510        * Returns a flag indicating whether the requested feature is supported
  511        * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
  512        * See <a href="http://www.saxproject.org">the saxproject website</a>
  513        * for information about the standard SAX2 feature flags.
  514        *
  515        * @param feature Name of the feature to inquire about
  516        *
  517        * @exception ParserConfigurationException if a parser configuration error
  518        *  occurs
  519        * @exception SAXNotRecognizedException if the property name is
  520        *  not recognized
  521        * @exception SAXNotSupportedException if the property name is
  522        *  recognized but not supported
  523        */
  524       public boolean getFeature(String feature)
  525           throws ParserConfigurationException, SAXNotRecognizedException,
  526           SAXNotSupportedException {
  527   
  528           return (getFactory().getFeature(feature));
  529   
  530       }
  531   
  532   
  533       /**
  534        * Sets a flag indicating whether the requested feature is supported
  535        * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
  536        * See <a href="http://www.saxproject.org">the saxproject website</a>
  537        * for information about the standard SAX2 feature flags.  In order to be
  538        * effective, this method must be called <strong>before</strong> the
  539        * <code>getParser()</code> method is called for the first time, either
  540        * directly or indirectly.
  541        *
  542        * @param feature Name of the feature to set the status for
  543        * @param value The new value for this feature
  544        *
  545        * @exception ParserConfigurationException if a parser configuration error
  546        *  occurs
  547        * @exception SAXNotRecognizedException if the property name is
  548        *  not recognized
  549        * @exception SAXNotSupportedException if the property name is
  550        *  recognized but not supported
  551        */
  552       public void setFeature(String feature, boolean value)
  553           throws ParserConfigurationException, SAXNotRecognizedException,
  554           SAXNotSupportedException {
  555   
  556           getFactory().setFeature(feature, value);
  557   
  558       }
  559   
  560   
  561       /**
  562        * Return the current Logger associated with this instance of the Digester
  563        */
  564       public Log getLogger() {
  565   
  566           return log;
  567   
  568       }
  569   
  570   
  571       /**
  572        * Set the current logger for this Digester.
  573        */
  574       public void setLogger(Log log) {
  575   
  576           this.log = log;
  577   
  578       }
  579   
  580       /**
  581        * Gets the logger used for logging SAX-related information.
  582        * <strong>Note</strong> the output is finely grained.
  583        *
  584        * @since 1.6
  585        */
  586       public Log getSAXLogger() {
  587           
  588           return saxLog;
  589       }
  590       
  591   
  592       /**
  593        * Sets the logger used for logging SAX-related information.
  594        * <strong>Note</strong> the output is finely grained.
  595        * @param saxLog Log, not null
  596        *
  597        * @since 1.6
  598        */    
  599       public void setSAXLogger(Log saxLog) {
  600       
  601           this.saxLog = saxLog;
  602       }
  603   
  604       /**
  605        * Return the current rule match path
  606        */
  607       public String getMatch() {
  608   
  609           return match;
  610   
  611       }
  612   
  613   
  614       /**
  615        * Return the "namespace aware" flag for parsers we create.
  616        */
  617       public boolean getNamespaceAware() {
  618   
  619           return (this.namespaceAware);
  620   
  621       }
  622   
  623   
  624       /**
  625        * Set the "namespace aware" flag for parsers we create.
  626        *
  627        * @param namespaceAware The new "namespace aware" flag
  628        */
  629       public void setNamespaceAware(boolean namespaceAware) {
  630   
  631           this.namespaceAware = namespaceAware;
  632   
  633       }
  634   
  635       
  636       /**
  637        * Set the publid id of the current file being parse.
  638        * @param publicId the DTD/Schema public's id.
  639        */
  640       public void setPublicId(String publicId){
  641           this.publicId = publicId;
  642       }
  643       
  644       
  645       /**
  646        * Return the public identifier of the DTD we are currently
  647        * parsing under, if any.
  648        */
  649       public String getPublicId() {
  650   
  651           return (this.publicId);
  652   
  653       }
  654   
  655   
  656       /**
  657        * Return the namespace URI that will be applied to all subsequently
  658        * added <code>Rule</code> objects.
  659        */
  660       public String getRuleNamespaceURI() {
  661   
  662           return (getRules().getNamespaceURI());
  663   
  664       }
  665   
  666   
  667       /**
  668        * Set the namespace URI that will be applied to all subsequently
  669        * added <code>Rule</code> objects.
  670        *
  671        * @param ruleNamespaceURI Namespace URI that must match on all
  672        *  subsequently added rules, or <code>null</code> for matching
  673        *  regardless of the current namespace URI
  674        */
  675       public void setRuleNamespaceURI(String ruleNamespaceURI) {
  676   
  677           getRules().setNamespaceURI(ruleNamespaceURI);
  678   
  679       }
  680   
  681   
  682       /**
  683        * Return the SAXParser we will use to parse the input stream.  If there
  684        * is a problem creating the parser, return <code>null</code>.
  685        */
  686       public SAXParser getParser() {
  687   
  688           // Return the parser we already created (if any)
  689           if (parser != null) {
  690               return (parser);
  691           }
  692   
  693           // Create a new parser
  694           try {
  695               if (validating && (schemaLocation != null)) {
  696                   // There is no portable way to specify the location of
  697                   // an xml schema to be applied to the input document, so
  698                   // we have to use parser-specific code for this. That code
  699                   // is hidden behind the ParserFeatureSetterFactory class.
  700   
  701                   Properties properties = new Properties();
  702                   properties.put("SAXParserFactory", getFactory());
  703                   if (schemaLocation != null) {
  704                       properties.put("schemaLocation", schemaLocation);
  705                       properties.put("schemaLanguage", schemaLanguage);
  706                   }
  707                   parser = ParserFeatureSetterFactory.newSAXParser(properties);
  708               } else {
  709                   // The user doesn't want to use any non-portable parsing features,
  710                   // so we can just use the portable API here. Note that method
  711                   // getFactory returns a factory already configured with the
  712                   // appropriate namespaceAware and validating properties.
  713   
  714                   parser = getFactory().newSAXParser();
  715               }
  716           } catch (Exception e) {
  717               log.error("Digester.getParser: ", e);
  718               return (null);
  719           }
  720   
  721           return (parser);
  722   
  723       }
  724   
  725   
  726       /**
  727        * Return the current value of the specified property for the underlying
  728        * <code>XMLReader</code> implementation.
  729        * See <a href="http://www.saxproject.org">the saxproject website</a>
  730        * for information about the standard SAX2 properties.
  731        *
  732        * @param property Property name to be retrieved
  733        *
  734        * @exception SAXNotRecognizedException if the property name is
  735        *  not recognized
  736        * @exception SAXNotSupportedException if the property name is
  737        *  recognized but not supported
  738        */
  739       public Object getProperty(String property)
  740           throws SAXNotRecognizedException, SAXNotSupportedException {
  741   
  742           return (getParser().getProperty(property));
  743   
  744       }
  745   
  746   
  747       /**
  748        * Set the current value of the specified property for the underlying
  749        * <code>XMLReader</code> implementation.
  750        * See <a href="http://www.saxproject.org">the saxproject website</a>
  751        * for information about the standard SAX2 properties.
  752        *
  753        * @param property Property name to be set
  754        * @param value Property value to be set
  755        *
  756        * @exception SAXNotRecognizedException if the property name is
  757        *  not recognized
  758        * @exception SAXNotSupportedException if the property name is
  759        *  recognized but not supported
  760        */
  761       public void setProperty(String property, Object value)
  762           throws SAXNotRecognizedException, SAXNotSupportedException {
  763   
  764           getParser().setProperty(property, value);
  765   
  766       }
  767   
  768   
  769       /**
  770        * By setting the reader in the constructor, you can bypass JAXP and
  771        * be able to use digester in Weblogic 6.0.  
  772        *
  773        * @deprecated Use getXMLReader() instead, which can throw a
  774        *  SAXException if the reader cannot be instantiated
  775        */
  776       public XMLReader getReader() {
  777   
  778           try {
  779               return (getXMLReader());
  780           } catch (SAXException e) {
  781               log.error("Cannot get XMLReader", e);
  782               return (null);
  783           }
  784   
  785       }
  786   
  787   
  788       /**
  789        * Return the <code>Rules</code> implementation object containing our
  790        * rules collection and associated matching policy.  If none has been
  791        * established, a default implementation will be created and returned.
  792        */
  793       public Rules getRules() {
  794   
  795           if (this.rules == null) {
  796               this.rules = new RulesBase();
  797               this.rules.setDigester(this);
  798           }
  799           return (this.rules);
  800   
  801       }
  802   
  803       
  804       /**
  805        * Set the <code>Rules</code> implementation object containing our
  806        * rules collection and associated matching policy.
  807        *
  808        * @param rules New Rules implementation
  809        */
  810       public void setRules(Rules rules) {
  811   
  812           this.rules = rules;
  813           this.rules.setDigester(this);
  814   
  815       }
  816   
  817   
  818       /**
  819        * Return the XML Schema URI used for validating an XML instance.
  820        */
  821       public String getSchema() {
  822   
  823           return (this.schemaLocation);
  824   
  825       }
  826   
  827   
  828       /**
  829        * Set the XML Schema URI used for validating the input XML.
  830        * <p>
  831        * It is often desirable to <i>force</i> the input document to be
  832        * validated against a particular schema regardless of what type
  833        * the input document declares itself to be. This method allows that
  834        * to be done. 
  835        * <p>
  836        * Note, however, that there is no standard API for enabling this
  837        * feature on the underlying SAX parser; this method therefore only works 
  838        * for those parsers explicitly supported by Digester's
  839        * ParserFeatureSetterFactory class. If the underlying parser does not
  840        * support the feature, or is not one of the supported parsers, then
  841        * an exception will be thrown when getParser is called (explicitly, 
  842        * or implicitly via the parse method).
  843        * <p>
  844        * See also method setSchemaLanguage which allows the type of the schema
  845        * specified here to be defined. By default, the schema is expected to
  846        * be a W3C xml schema definition.
  847        * <p>
  848        * IMPORTANT NOTE: This functionality was never very reliable, and has
  849        * been horribly broken since the 1.6 release of Digester. There are
  850        * currently no plans to fix it, so you are strongly recommended to
  851        * avoid using this method. Instead, create an XMLParser instance
  852        * yourself, configure validation appropriately, and pass it as a
  853        * parameter to the Digester constructor.
  854        *
  855        * @param schemaLocation a URI to the schema.
  856        */
  857       public void setSchema(String schemaLocation){
  858   
  859           this.schemaLocation = schemaLocation;
  860   
  861       }   
  862       
  863   
  864       /**
  865        * Return the XML Schema language used when parsing.
  866        */
  867       public String getSchemaLanguage() {
  868   
  869           return (this.schemaLanguage);
  870   
  871       }
  872   
  873   
  874       /**
  875        * Set the XML Schema language used when parsing. By default, we use W3C.
  876        *
  877        * @param schemaLanguage a URI to the schema language.
  878        */
  879       public void setSchemaLanguage(String schemaLanguage){
  880   
  881           this.schemaLanguage = schemaLanguage;
  882   
  883       }   
  884   
  885   
  886       /**
  887        * Return the boolean as to whether the context classloader should be used.
  888        */
  889       public boolean getUseContextClassLoader() {
  890   
  891           return useContextClassLoader;
  892   
  893       }
  894   
  895   
  896       /**
  897        * Determine whether to use the Context ClassLoader (the one found by
  898        * calling <code>Thread.currentThread().getContextClassLoader()</code>)
  899        * to resolve/load classes that are defined in various rules.  If not
  900        * using Context ClassLoader, then the class-loading defaults to
  901        * using the calling-class' ClassLoader.
  902        *
  903        * @param use determines whether to use Context ClassLoader.
  904        */
  905       public void setUseContextClassLoader(boolean use) {
  906   
  907           useContextClassLoader = use;
  908   
  909       }
  910   
  911   
  912       /**
  913        * Return the validating parser flag.
  914        */
  915       public boolean getValidating() {
  916   
  917           return (this.validating);
  918   
  919       }
  920   
  921   
  922       /**
  923        * Set the validating parser flag.  This must be called before
  924        * <code>parse()</code> is called the first time.
  925        *
  926        * @param validating The new validating parser flag.
  927        */
  928       public void setValidating(boolean validating) {
  929   
  930           this.validating = validating;
  931   
  932       }
  933   
  934   
  935       /**
  936        * Return the XMLReader to be used for parsing the input document.
  937        *
  938        * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a 
  939        * parser that contains a schema with a DTD.
  940        * @exception SAXException if no XMLReader can be instantiated
  941        */
  942       public XMLReader getXMLReader() throws SAXException {
  943           if (reader == null){
  944               reader = getParser().getXMLReader();
  945           }        
  946                                  
  947           reader.setDTDHandler(this);           
  948           reader.setContentHandler(this);        
  949           
  950           if (entityResolver == null){
  951               reader.setEntityResolver(this);
  952           } else {
  953               reader.setEntityResolver(entityResolver);           
  954           }
  955           
  956           reader.setErrorHandler(this);
  957           return reader;
  958       }
  959   
  960       /**
  961        * Gets the <code>Substitutor</code> used to convert attributes and body text.
  962        * @return Substitutor, null if not substitutions are to be performed.
  963        */
  964       public Substitutor getSubstitutor() {
  965           return substitutor;
  966       }
  967       
  968       /** 
  969        * Sets the <code>Substitutor</code> to be used to convert attributes and body text.
  970        * @param substitutor the Substitutor to be used to convert attributes and body text
  971        * or null if not substitution of these values is to be performed.
  972        */
  973       public void setSubstitutor(Substitutor substitutor) {
  974           this.substitutor = substitutor;
  975       }
  976   
  977       /*
  978        * See setCustomContentHandler.
  979        * 
  980        * @since 1.7 
  981        */
  982       public ContentHandler getCustomContentHandler() {
  983           return customContentHandler;
  984       }
  985   
  986       /** 
  987        * Redirects (or cancels redirecting) of SAX ContentHandler events to an
  988        * external object.
  989        * <p>
  990        * When this object's customContentHandler is non-null, any SAX events
  991        * received from the parser will simply be passed on to the specified 
  992        * object instead of this object handling them. This allows Rule classes 
  993        * to take control of the SAX event stream for a while in order to do 
  994        * custom processing. Such a rule should save the old value before setting
  995        * a new one, and restore the old value in order to resume normal digester
  996        * processing.
  997        * <p>
  998        * An example of a Rule which needs this feature is NodeCreateRule.
  999        * <p>
 1000        * Note that saving the old value is probably not needed as it should always
 1001        * be null; a custom rule that wants to take control could only have been 
 1002        * called when there was no custom content handler. But it seems cleaner
 1003        * to properly save/restore the value and maybe some day this will come in
 1004        * useful.
 1005        * <p>
 1006        * Note also that this is not quite equivalent to
 1007        * <pre>
 1008        * digester.getXMLReader().setContentHandler(handler)
 1009        * </pre>
 1010        * for these reasons:
 1011        * <ul>
 1012        * <li>Some xml parsers don't like having setContentHandler called after
 1013        * parsing has started. The Aelfred parser is one example.</li>
 1014        * <li>Directing the events via the Digester object potentially allows
 1015        * us to log information about those SAX events at the digester level.</li>
 1016        * </ul>
 1017        * 
 1018        * @since 1.7 
 1019        */
 1020       public void setCustomContentHandler(ContentHandler handler) {
 1021           customContentHandler = handler;
 1022       }
 1023   
 1024       /** 
 1025        * Define a callback object which is invoked whever an object is pushed onto
 1026        * a digester object stack, or popped off one.
 1027        * 
 1028        * @since 1.8
 1029        */
 1030       public void setStackAction(StackAction stackAction) {
 1031           this.stackAction = stackAction;
 1032       }
 1033   
 1034       /**
 1035        * See setStackAction. 
 1036        * 
 1037        * @since 1.8
 1038        */
 1039       public StackAction getStackAction() {
 1040           return stackAction;
 1041       }
 1042   
 1043       /**
 1044        * Get the most current namespaces for all prefixes.
 1045        *
 1046        * @return Map A map with namespace prefixes as keys and most current
 1047        *             namespace URIs for the corresponding prefixes as values
 1048        *
 1049        * @since 1.8
 1050        */
 1051       public Map getCurrentNamespaces() {
 1052           if (!namespaceAware) {
 1053               log.warn("Digester is not namespace aware");
 1054           }
 1055           Map currentNamespaces = new HashMap();
 1056           Iterator nsIterator = namespaces.entrySet().iterator();
 1057           while (nsIterator.hasNext()) {
 1058               Map.Entry nsEntry = (Map.Entry) nsIterator.next();
 1059               try {
 1060                   currentNamespaces.put(nsEntry.getKey(),
 1061                       ((ArrayStack) nsEntry.getValue()).peek());
 1062               } catch (RuntimeException e) {
 1063                   // rethrow, after logging
 1064                   log.error(e.getMessage(), e);
 1065                   throw e;
 1066               }
 1067           }
 1068           return currentNamespaces;
 1069       }
 1070   
 1071       // ------------------------------------------------- ContentHandler Methods
 1072   
 1073   
 1074       /**
 1075        * Process notification of character data received from the body of
 1076        * an XML element.
 1077        *
 1078        * @param buffer The characters from the XML document
 1079        * @param start Starting offset into the buffer
 1080        * @param length Number of characters from the buffer
 1081        *
 1082        * @exception SAXException if a parsing error is to be reported
 1083        */
 1084       public void characters(char buffer[], int start, int length)
 1085               throws SAXException {
 1086   
 1087           if (customContentHandler != null) {
 1088               // forward calls instead of handling them here
 1089               customContentHandler.characters(buffer, start, length);
 1090               return;
 1091           }
 1092   
 1093           if (saxLog.isDebugEnabled()) {
 1094               saxLog.debug("characters(" + new String(buffer, start, length) + ")");
 1095           }
 1096   
 1097           bodyText.append(buffer, start, length);
 1098   
 1099       }
 1100   
 1101   
 1102       /**
 1103        * Process notification of the end of the document being reached.
 1104        *
 1105        * @exception SAXException if a parsing error is to be reported
 1106        */
 1107       public void endDocument() throws SAXException {
 1108   
 1109           if (saxLog.isDebugEnabled()) {
 1110               if (getCount() > 1) {
 1111                   saxLog.debug("endDocument():  " + getCount() +
 1112                                " elements left");
 1113               } else {
 1114                   saxLog.debug("endDocument()");
 1115               }
 1116           }
 1117   
 1118           // Fire "finish" events for all defined rules
 1119           Iterator rules = getRules().rules().iterator();
 1120           while (rules.hasNext()) {
 1121               Rule rule = (Rule) rules.next();
 1122               try {
 1123                   rule.finish();
 1124               } catch (Exception e) {
 1125                   log.error("Finish event threw exception", e);
 1126                   throw createSAXException(e);
 1127               } catch (Error e) {
 1128                   log.error("Finish event threw error", e);
 1129                   throw e;
 1130               }
 1131           }
 1132   
 1133           // Perform final cleanup
 1134           clear();
 1135   
 1136       }
 1137   
 1138   
 1139       /**
 1140        * Process notification of the end of an XML element being reached.
 1141        *
 1142        * @param namespaceURI - The Namespace URI, or the empty string if the
 1143        *   element has no Namespace URI or if Namespace processing is not
 1144        *   being performed.
 1145        * @param localName - The local name (without prefix), or the empty
 1146        *   string if Namespace processing is not being performed.
 1147        * @param qName - The qualified XML 1.0 name (with prefix), or the
 1148        *   empty string if qualified names are not available.
 1149        * @exception SAXException if a parsing error is to be reported
 1150        */
 1151       public void endElement(String namespaceURI, String localName,
 1152                              String qName) throws SAXException {
 1153   
 1154           if (customContentHandler != null) {
 1155               // forward calls instead of handling them here
 1156               customContentHandler.endElement(namespaceURI, localName, qName);
 1157               return;
 1158           }
 1159   
 1160           boolean debug = log.isDebugEnabled();
 1161   
 1162           if (debug) {
 1163               if (saxLog.isDebugEnabled()) {
 1164                   saxLog.debug("endElement(" + namespaceURI + "," + localName +
 1165                           "," + qName + ")");
 1166               }
 1167               log.debug("  match='" + match + "'");
 1168               log.debug("  bodyText='" + bodyText + "'");
 1169           }
 1170   
 1171           // the actual element name is either in localName or qName, depending 
 1172           // on whether the parser is namespace aware
 1173           String name = localName;
 1174           if ((name == null) || (name.length() < 1)) {
 1175               name = qName;
 1176           }
 1177   
 1178           // Fire "body" events for all relevant rules
 1179           List rules = (List) matches.pop();
 1180           if ((rules != null) && (rules.size() > 0)) {
 1181               String bodyText = this.bodyText.toString();
 1182               Substitutor substitutor = getSubstitutor();
 1183               if (substitutor!= null) {
 1184                   bodyText = substitutor.substitute(bodyText);
 1185               }
 1186               for (int i = 0; i < rules.size(); i++) {
 1187                   try {
 1188                       Rule rule = (Rule) rules.get(i);
 1189                       if (debug) {
 1190                           log.debug("  Fire body() for " + rule);
 1191                       }
 1192                       rule.body(namespaceURI, name, bodyText);
 1193                   } catch (Exception e) {
 1194                       log.error("Body event threw exception", e);
 1195                       throw createSAXException(e);
 1196                   } catch (Error e) {
 1197                       log.error("Body event threw error", e);
 1198                       throw e;
 1199                   }
 1200               }
 1201           } else {
 1202               if (debug) {
 1203                   log.debug("  No rules found matching '" + match + "'.");
 1204               }
 1205           }
 1206   
 1207           // Recover the body text from the surrounding element
 1208           bodyText = (StringBuffer) bodyTexts.pop();
 1209           if (debug) {
 1210               log.debug("  Popping body text '" + bodyText.toString() + "'");
 1211           }
 1212   
 1213           // Fire "end" events for all relevant rules in reverse order
 1214           if (rules != null) {
 1215               for (int i = 0; i < rules.size(); i++) {
 1216                   int j = (rules.size() - i) - 1;
 1217                   try {
 1218                       Rule rule = (Rule) rules.get(j);
 1219                       if (debug) {
 1220                           log.debug("  Fire end() for " + rule);
 1221                       }
 1222                       rule.end(namespaceURI, name);
 1223                   } catch (Exception e) {
 1224                       log.error("End event threw exception", e);
 1225                       throw createSAXException(e);
 1226                   } catch (Error e) {
 1227                       log.error("End event threw error", e);
 1228                       throw e;
 1229                   }
 1230               }
 1231           }
 1232   
 1233           // Recover the previous match expression
 1234           int slash = match.lastIndexOf('/');
 1235           if (slash >= 0) {
 1236               match = match.substring(0, slash);
 1237           } else {
 1238               match = "";
 1239           }
 1240   
 1241       }
 1242   
 1243   
 1244       /**
 1245        * Process notification that a namespace prefix is going out of scope.
 1246        *
 1247        * @param prefix Prefix that is going out of scope
 1248        *
 1249        * @exception SAXException if a parsing error is to be reported
 1250        */
 1251       public void endPrefixMapping(String prefix) throws SAXException {
 1252   
 1253           if (saxLog.isDebugEnabled()) {
 1254               saxLog.debug("endPrefixMapping(" + prefix + ")");
 1255           }
 1256   
 1257           // Deregister this prefix mapping
 1258           ArrayStack stack = (ArrayStack) namespaces.get(prefix);
 1259           if (stack == null) {
 1260               return;
 1261           }
 1262           try {
 1263               stack.pop();
 1264               if (stack.empty())
 1265                   namespaces.remove(prefix);
 1266           } catch (EmptyStackException e) {
 1267               throw createSAXException("endPrefixMapping popped too many times");
 1268           }
 1269   
 1270       }
 1271   
 1272   
 1273       /**
 1274        * Process notification of ignorable whitespace received from the body of
 1275        * an XML element.
 1276        *
 1277        * @param buffer The characters from the XML document
 1278        * @param start Starting offset into the buffer
 1279        * @param len Number of characters from the buffer
 1280        *
 1281        * @exception SAXException if a parsing error is to be reported
 1282        */
 1283       public void ignorableWhitespace(char buffer[], int start, int len)
 1284               throws SAXException {
 1285   
 1286           if (saxLog.isDebugEnabled()) {
 1287               saxLog.debug("ignorableWhitespace(" +
 1288                       new String(buffer, start, len) + ")");
 1289           }
 1290   
 1291           ;   // No processing required
 1292   
 1293       }
 1294   
 1295   
 1296       /**
 1297        * Process notification of a processing instruction that was encountered.
 1298        *
 1299        * @param target The processing instruction target
 1300        * @param data The processing instruction data (if any)
 1301        *
 1302        * @exception SAXException if a parsing error is to be reported
 1303        */
 1304       public void processingInstruction(String target, String data)
 1305               throws SAXException {
 1306   
 1307           if (customContentHandler != null) {
 1308               // forward calls instead of handling them here
 1309               customContentHandler.processingInstruction(target, data);
 1310               return;
 1311           }
 1312   
 1313           if (saxLog.isDebugEnabled()) {
 1314               saxLog.debug("processingInstruction('" + target + "','" + data + "')");
 1315           }
 1316   
 1317           ;   // No processing is required
 1318   
 1319       }
 1320   
 1321   
 1322       /**
 1323        * Gets the document locator associated with our parser.
 1324        *
 1325        * @return the Locator supplied by the document parser
 1326        */
 1327       public Locator getDocumentLocator() {
 1328   
 1329           return locator;
 1330   
 1331       }
 1332   
 1333       /**
 1334        * Sets the document locator associated with our parser.
 1335        *
 1336        * @param locator The new locator
 1337        */
 1338       public void setDocumentLocator(Locator locator) {
 1339   
 1340           if (saxLog.isDebugEnabled()) {
 1341               saxLog.debug("setDocumentLocator(" + locator + ")");
 1342           }
 1343   
 1344           this.locator = locator;
 1345   
 1346       }
 1347   
 1348   
 1349       /**
 1350        * Process notification of a skipped entity.
 1351        *
 1352        * @param name Name of the skipped entity
 1353        *
 1354        * @exception SAXException if a parsing error is to be reported
 1355        */
 1356       public void skippedEntity(String name) throws SAXException {
 1357   
 1358           if (saxLog.isDebugEnabled()) {
 1359               saxLog.debug("skippedEntity(" + name + ")");
 1360           }
 1361   
 1362           ; // No processing required
 1363   
 1364       }
 1365   
 1366   
 1367       /**
 1368        * Process notification of the beginning of the document being reached.
 1369        *
 1370        * @exception SAXException if a parsing error is to be reported
 1371        */
 1372       public void startDocument() throws SAXException {
 1373   
 1374           if (saxLog.isDebugEnabled()) {
 1375               saxLog.debug("startDocument()");
 1376           }
 1377   
 1378           // ensure that the digester is properly configured, as 
 1379           // the digester could be used as a SAX ContentHandler
 1380           // rather than via the parse() methods.
 1381           configure();
 1382       }
 1383   
 1384   
 1385       /**
 1386        * Process notification of the start of an XML element being reached.
 1387        *
 1388        * @param namespaceURI The Namespace URI, or the empty string if the element
 1389        *   has no Namespace URI or if Namespace processing is not being performed.
 1390        * @param localName The local name (without prefix), or the empty
 1391        *   string if Namespace processing is not being performed.
 1392        * @param qName The qualified name (with prefix), or the empty
 1393        *   string if qualified names are not available.\
 1394        * @param list The attributes attached to the element. If there are
 1395        *   no attributes, it shall be an empty Attributes object. 
 1396        * @exception SAXException if a parsing error is to be reported
 1397        */
 1398       public void startElement(String namespaceURI, String localName,
 1399                                String qName, Attributes list)
 1400               throws SAXException {
 1401           boolean debug = log.isDebugEnabled();
 1402           
 1403           if (customContentHandler != null) {
 1404               // forward calls instead of handling them here
 1405               customContentHandler.startElement(namespaceURI, localName, qName, list);
 1406               return;
 1407           }
 1408   
 1409           if (saxLog.isDebugEnabled()) {
 1410               saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
 1411                       qName + ")");
 1412           }
 1413           
 1414           // Save the body text accumulated for our surrounding element
 1415           bodyTexts.push(bodyText);
 1416           if (debug) {
 1417               log.debug("  Pushing body text '" + bodyText.toString() + "'");
 1418           }
 1419           bodyText = new StringBuffer();
 1420   
 1421           // the actual element name is either in localName or qName, depending 
 1422           // on whether the parser is namespace aware
 1423           String name = localName;
 1424           if ((name == null) || (name.length() < 1)) {
 1425               name = qName;
 1426           }
 1427   
 1428           // Compute the current matching rule
 1429           StringBuffer sb = new StringBuffer(match);
 1430           if (match.length() > 0) {
 1431               sb.append('/');
 1432           }
 1433           sb.append(name);
 1434           match = sb.toString();
 1435           if (debug) {
 1436               log.debug("  New match='" + match + "'");
 1437           }
 1438   
 1439           // Fire "begin" events for all relevant rules
 1440           List rules = getRules().match(namespaceURI, match);
 1441           matches.push(rules);
 1442           if ((rules != null) && (rules.size() > 0)) {
 1443               Substitutor substitutor = getSubstitutor();
 1444               if (substitutor!= null) {
 1445                   list = substitutor.substitute(list);
 1446               }
 1447               for (int i = 0; i < rules.size(); i++) {
 1448                   try {
 1449                       Rule rule = (Rule) rules.get(i);
 1450                       if (debug) {
 1451                           log.debug("  Fire begin() for " + rule);
 1452                       }
 1453                       rule.begin(namespaceURI, name, list);
 1454                   } catch (Exception e) {
 1455                       log.error("Begin event threw exception", e);
 1456                       throw createSAXException(e);
 1457                   } catch (Error e) {
 1458                       log.error("Begin event threw error", e);
 1459                       throw e;
 1460                   }
 1461               }
 1462           } else {
 1463               if (debug) {
 1464                   log.debug("  No rules found matching '" + match + "'.");
 1465               }
 1466           }
 1467   
 1468       }
 1469   
 1470   
 1471       /**
 1472        * Process notification that a namespace prefix is coming in to scope.
 1473        *
 1474        * @param prefix Prefix that is being declared
 1475        * @param namespaceURI Corresponding namespace URI being mapped to
 1476        *
 1477        * @exception SAXException if a parsing error is to be reported
 1478        */
 1479       public void startPrefixMapping(String prefix, String namespaceURI)
 1480               throws SAXException {
 1481   
 1482           if (saxLog.isDebugEnabled()) {
 1483               saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
 1484           }
 1485   
 1486           // Register this prefix mapping
 1487           ArrayStack stack = (ArrayStack) namespaces.get(prefix);
 1488           if (stack == null) {
 1489               stack = new ArrayStack();
 1490               namespaces.put(prefix, stack);
 1491           }
 1492           stack.push(namespaceURI);
 1493   
 1494       }
 1495   
 1496   
 1497       // ----------------------------------------------------- DTDHandler Methods
 1498   
 1499   
 1500       /**
 1501        * Receive notification of a notation declaration event.
 1502        *
 1503        * @param name The notation name
 1504        * @param publicId The public identifier (if any)
 1505        * @param systemId The system identifier (if any)
 1506        */
 1507       public void notationDecl(String name, String publicId, String systemId) {
 1508   
 1509           if (saxLog.isDebugEnabled()) {
 1510               saxLog.debug("notationDecl(" + name + "," + publicId + "," +
 1511                       systemId + ")");
 1512           }
 1513   
 1514       }
 1515   
 1516   
 1517       /**
 1518        * Receive notification of an unparsed entity declaration event.
 1519        *
 1520        * @param name The unparsed entity name
 1521        * @param publicId The public identifier (if any)
 1522        * @param systemId The system identifier (if any)
 1523        * @param notation The name of the associated notation
 1524        */
 1525       public void unparsedEntityDecl(String name, String publicId,
 1526                                      String systemId, String notation) {
 1527   
 1528           if (saxLog.isDebugEnabled()) {
 1529               saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," +
 1530                       systemId + "," + notation + ")");
 1531           }
 1532   
 1533       }
 1534   
 1535   
 1536       // ----------------------------------------------- EntityResolver Methods
 1537   
 1538       /**
 1539        * Set the <code>EntityResolver</code> used by SAX when resolving
 1540        * public id and system id.
 1541        * This must be called before the first call to <code>parse()</code>.
 1542        * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
 1543        */
 1544       public void setEntityResolver(EntityResolver entityResolver){
 1545           this.entityResolver = entityResolver;
 1546       }
 1547       
 1548       
 1549       /**
 1550        * Return the Entity Resolver used by the SAX parser.
 1551        * @return Return the Entity Resolver used by the SAX parser.
 1552        */
 1553       public EntityResolver getEntityResolver(){
 1554           return entityResolver;
 1555       }
 1556   
 1557       /**
 1558        * Resolve the requested external entity.
 1559        *
 1560        * @param publicId The public identifier of the entity being referenced
 1561        * @param systemId The system identifier of the entity being referenced
 1562        *
 1563        * @exception SAXException if a parsing exception occurs
 1564        * 
 1565        */
 1566       public InputSource resolveEntity(String publicId, String systemId)
 1567               throws SAXException {     
 1568                   
 1569           if (saxLog.isDebugEnabled()) {
 1570               saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')");
 1571           }
 1572           
 1573           if (publicId != null)
 1574               this.publicId = publicId;
 1575                                          
 1576           // Has this system identifier been registered?
 1577           URL entityURL = null;
 1578           if (publicId != null) {
 1579               entityURL = (URL) entityValidator.get(publicId);
 1580           }
 1581            
 1582           // Redirect the schema location to a local destination
 1583           if (schemaLocation != null && entityURL == null && systemId != null){
 1584               entityURL = (URL) entityValidator.get(systemId);
 1585           } 
 1586   
 1587           if (entityURL == null) { 
 1588               if (systemId == null) {
 1589                   // cannot resolve
 1590                   if (log.isDebugEnabled()) {
 1591                       log.debug(" Cannot resolve entity: '" + entityURL + "'");
 1592                   }
 1593                   return (null);
 1594                   
 1595               } else {
 1596                   // try to resolve using system ID
 1597                   if (log.isDebugEnabled()) {
 1598                       log.debug(" Trying to resolve using system ID '" + systemId + "'");
 1599                   } 
 1600                   try {
 1601                       entityURL = new URL(systemId);
 1602                   } catch (MalformedURLException e) {
 1603                       throw new IllegalArgumentException("Malformed URL '" + systemId
 1604                           + "' : " + e.getMessage());
 1605                   }
 1606               }
 1607           }
 1608           
 1609           // Return an input source to our alternative URL
 1610           if (log.isDebugEnabled()) {
 1611               log.debug(" Resolving to alternate DTD '" + entityURL + "'");
 1612           }  
 1613           
 1614           try {
 1615               return createInputSourceFromURL(entityURL);
 1616           } catch (Exception e) {
 1617               throw createSAXException(e);
 1618           }
 1619       }
 1620   
 1621   
 1622       // ------------------------------------------------- ErrorHandler Methods
 1623   
 1624   
 1625       /**
 1626        * Forward notification of a parsing error to the application supplied
 1627        * error handler (if any).
 1628        *
 1629        * @param exception The error information
 1630        *
 1631        * @exception SAXException if a parsing exception occurs
 1632        */
 1633       public void error(SAXParseException exception) throws SAXException {
 1634   
 1635           log.error("Parse Error at line " + exception.getLineNumber() +
 1636                   " column " + exception.getColumnNumber() + ": " +
 1637                   exception.getMessage(), exception);
 1638           if (errorHandler != null) {
 1639               errorHandler.error(exception);
 1640           }
 1641   
 1642       }
 1643   
 1644   
 1645       /**
 1646        * Forward notification of a fatal parsing error to the application
 1647        * supplied error handler (if any).
 1648        *
 1649        * @param exception The fatal error information
 1650        *
 1651        * @exception SAXException if a parsing exception occurs
 1652        */
 1653       public void fatalError(SAXParseException exception) throws SAXException {
 1654   
 1655           log.error("Parse Fatal Error at line " + exception.getLineNumber() +
 1656                   " column " + exception.getColumnNumber() + ": " +
 1657                   exception.getMessage(), exception);
 1658           if (errorHandler != null) {
 1659               errorHandler.fatalError(exception);
 1660           }
 1661   
 1662       }
 1663   
 1664   
 1665       /**
 1666        * Forward notification of a parse warning to the application supplied
 1667        * error handler (if any).
 1668        *
 1669        * @param exception The warning information
 1670        *
 1671        * @exception SAXException if a parsing exception occurs
 1672        */
 1673       public void warning(SAXParseException exception) throws SAXException {
 1674            if (errorHandler != null) {
 1675               log.warn("Parse Warning Error at line " + exception.getLineNumber() +
 1676                   " column " + exception.getColumnNumber() + ": " +
 1677                   exception.getMessage(), exception);
 1678               
 1679               errorHandler.warning(exception);
 1680           }
 1681   
 1682       }
 1683   
 1684   
 1685       // ------------------------------------------------------- Public Methods
 1686   
 1687   
 1688       /**
 1689        * Log a message to our associated logger.
 1690        *
 1691        * @param message The message to be logged
 1692        * @deprecated Call getLogger() and use it's logging methods
 1693        */
 1694       public void log(String message) {
 1695   
 1696           log.info(message);
 1697   
 1698       }
 1699   
 1700   
 1701       /**
 1702        * Log a message and exception to our associated logger.
 1703        *
 1704        * @param message The message to be logged
 1705        * @deprecated Call getLogger() and use it's logging methods
 1706        */
 1707       public void log(String message, Throwable exception) {
 1708   
 1709           log.error(message, exception);
 1710   
 1711       }
 1712   
 1713   
 1714       /**
 1715        * Parse the content of the specified file using this Digester.  Returns
 1716        * the root element from the object stack (if any).
 1717        *
 1718        * @param file File containing the XML data to be parsed
 1719        *
 1720        * @exception IOException if an input/output error occurs
 1721        * @exception SAXException if a parsing exception occurs
 1722        */
 1723       public Object parse(File file) throws IOException, SAXException {
 1724   
 1725           configure();
 1726           InputSource input = new InputSource(new FileInputStream(file));
 1727           input.setSystemId(file.toURL().toString());
 1728           getXMLReader().parse(input);
 1729           cleanup();
 1730           return (root);
 1731   
 1732       }   
 1733       /**
 1734        * Parse the content of the specified input source using this Digester.
 1735        * Returns the root element from the object stack (if any).
 1736        *
 1737        * @param input Input source containing the XML data to be parsed
 1738        *
 1739        * @exception IOException if an input/output error occurs
 1740        * @exception SAXException if a parsing exception occurs
 1741        */
 1742       public Object parse(InputSource input) throws IOException, SAXException {
 1743    
 1744           configure();
 1745           getXMLReader().parse(input);
 1746           cleanup();
 1747           return (root);
 1748   
 1749       }
 1750   
 1751   
 1752       /**
 1753        * Parse the content of the specified input stream using this Digester.
 1754        * Returns the root element from the object stack (if any).
 1755        *
 1756        * @param input Input stream containing the XML data to be parsed
 1757        *
 1758        * @exception IOException if an input/output error occurs
 1759        * @exception SAXException if a parsing exception occurs
 1760        */
 1761       public Object parse(InputStream input) throws IOException, SAXException {
 1762   
 1763           configure();
 1764           InputSource is = new InputSource(input);
 1765           getXMLReader().parse(is);
 1766           cleanup();
 1767           return (root);
 1768   
 1769       }
 1770   
 1771   
 1772       /**
 1773        * Parse the content of the specified reader using this Digester.
 1774        * Returns the root element from the object stack (if any).
 1775        *
 1776        * @param reader Reader containing the XML data to be parsed
 1777        *
 1778        * @exception IOException if an input/output error occurs
 1779        * @exception SAXException if a parsing exception occurs
 1780        */
 1781       public Object parse(Reader reader) throws IOException, SAXException {
 1782   
 1783           configure();
 1784           InputSource is = new InputSource(reader);
 1785           getXMLReader().parse(is);
 1786           cleanup();
 1787           return (root);
 1788   
 1789       }
 1790   
 1791   
 1792       /**
 1793        * Parse the content of the specified URI using this Digester.
 1794        * Returns the root element from the object stack (if any).
 1795        *
 1796        * @param uri URI containing the XML data to be parsed
 1797        *
 1798        * @exception IOException if an input/output error occurs
 1799        * @exception SAXException if a parsing exception occurs
 1800        */
 1801       public Object parse(String uri) throws IOException, SAXException {
 1802   
 1803           configure();
 1804           InputSource is = createInputSourceFromURL(uri);
 1805           getXMLReader().parse(is);
 1806           cleanup();
 1807           return (root);
 1808   
 1809       }
 1810   
 1811   
 1812       /**
 1813        * Parse the content of the specified URL using this Digester.
 1814        * Returns the root element from the object stack (if any).
 1815        *
 1816        * @param urL URL containing the XML data to be parsed
 1817        *
 1818        * @exception IOException if an input/output error occurs
 1819        * @exception SAXException if a parsing exception occurs
 1820        *
 1821        * @since 1.8
 1822        */
 1823       public Object parse(URL url) throws IOException, SAXException {
 1824   
 1825           configure();
 1826           InputSource is = createInputSourceFromURL(url);
 1827           getXMLReader().parse(is);
 1828           cleanup();
 1829           return (root);
 1830   
 1831       }
 1832   
 1833   
 1834       /**
 1835        * <p>Register the specified DTD URL for the specified public identifier.
 1836        * This must be called before the first call to <code>parse()</code>.
 1837        * </p><p>
 1838        * <code>Digester</code> contains an internal <code>EntityResolver</code>
 1839        * implementation. This maps <code>PUBLICID</code>'s to URLs 
 1840        * (from which the resource will be loaded). A common use case for this
 1841        * method is to register local URLs (possibly computed at runtime by a 
 1842        * classloader) for DTDs. This allows the performance advantage of using
 1843        * a local version without having to ensure every <code>SYSTEM</code>
 1844        * URI on every processed xml document is local. This implementation provides
 1845        * only basic functionality. If more sophisticated features are required,
 1846        * using {@link #setEntityResolver} to set a custom resolver is recommended.
 1847        * </p><p>
 1848        * <strong>Note:</strong> This method will have no effect when a custom 
 1849        * <code>EntityResolver</code> has been set. (Setting a custom 
 1850        * <code>EntityResolver</code> overrides the internal implementation.) 
 1851        * </p>
 1852        * @param publicId Public identifier of the DTD to be resolved
 1853        * @param entityURL The URL to use for reading this DTD
 1854        *
 1855        * @since 1.8
 1856        */
 1857       public void register(String publicId, URL entityURL) {
 1858   
 1859           if (log.isDebugEnabled()) {
 1860               log.debug("register('" + publicId + "', '" + entityURL + "'");
 1861           }
 1862           entityValidator.put(publicId, entityURL);
 1863   
 1864       }
 1865   
 1866   
 1867       /**
 1868        * <p>Convenience method that registers the string version of an entity URL
 1869        * instead of a URL version.</p>
 1870        *
 1871        * @param publicId Public identifier of the entity to be resolved
 1872        * @param entityURL The URL to use for reading this entity
 1873        */
 1874       public void register(String publicId, String entityURL) {
 1875   
 1876           if (log.isDebugEnabled()) {
 1877               log.debug("register('" + publicId + "', '" + entityURL + "'");
 1878           }
 1879           try {
 1880               entityValidator.put(publicId, new URL(entityURL));
 1881           } catch (MalformedURLException e) {
 1882               throw new IllegalArgumentException("Malformed URL '" + entityURL
 1883                   + "' : " + e.getMessage());
 1884           }
 1885   
 1886       }
 1887   
 1888   
 1889       /**
 1890        * <p><code>List</code> of <code>InputSource</code> instances
 1891        * created by a <code>createInputSourceFromURL()</code> method
 1892        * call.  These represent open input streams that need to be
 1893        * closed to avoid resource leaks, as well as potentially locked
 1894        * JAR files on Windows.</p>
 1895        */
 1896       protected List inputSources = new ArrayList(5);
 1897   
 1898   
 1899       /**
 1900        * Given a URL, return an InputSource that reads from that URL.
 1901        * <p>
 1902        * Ideally this function would not be needed and code could just use
 1903        * <code>new InputSource(entityURL)</code>. Unfortunately it appears
 1904        * that when the entityURL points to a file within a jar archive a
 1905        * caching mechanism inside the InputSource implementation causes a
 1906        * file-handle to the jar file to remain open. On Windows systems
 1907        * this then causes the jar archive file to be locked on disk
 1908        * ("in use") which makes it impossible to delete the jar file -
 1909        * and that really stuffs up "undeploy" in webapps in particular.
 1910        * <p>
 1911        * In JDK1.4 and later, Apache XercesJ is used as the xml parser.
 1912        * The InputSource object provided is converted into an XMLInputSource,
 1913        * and eventually passed to an instance of XMLDocumentScannerImpl to
 1914        * specify the source data to be converted into tokens for the rest
 1915        * of the XMLReader code to handle. XMLDocumentScannerImpl calls
 1916        * fEntityManager.startDocumentEntity(source), where fEntityManager
 1917        * is declared in ancestor class XMLScanner to be an XMLEntityManager. In
 1918        * that class, if the input source stream is null, then:
 1919        * <pre>
 1920        *  URL location = new URL(expandedSystemId);
 1921        *  URLConnection connect = location.openConnection();
 1922        *  if (connect instanceof HttpURLConnection) {
 1923        *    setHttpProperties(connect,xmlInputSource);
 1924        *  }
 1925        *  stream = connect.getInputStream();
 1926        * </pre>
 1927        * This method pretty much duplicates the standard behaviour, except
 1928        * that it calls URLConnection.setUseCaches(false) before opening
 1929        * the connection.
 1930        *
 1931        * @since 1.8
 1932        */
 1933       public InputSource createInputSourceFromURL(URL url)
 1934         throws MalformedURLException, IOException {
 1935   
 1936           URLConnection connection = url.openConnection();
 1937           connection.setUseCaches(false);
 1938           InputStream stream = connection.getInputStream();
 1939           InputSource source = new InputSource(stream);
 1940           source.setSystemId(url.toExternalForm());
 1941           inputSources.add(source);
 1942           return source;
 1943   
 1944       }
 1945   
 1946   
 1947       /**
 1948        * <p>Convenience method that creates an <code>InputSource</code>
 1949        * from the string version of a URL.</p>
 1950        *
 1951        * @param url URL for which to create an <code>InputSource</code>
 1952        *
 1953        * @since 1.8
 1954        */
 1955       public InputSource createInputSourceFromURL(String url)
 1956         throws MalformedURLException, IOException {
 1957   
 1958           return createInputSourceFromURL(new URL(url));
 1959   
 1960       }
 1961   
 1962   
 1963       // --------------------------------------------------------- Rule Methods
 1964   
 1965   
 1966       /**
 1967        * <p>Register a new Rule matching the specified pattern.
 1968        * This method sets the <code>Digester</code> property on the rule.</p>
 1969        *
 1970        * @param pattern Element matching pattern
 1971        * @param rule Rule to be registered
 1972        */
 1973       public void addRule(String pattern, Rule rule) {
 1974   
 1975           rule.setDigester(this);
 1976           getRules().add(pattern, rule);
 1977   
 1978       }
 1979   
 1980   
 1981       /**
 1982        * Register a set of Rule instances defined in a RuleSet.
 1983        *
 1984        * @param ruleSet The RuleSet instance to configure from
 1985        */
 1986       public void addRuleSet(RuleSet ruleSet) {
 1987   
 1988           String oldNamespaceURI = getRuleNamespaceURI();
 1989           String newNamespaceURI = ruleSet.getNamespaceURI();
 1990           if (log.isDebugEnabled()) {
 1991               if (newNamespaceURI == null) {
 1992                   log.debug("addRuleSet() with no namespace URI");
 1993               } else {
 1994                   log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
 1995               }
 1996           }
 1997           setRuleNamespaceURI(newNamespaceURI);
 1998           ruleSet.addRuleInstances(this);
 1999           setRuleNamespaceURI(oldNamespaceURI);
 2000   
 2001       }
 2002   
 2003   
 2004       /**
 2005        * Add a "bean property setter" rule for the specified parameters.
 2006        *
 2007        * @param pattern Element matching pattern
 2008        * @see BeanPropertySetterRule
 2009        */
 2010       public void addBeanPropertySetter(String pattern) {
 2011   
 2012           addRule(pattern,
 2013                   new BeanPropertySetterRule());
 2014   
 2015       }
 2016   
 2017   
 2018       /**
 2019        * Add a "bean property setter" rule for the specified parameters.
 2020        *
 2021        * @param pattern Element matching pattern
 2022        * @param propertyName Name of property to set
 2023        * @see BeanPropertySetterRule
 2024        */
 2025       public void addBeanPropertySetter(String pattern,
 2026                                         String propertyName) {
 2027   
 2028           addRule(pattern,
 2029                   new BeanPropertySetterRule(propertyName));
 2030   
 2031       }
 2032   
 2033       /**
 2034        * Add an "call method" rule for a method which accepts no arguments.
 2035        *
 2036        * @param pattern Element matching pattern
 2037        * @param methodName Method name to be called
 2038        * @see CallMethodRule
 2039        */
 2040       public void addCallMethod(String pattern, String methodName) {
 2041   
 2042           addRule(
 2043                   pattern,
 2044                   new CallMethodRule(methodName));
 2045   
 2046       }
 2047   
 2048       /**
 2049        * Add an "call method" rule for the specified parameters.
 2050        *
 2051        * @param pattern Element matching pattern
 2052        * @param methodName Method name to be called
 2053        * @param paramCount Number of expected parameters (or zero
 2054        *  for a single parameter from the body of this element)
 2055        * @see CallMethodRule
 2056        */
 2057       public void addCallMethod(String pattern, String methodName,
 2058                                 int paramCount) {
 2059   
 2060           addRule(pattern,
 2061                   new CallMethodRule(methodName, paramCount));
 2062   
 2063       }
 2064   
 2065   
 2066       /**
 2067        * Add an "call method" rule for the specified parameters.
 2068        * If <code>paramCount</code> is set to zero the rule will use
 2069        * the body of the matched element as the single argument of the
 2070        * method, unless <code>paramTypes</code> is null or empty, in this
 2071        * case the rule will call the specified method with no arguments.
 2072        *
 2073        * @param pattern Element matching pattern
 2074        * @param methodName Method name to be called
 2075        * @param paramCount Number of expected parameters (or zero
 2076        *  for a single parameter from the body of this element)
 2077        * @param paramTypes Set of Java class names for the types
 2078        *  of the expected parameters
 2079        *  (if you wish to use a primitive type, specify the corresonding
 2080        *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
 2081        *  for a <code>boolean</code> parameter)
 2082        * @see CallMethodRule
 2083        */
 2084       public void addCallMethod(String pattern, String methodName,
 2085                                 int paramCount, String paramTypes[]) {
 2086   
 2087           addRule(pattern,
 2088                   new CallMethodRule(
 2089                                       methodName,
 2090                                       paramCount, 
 2091                                       paramTypes));
 2092   
 2093       }
 2094   
 2095   
 2096       /**
 2097        * Add an "call method" rule for the specified parameters.
 2098        * If <code>paramCount</code> is set to zero the rule will use
 2099        * the body of the matched element as the single argument of the
 2100        * method, unless <code>paramTypes</code> is null or empty, in this
 2101        * case the rule will call the specified method with no arguments.
 2102        *
 2103        * @param pattern Element matching pattern
 2104        * @param methodName Method name to be called
 2105        * @param paramCount Number of expected parameters (or zero
 2106        *  for a single parameter from the body of this element)
 2107        * @param paramTypes The Java class names of the arguments
 2108        *  (if you wish to use a primitive type, specify the corresonding
 2109        *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
 2110        *  for a <code>boolean</code> parameter)
 2111        * @see CallMethodRule
 2112        */
 2113       public void addCallMethod(String pattern, String methodName,
 2114                                 int paramCount, Class paramTypes[]) {
 2115   
 2116           addRule(pattern,
 2117                   new CallMethodRule(
 2118                                       methodName,
 2119                                       paramCount, 
 2120                                       paramTypes));
 2121   
 2122       }
 2123   
 2124   
 2125       /**
 2126        * Add a "call parameter" rule for the specified parameters.
 2127        *
 2128        * @param pattern Element matching pattern
 2129        * @param paramIndex Zero-relative parameter index to set
 2130        *  (from the body of this element)
 2131        * @see CallParamRule
 2132        */
 2133       public void addCallParam(String pattern, int paramIndex) {
 2134   
 2135           addRule(pattern,
 2136                   new CallParamRule(paramIndex));
 2137   
 2138       }
 2139   
 2140   
 2141       /**
 2142        * Add a "call parameter" rule for the specified parameters.
 2143        *
 2144        * @param pattern Element matching pattern
 2145        * @param paramIndex Zero-relative parameter index to set
 2146        *  (from the specified attribute)
 2147        * @param attributeName Attribute whose value is used as the
 2148        *  parameter value
 2149        * @see CallParamRule
 2150        */
 2151       public void addCallParam(String pattern, int paramIndex,
 2152                                String attributeName) {
 2153   
 2154           addRule(pattern,
 2155                   new CallParamRule(paramIndex, attributeName));
 2156   
 2157       }
 2158   
 2159   
 2160       /**
 2161        * Add a "call parameter" rule.
 2162        * This will either take a parameter from the stack 
 2163        * or from the current element body text. 
 2164        *
 2165        * @param paramIndex The zero-relative parameter number
 2166        * @param fromStack Should the call parameter be taken from the top of the stack?
 2167        * @see CallParamRule
 2168        */    
 2169       public void addCallParam(String pattern, int paramIndex, boolean fromStack) {
 2170       
 2171           addRule(pattern,
 2172                   new CallParamRule(paramIndex, fromStack));
 2173         
 2174       }
 2175   
 2176       /**
 2177        * Add a "call parameter" rule that sets a parameter from the stack.
 2178        * This takes a parameter from the given position on the stack.
 2179        *
 2180        * @param paramIndex The zero-relative parameter number
 2181        * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
 2182        * where 0 is the top of the stack, 1 the next element down and so on
 2183        * @see CallMethodRule
 2184        */    
 2185       public void addCallParam(String pattern, int paramIndex, int stackIndex) {
 2186       
 2187           addRule(pattern,
 2188                   new CallParamRule(paramIndex, stackIndex));
 2189         
 2190       }
 2191       
 2192       /**
 2193        * Add a "call parameter" rule that sets a parameter from the current 
 2194        * <code>Digester</code> matching path.
 2195        * This is sometimes useful when using rules that support wildcards.
 2196        *
 2197        * @param pattern the pattern that this rule should match
 2198        * @param paramIndex The zero-relative parameter number
 2199        * @see CallMethodRule
 2200        */
 2201       public void addCallParamPath(String pattern,int paramIndex) {
 2202           addRule(pattern, new PathCallParamRule(paramIndex));
 2203       }
 2204       
 2205       /**
 2206        * Add a "call parameter" rule that sets a parameter from a 
 2207        * caller-provided object. This can be used to pass constants such as
 2208        * strings to methods; it can also be used to pass mutable objects,
 2209        * providing ways for objects to do things like "register" themselves
 2210        * with some shared object.
 2211        * <p>
 2212        * Note that when attempting to locate a matching method to invoke,
 2213        * the true type of the paramObj is used, so that despite the paramObj
 2214        * being passed in here as type Object, the target method can declare
 2215        * its parameters as being the true type of the object (or some ancestor
 2216        * type, according to the usual type-conversion rules).
 2217        *
 2218        * @param paramIndex The zero-relative parameter number
 2219        * @param paramObj Any arbitrary object to be passed to the target
 2220        * method.
 2221        * @see CallMethodRule
 2222        *
 2223        * @since 1.6
 2224        */    
 2225       public void addObjectParam(String pattern, int paramIndex, 
 2226                                  Object paramObj) {
 2227       
 2228           addRule(pattern,
 2229                   new ObjectParamRule(paramIndex, paramObj));
 2230         
 2231       }
 2232       
 2233       /**
 2234        * Add a "factory create" rule for the specified parameters.
 2235        * Exceptions thrown during the object creation process will be propagated.
 2236        *
 2237        * @param pattern Element matching pattern
 2238        * @param className Java class name of the object creation factory class
 2239        * @see FactoryCreateRule
 2240        */
 2241       public void addFactoryCreate(String pattern, String className) {
 2242   
 2243           addFactoryCreate(pattern, className, false);
 2244   
 2245       }
 2246   
 2247   
 2248       /**
 2249        * Add a "factory create" rule for the specified parameters.
 2250        * Exceptions thrown during the object creation process will be propagated.
 2251        *
 2252        * @param pattern Element matching pattern
 2253        * @param clazz Java class of the object creation factory class
 2254        * @see FactoryCreateRule
 2255        */
 2256       public void addFactoryCreate(String pattern, Class clazz) {
 2257   
 2258           addFactoryCreate(pattern, clazz, false);
 2259   
 2260       }
 2261   
 2262   
 2263       /**
 2264        * Add a "factory create" rule for the specified parameters.
 2265        * Exceptions thrown during the object creation process will be propagated.
 2266        *
 2267        * @param pattern Element matching pattern
 2268        * @param className Java class name of the object creation factory class
 2269        * @param attributeName Attribute name which, if present, overrides the
 2270        *  value specified by <code>className</code>
 2271        * @see FactoryCreateRule
 2272        */
 2273       public void addFactoryCreate(String pattern, String className,
 2274                                    String attributeName) {
 2275   
 2276           addFactoryCreate(pattern, className, attributeName, false);
 2277   
 2278       }
 2279   
 2280   
 2281       /**
 2282        * Add a "factory create" rule for the specified parameters.
 2283        * Exceptions thrown during the object creation process will be propagated.
 2284        *
 2285        * @param pattern Element matching pattern
 2286        * @param clazz Java class of the object creation factory class
 2287        * @param attributeName Attribute name which, if present, overrides the
 2288        *  value specified by <code>className</code>
 2289        * @see FactoryCreateRule
 2290        */
 2291       public void addFactoryCreate(String pattern, Class clazz,
 2292                                    String attributeName) {
 2293   
 2294           addFactoryCreate(pattern, clazz, attributeName, false);
 2295   
 2296       }
 2297   
 2298   
 2299       /**
 2300        * Add a "factory create" rule for the specified parameters.
 2301        * Exceptions thrown during the object creation process will be propagated.
 2302        *
 2303        * @param pattern Element matching pattern
 2304        * @param creationFactory Previously instantiated ObjectCreationFactory
 2305        *  to be utilized
 2306        * @see FactoryCreateRule
 2307        */
 2308       public void addFactoryCreate(String pattern,
 2309                                    ObjectCreationFactory creationFactory) {
 2310   
 2311           addFactoryCreate(pattern, creationFactory, false);
 2312   
 2313       }
 2314   
 2315       /**
 2316        * Add a "factory create" rule for the specified parameters.
 2317        *
 2318        * @param pattern Element matching pattern
 2319        * @param className Java class name of the object creation factory class
 2320        * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
 2321        * object creation will be ignored.
 2322        * @see FactoryCreateRule
 2323        */
 2324       public void addFactoryCreate(
 2325                                       String pattern, 
 2326                                       String className,
 2327                                       boolean ignoreCreateExceptions) {
 2328   
 2329           addRule(
 2330                   pattern,
 2331                   new FactoryCreateRule(className, ignoreCreateExceptions));
 2332   
 2333       }
 2334   
 2335   
 2336       /**
 2337        * Add a "factory create" rule for the specified parameters.
 2338        *
 2339        * @param pattern Element matching pattern
 2340        * @param clazz Java class of the object creation factory class
 2341        * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
 2342        * object creation will be ignored.
 2343        * @see FactoryCreateRule
 2344        */
 2345       public void addFactoryCreate(
 2346                                       String pattern, 
 2347                                       Class clazz,
 2348                                       boolean ignoreCreateExceptions) {
 2349   
 2350           addRule(
 2351                   pattern,
 2352                   new FactoryCreateRule(clazz, ignoreCreateExceptions));
 2353   
 2354       }
 2355   
 2356   
 2357       /**
 2358        * Add a "factory create" rule for the specified parameters.
 2359        *
 2360        * @param pattern Element matching pattern
 2361        * @param className Java class name of the object creation factory class
 2362        * @param attributeName Attribute name which, if present, overrides the
 2363        *  value specified by <code>className</code>
 2364        * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
 2365        * object creation will be ignored.
 2366        * @see FactoryCreateRule
 2367        */
 2368       public void addFactoryCreate(
 2369                                   String pattern, 
 2370                                   String className,
 2371                                   String attributeName,
 2372                                   boolean ignoreCreateExceptions) {
 2373   
 2374           addRule(
 2375                   pattern,
 2376                   new FactoryCreateRule(className, attributeName, ignoreCreateExceptions));
 2377   
 2378       }
 2379   
 2380   
 2381       /**
 2382        * Add a "factory create" rule for the specified parameters.
 2383        *
 2384        * @param pattern Element matching pattern
 2385        * @param clazz Java class of the object creation factory class
 2386        * @param attributeName Attribute name which, if present, overrides the
 2387        *  value specified by <code>className</code>
 2388        * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
 2389        * object creation will be ignored.
 2390        * @see FactoryCreateRule
 2391        */
 2392       public void addFactoryCreate(
 2393                                       String pattern, 
 2394                                       Class clazz,
 2395                                       String attributeName,
 2396                                       boolean ignoreCreateExceptions) {
 2397   
 2398           addRule(
 2399                   pattern,
 2400                   new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions));
 2401   
 2402       }
 2403   
 2404   
 2405       /**
 2406        * Add a "factory create" rule for the specified parameters.
 2407        *
 2408        * @param pattern Element matching pattern
 2409        * @param creationFactory Previously instantiated ObjectCreationFactory
 2410        *  to be utilized
 2411        * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
 2412        * object creation will be ignored.
 2413        * @see FactoryCreateRule
 2414        */
 2415       public void addFactoryCreate(String pattern,
 2416                                    ObjectCreationFactory creationFactory,
 2417                                    boolean ignoreCreateExceptions) {
 2418   
 2419           creationFactory.setDigester(this);
 2420           addRule(pattern,
 2421                   new FactoryCreateRule(creationFactory, ignoreCreateExceptions));
 2422   
 2423       }
 2424   
 2425       /**
 2426        * Add an "object create" rule for the specified parameters.
 2427        *
 2428        * @param pattern Element matching pattern
 2429        * @param className Java class name to be created
 2430        * @see ObjectCreateRule
 2431        */
 2432       public void addObjectCreate(String pattern, String className) {
 2433   
 2434           addRule(pattern,
 2435                   new ObjectCreateRule(className));
 2436   
 2437       }
 2438   
 2439   
 2440       /**
 2441        * Add an "object create" rule for the specified parameters.
 2442        *
 2443        * @param pattern Element matching pattern
 2444        * @param clazz Java class to be created
 2445        * @see ObjectCreateRule
 2446        */
 2447       public void addObjectCreate(String pattern, Class clazz) {
 2448   
 2449           addRule(pattern,
 2450                   new ObjectCreateRule(clazz));
 2451   
 2452       }
 2453   
 2454   
 2455       /**
 2456        * Add an "object create" rule for the specified parameters.
 2457        *
 2458        * @param pattern Element matching pattern
 2459        * @param className Default Java class name to be created
 2460        * @param attributeName Attribute name that optionally overrides
 2461        *  the default Java class name to be created
 2462        * @see ObjectCreateRule
 2463        */
 2464       public void addObjectCreate(String pattern, String className,
 2465                                   String attributeName) {
 2466   
 2467           addRule(pattern,
 2468                   new ObjectCreateRule(className, attributeName));
 2469   
 2470       }
 2471   
 2472   
 2473       /**
 2474        * Add an "object create" rule for the specified parameters.
 2475        *
 2476        * @param pattern Element matching pattern
 2477        * @param attributeName Attribute name that optionally overrides
 2478        * @param clazz Default Java class to be created
 2479        *  the default Java class name to be created
 2480        * @see ObjectCreateRule
 2481        */
 2482       public void addObjectCreate(String pattern,
 2483                                   String attributeName,
 2484                                   Class clazz) {
 2485   
 2486           addRule(pattern,
 2487                   new ObjectCreateRule(attributeName, clazz));
 2488   
 2489       }
 2490   
 2491       /**
 2492        * Adds an {@link SetNestedPropertiesRule}.
 2493        *
 2494        * @param pattern register the rule with this pattern
 2495        *
 2496        * @since 1.6
 2497        */
 2498       public void addSetNestedProperties(String pattern) {
 2499       
 2500           addRule(pattern, new SetNestedPropertiesRule());
 2501       }
 2502   
 2503       /**
 2504        * Adds an {@link SetNestedPropertiesRule}.
 2505        *
 2506        * @param pattern register the rule with this pattern
 2507        * @param elementName elment name that a property maps to
 2508        * @param propertyName property name of the element mapped from
 2509        *
 2510        * @since 1.6
 2511        */
 2512       public void addSetNestedProperties(String pattern, String elementName, String propertyName) {
 2513       
 2514           addRule(pattern, new SetNestedPropertiesRule(elementName, propertyName));
 2515       }
 2516   
 2517       /**
 2518        * Adds an {@link SetNestedPropertiesRule}.
 2519        *
 2520        * @param pattern register the rule with this pattern
 2521        * @param elementNames elment names that (in order) map to properties
 2522        * @param propertyNames property names that (in order) elements are mapped to
 2523        *
 2524        * @since 1.6
 2525        */    
 2526       public void addSetNestedProperties(String pattern, String[] elementNames, String[] propertyNames) {
 2527       
 2528           addRule(pattern, new SetNestedPropertiesRule(elementNames, propertyNames));
 2529       }
 2530   
 2531   
 2532       /**
 2533        * Add a "set next" rule for the specified parameters.
 2534        *
 2535        * @param pattern Element matching pattern
 2536        * @param methodName Method name to call on the parent element
 2537        * @see SetNextRule
 2538        */
 2539       public void addSetNext(String pattern, String methodName) {
 2540   
 2541           addRule(pattern,
 2542                   new SetNextRule(methodName));
 2543   
 2544       }
 2545   
 2546   
 2547       /**
 2548        * Add a "set next" rule for the specified parameters.
 2549        *
 2550        * @param pattern Element matching pattern
 2551        * @param methodName Method name to call on the parent element
 2552        * @param paramType Java class name of the expected parameter type
 2553        *  (if you wish to use a primitive type, specify the corresonding
 2554        *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
 2555        *  for a <code>boolean</code> parameter)
 2556        * @see SetNextRule
 2557        */
 2558       public void addSetNext(String pattern, String methodName,
 2559                              String paramType) {
 2560   
 2561           addRule(pattern,
 2562                   new SetNextRule(methodName, paramType));
 2563   
 2564       }
 2565   
 2566   
 2567       /**
 2568        * Add {@link SetRootRule} with the specified parameters.
 2569        *
 2570        * @param pattern Element matching pattern
 2571        * @param methodName Method name to call on the root object
 2572        * @see SetRootRule
 2573        */
 2574       public void addSetRoot(String pattern, String methodName) {
 2575   
 2576           addRule(pattern,
 2577                   new SetRootRule(methodName));
 2578   
 2579       }
 2580   
 2581   
 2582       /**
 2583        * Add {@link SetRootRule} with the specified parameters.
 2584        *
 2585        * @param pattern Element matching pattern
 2586        * @param methodName Method name to call on the root object
 2587        * @param paramType Java class name of the expected parameter type
 2588        * @see SetRootRule
 2589        */
 2590       public void addSetRoot(String pattern, String methodName,
 2591                              String paramType) {
 2592   
 2593           addRule(pattern,
 2594                   new SetRootRule(methodName, paramType));
 2595   
 2596       }
 2597   
 2598       /**
 2599        * Add a "set properties" rule for the specified parameters.
 2600        *
 2601        * @param pattern Element matching pattern
 2602        * @see SetPropertiesRule
 2603        */
 2604       public void addSetProperties(String pattern) {
 2605   
 2606           addRule(pattern,
 2607                   new SetPropertiesRule());
 2608   
 2609       }
 2610   
 2611       /**
 2612        * Add a "set properties" rule with a single overridden parameter.
 2613        * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
 2614        *
 2615        * @param pattern Element matching pattern
 2616        * @param attributeName map this attribute
 2617        * @param propertyName to this property
 2618        * @see SetPropertiesRule
 2619        */
 2620       public void addSetProperties(
 2621                                   String pattern, 
 2622                                   String attributeName,
 2623                                   String propertyName) {
 2624   
 2625           addRule(pattern,
 2626                   new SetPropertiesRule(attributeName, propertyName));
 2627   
 2628       }
 2629   
 2630       /**
 2631        * Add a "set properties" rule with overridden parameters.
 2632        * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
 2633        *
 2634        * @param pattern Element matching pattern
 2635        * @param attributeNames names of attributes with custom mappings
 2636        * @param propertyNames property names these attributes map to
 2637        * @see SetPropertiesRule
 2638        */
 2639       public void addSetProperties(
 2640                                   String pattern, 
 2641                                   String [] attributeNames,
 2642                                   String [] propertyNames) {
 2643   
 2644           addRule(pattern,
 2645                   new SetPropertiesRule(attributeNames, propertyNames));
 2646   
 2647       }
 2648   
 2649   
 2650       /**
 2651        * Add a "set property" rule for the specified parameters.
 2652        *
 2653        * @param pattern Element matching pattern
 2654        * @param name Attribute name containing the property name to be set
 2655        * @param value Attribute name containing the property value to set
 2656        * @see SetPropertyRule
 2657        */
 2658       public void addSetProperty(String pattern, String name, String value) {
 2659   
 2660           addRule(pattern,
 2661                   new SetPropertyRule(name, value));
 2662   
 2663       }
 2664   
 2665   
 2666       /**
 2667        * Add a "set top" rule for the specified parameters.
 2668        *
 2669        * @param pattern Element matching pattern
 2670        * @param methodName Method name to call on the parent element
 2671        * @see SetTopRule
 2672        */
 2673       public void addSetTop(String pattern, String methodName) {
 2674   
 2675           addRule(pattern,
 2676                   new SetTopRule(methodName));
 2677   
 2678       }
 2679   
 2680   
 2681       /**
 2682        * Add a "set top" rule for the specified parameters.
 2683        *
 2684        * @param pattern Element matching pattern
 2685        * @param methodName Method name to call on the parent element
 2686        * @param paramType Java class name of the expected parameter type
 2687        *  (if