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

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » util » digester » [javadoc | source]