001package org.apache.commons.digester3;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import static java.lang.String.format;
023
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.Reader;
029import java.lang.reflect.InvocationTargetException;
030import java.net.MalformedURLException;
031import java.net.URL;
032import java.net.URLConnection;
033import java.util.ArrayList;
034import java.util.Collections;
035import java.util.EmptyStackException;
036import java.util.HashMap;
037import java.util.List;
038import java.util.Map;
039import java.util.Stack;
040import java.util.concurrent.Callable;
041import java.util.concurrent.ExecutorService;
042import java.util.concurrent.Future;
043
044import javax.xml.parsers.ParserConfigurationException;
045import javax.xml.parsers.SAXParser;
046import javax.xml.parsers.SAXParserFactory;
047import javax.xml.validation.Schema;
048
049import org.apache.commons.logging.Log;
050import org.apache.commons.logging.LogFactory;
051import org.xml.sax.Attributes;
052import org.xml.sax.ContentHandler;
053import org.xml.sax.EntityResolver;
054import org.xml.sax.ErrorHandler;
055import org.xml.sax.InputSource;
056import org.xml.sax.Locator;
057import org.xml.sax.SAXException;
058import org.xml.sax.SAXNotRecognizedException;
059import org.xml.sax.SAXNotSupportedException;
060import org.xml.sax.SAXParseException;
061import org.xml.sax.XMLReader;
062import org.xml.sax.helpers.DefaultHandler;
063
064/**
065 * <p>
066 * A <strong>Digester</strong> processes an XML input stream by matching a series of element nesting patterns to execute
067 * Rules that have been added prior to the start of parsing.
068 * </p>
069 * <p>
070 * See the <a href="package-summary.html#package_description">Digester Developer Guide</a> for more information.
071 * </p>
072 * <p>
073 * <strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may only be used within the context of a single
074 * thread at a time, and a call to <code>parse()</code> must be completed before another can be initiated even from the
075 * same thread.
076 * </p>
077 * <p>
078 * A Digester instance should not be used for parsing more than one input document. The problem is that the Digester
079 * class has quite a few member variables whose values "evolve" as SAX events are received during a parse. When reusing
080 * the Digester instance, all these members must be reset back to their initial states before the second parse begins.
081 * The "clear()" method makes a stab at resetting these, but it is actually rather a difficult problem. If you are
082 * determined to reuse Digester instances, then at the least you should call the clear() method before each parse, and
083 * must call it if the Digester parse terminates due to an exception during a parse.
084 * </p>
085 * <p>
086 * <strong>LEGACY IMPLEMENTATION NOTE</strong> - When using the legacy XML schema support (instead of using the
087 * {@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
088 * make this class work with the legacy XML schema support.
089 * </p>
090 * <p>
091 * This package was inspired by the <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1, but is organized
092 * somewhat differently.
093 * </p>
094 */
095public class Digester
096    extends DefaultHandler
097{
098
099    // --------------------------------------------------------- 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}