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

Save This Page
Home » commons-digester-2.0-src » org.apache.commons » digester » [javadoc | source]