View Javadoc

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