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     *
779     * @param validating The new validating parser flag.
780     */
781    public void setValidating( boolean validating )
782    {
783        this.validating = validating;
784    }
785
786    /**
787     * Return the XMLReader to be used for parsing the input document.
788     *
789     * FIXME: there is a bug in JAXP/XERCES that prevent the use of a parser that contains a schema with a DTD.
790     *
791     * @return the XMLReader to be used for parsing the input document.
792     * @exception SAXException if no XMLReader can be instantiated
793     */
794    public XMLReader getXMLReader()
795        throws SAXException
796    {
797        if ( reader == null )
798        {
799            reader = getParser().getXMLReader();
800        }
801
802        reader.setDTDHandler( this );
803        reader.setContentHandler( this );
804
805        if ( entityResolver == null )
806        {
807            reader.setEntityResolver( this );
808        }
809        else
810        {
811            reader.setEntityResolver( entityResolver );
812        }
813
814        reader.setErrorHandler( this );
815        return reader;
816    }
817
818    /**
819     * Gets the <code>Substitutor</code> used to convert attributes and body text.
820     *
821     * @return the <code>Substitutor</code> used to convert attributes and body text,
822     *         null if not substitutions are to be performed.
823     */
824    public Substitutor getSubstitutor()
825    {
826        return substitutor;
827    }
828
829    /**
830     * Sets the <code>Substitutor</code> to be used to convert attributes and body text.
831     *
832     * @param substitutor the Substitutor to be used to convert attributes and body text or null if not substitution of
833     *            these values is to be performed.
834     */
835    public void setSubstitutor( Substitutor substitutor )
836    {
837        this.substitutor = substitutor;
838    }
839
840    /**
841     * returns the custom SAX ContentHandler where events are redirected.
842     *
843     * @return the custom SAX ContentHandler where events are redirected.
844     * @see #setCustomContentHandler(ContentHandler)
845     * @since 1.7
846     */
847    public ContentHandler getCustomContentHandler()
848    {
849        return customContentHandler;
850    }
851
852    /**
853     * Redirects (or cancels redirecting) of SAX ContentHandler events to an external object.
854     * <p>
855     * When this object's customContentHandler is non-null, any SAX events received from the parser will simply be
856     * passed on to the specified object instead of this object handling them. This allows Rule classes to take control
857     * of the SAX event stream for a while in order to do custom processing. Such a rule should save the old value
858     * before setting a new one, and restore the old value in order to resume normal digester processing.
859     * <p>
860     * An example of a Rule which needs this feature is NodeCreateRule.
861     * <p>
862     * Note that saving the old value is probably not needed as it should always be null; a custom rule that wants to
863     * take control could only have been called when there was no custom content handler. But it seems cleaner to
864     * properly save/restore the value and maybe some day this will come in useful.
865     * <p>
866     * Note also that this is not quite equivalent to
867     *
868     * <pre>
869     * digester.getXMLReader().setContentHandler( handler )
870     * </pre>
871     *
872     * for these reasons:
873     * <ul>
874     * <li>Some xml parsers don't like having setContentHandler called after parsing has started. The Aelfred parser is
875     * one example.</li>
876     * <li>Directing the events via the Digester object potentially allows us to log information about those SAX events
877     * at the digester level.</li>
878     * </ul>
879     *
880     * @param handler the custom SAX ContentHandler where events are redirected.
881     * @since 1.7
882     */
883    public void setCustomContentHandler( ContentHandler handler )
884    {
885        customContentHandler = handler;
886    }
887
888    /**
889     * Define a callback object which is invoked whenever an object is pushed onto a digester object stack,
890     * or popped off one.
891     *
892     * @param stackAction the callback object which is invoked whenever an object is pushed onto a digester
893     *        object stack, or popped off one.
894     * @since 1.8
895     */
896    public void setStackAction( StackAction stackAction )
897    {
898        this.stackAction = stackAction;
899    }
900
901    /**
902     * Return the callback object which is invoked whenever an object is pushed onto a digester object stack,
903     * or popped off one.
904     *
905     * @return the callback object which is invoked whenever an object is pushed onto a digester object stack,
906     *         or popped off one.
907     * @see #setStackAction(StackAction)
908     * @since 1.8
909     */
910    public StackAction getStackAction()
911    {
912        return stackAction;
913    }
914
915    /**
916     * Get the most current namespaces for all prefixes.
917     *
918     * @return Map A map with namespace prefixes as keys and most current namespace URIs for the corresponding prefixes
919     *         as values
920     * @since 1.8
921     */
922    public Map<String, String> getCurrentNamespaces()
923    {
924        if ( !namespaceAware )
925        {
926            log.warn( "Digester is not namespace aware" );
927        }
928        Map<String, String> currentNamespaces = new HashMap<String, String>();
929        for ( Map.Entry<String, Stack<String>> nsEntry : namespaces.entrySet() )
930        {
931            try
932            {
933                currentNamespaces.put( nsEntry.getKey(), nsEntry.getValue().peek() );
934            }
935            catch ( RuntimeException e )
936            {
937                // rethrow, after logging
938                log.error( e.getMessage(), e );
939                throw e;
940            }
941        }
942        return currentNamespaces;
943    }
944
945    /**
946     * Returns the executor service used to run asynchronous parse method.
947     *
948     * @return the executor service used to run asynchronous parse method
949     * @since 3.1
950     */
951    public ExecutorService getExecutorService()
952    {
953        return executorService;
954    }
955
956    /**
957     * Sets the executor service to run asynchronous parse method.
958     *
959     * @param executorService the executor service to run asynchronous parse method
960     * @since 3.1
961     */
962    public void setExecutorService( ExecutorService executorService )
963    {
964        this.executorService = executorService;
965    }
966
967    // ------------------------------------------------- ContentHandler Methods
968
969    /**
970     * {@inheritDoc}
971     */
972    @Override
973    public void characters( char buffer[], int start, int length )
974        throws SAXException
975    {
976        if ( customContentHandler != null )
977        {
978            // forward calls instead of handling them here
979            customContentHandler.characters( buffer, start, length );
980            return;
981        }
982
983        if ( saxLog.isDebugEnabled() )
984        {
985            saxLog.debug( "characters(" + new String( buffer, start, length ) + ")" );
986        }
987
988        bodyText.append( buffer, start, length );
989    }
990
991    /**
992     * {@inheritDoc}
993     */
994    @Override
995    public void endDocument()
996        throws SAXException
997    {
998        if ( saxLog.isDebugEnabled() )
999        {
1000            if ( getCount() > 1 )
1001            {
1002                saxLog.debug( "endDocument():  " + getCount() + " elements left" );
1003            }
1004            else
1005            {
1006                saxLog.debug( "endDocument()" );
1007            }
1008        }
1009
1010        // Fire "finish" events for all defined rules
1011        for ( Rule rule : getRules().rules() )
1012        {
1013            try
1014            {
1015                rule.finish();
1016            }
1017            catch ( Exception e )
1018            {
1019                log.error( "Finish event threw exception", e );
1020                throw createSAXException( e );
1021            }
1022            catch ( Error e )
1023            {
1024                log.error( "Finish event threw error", e );
1025                throw e;
1026            }
1027        }
1028
1029        // Perform final cleanup
1030        clear();
1031    }
1032
1033    /**
1034     * {@inheritDoc}
1035     */
1036    @Override
1037    public void endElement( String namespaceURI, String localName, String qName )
1038        throws SAXException
1039    {
1040        if ( customContentHandler != null )
1041        {
1042            // forward calls instead of handling them here
1043            customContentHandler.endElement( namespaceURI, localName, qName );
1044            return;
1045        }
1046
1047        boolean debug = log.isDebugEnabled();
1048
1049        if ( debug )
1050        {
1051            if ( saxLog.isDebugEnabled() )
1052            {
1053                saxLog.debug( "endElement(" + namespaceURI + "," + localName + "," + qName + ")" );
1054            }
1055            log.debug( "  match='" + match + "'" );
1056            log.debug( "  bodyText='" + bodyText + "'" );
1057        }
1058
1059        // the actual element name is either in localName or qName, depending
1060        // on whether the parser is namespace aware
1061        String name = localName;
1062        if ( ( name == null ) || ( name.length() < 1 ) )
1063        {
1064            name = qName;
1065        }
1066
1067        // Fire "body" events for all relevant rules
1068        List<Rule> rules = matches.pop();
1069        if ( ( rules != null ) && ( rules.size() > 0 ) )
1070        {
1071            String bodyText = this.bodyText.toString();
1072            Substitutor substitutor = getSubstitutor();
1073            if ( substitutor != null )
1074            {
1075                bodyText = substitutor.substitute( bodyText );
1076            }
1077            for ( int i = 0; i < rules.size(); i++ )
1078            {
1079                try
1080                {
1081                    Rule rule = rules.get( i );
1082                    if ( debug )
1083                    {
1084                        log.debug( "  Fire body() for " + rule );
1085                    }
1086                    rule.body( namespaceURI, name, bodyText );
1087                }
1088                catch ( Exception e )
1089                {
1090                    log.error( "Body event threw exception", e );
1091                    throw createSAXException( e );
1092                }
1093                catch ( Error e )
1094                {
1095                    log.error( "Body event threw error", e );
1096                    throw e;
1097                }
1098            }
1099        }
1100        else
1101        {
1102            if ( debug )
1103            {
1104                log.debug( "  No rules found matching '" + match + "'." );
1105            }
1106        }
1107
1108        // Recover the body text from the surrounding element
1109        bodyText = bodyTexts.pop();
1110        if ( debug )
1111        {
1112            log.debug( "  Popping body text '" + bodyText.toString() + "'" );
1113        }
1114
1115        // Fire "end" events for all relevant rules in reverse order
1116        if ( rules != null )
1117        {
1118            for ( int i = 0; i < rules.size(); i++ )
1119            {
1120                int j = ( rules.size() - i ) - 1;
1121                try
1122                {
1123                    Rule rule = rules.get( j );
1124                    if ( debug )
1125                    {
1126                        log.debug( "  Fire end() for " + rule );
1127                    }
1128                    rule.end( namespaceURI, name );
1129                }
1130                catch ( Exception e )
1131                {
1132                    log.error( "End event threw exception", e );
1133                    throw createSAXException( e );
1134                }
1135                catch ( Error e )
1136                {
1137                    log.error( "End event threw error", e );
1138                    throw e;
1139                }
1140            }
1141        }
1142
1143        // Recover the previous match expression
1144        int slash = match.lastIndexOf( '/' );
1145        if ( slash >= 0 )
1146        {
1147            match = match.substring( 0, slash );
1148        }
1149        else
1150        {
1151            match = "";
1152        }
1153    }
1154
1155    /**
1156     * {@inheritDoc}
1157     */
1158    @Override
1159    public void endPrefixMapping( String prefix )
1160        throws SAXException
1161    {
1162        if ( saxLog.isDebugEnabled() )
1163        {
1164            saxLog.debug( "endPrefixMapping(" + prefix + ")" );
1165        }
1166
1167        // Deregister this prefix mapping
1168        Stack<String> stack = namespaces.get( prefix );
1169        if ( stack == null )
1170        {
1171            return;
1172        }
1173        try
1174        {
1175            stack.pop();
1176            if ( stack.empty() )
1177            {
1178                namespaces.remove( prefix );
1179            }
1180        }
1181        catch ( EmptyStackException e )
1182        {
1183            throw createSAXException( "endPrefixMapping popped too many times" );
1184        }
1185    }
1186
1187    /**
1188     * {@inheritDoc}
1189     */
1190    @Override
1191    public void ignorableWhitespace( char buffer[], int start, int len )
1192        throws SAXException
1193    {
1194        if ( saxLog.isDebugEnabled() )
1195        {
1196            saxLog.debug( "ignorableWhitespace(" + new String( buffer, start, len ) + ")" );
1197        }
1198
1199        // No processing required
1200    }
1201
1202    /**
1203     * {@inheritDoc}
1204     */
1205    @Override
1206    public void processingInstruction( String target, String data )
1207        throws SAXException
1208    {
1209        if ( customContentHandler != null )
1210        {
1211            // forward calls instead of handling them here
1212            customContentHandler.processingInstruction( target, data );
1213            return;
1214        }
1215
1216        if ( saxLog.isDebugEnabled() )
1217        {
1218            saxLog.debug( "processingInstruction('" + target + "','" + data + "')" );
1219        }
1220
1221        // No processing is required
1222    }
1223
1224    /**
1225     * Gets the document locator associated with our parser.
1226     *
1227     * @return the Locator supplied by the document parser
1228     */
1229    public Locator getDocumentLocator()
1230    {
1231        return locator;
1232    }
1233
1234    /**
1235     * {@inheritDoc}
1236     */
1237    @Override
1238    public void setDocumentLocator( Locator locator )
1239    {
1240        if ( saxLog.isDebugEnabled() )
1241        {
1242            saxLog.debug( "setDocumentLocator(" + locator + ")" );
1243        }
1244
1245        this.locator = locator;
1246    }
1247
1248    /**
1249     * {@inheritDoc}
1250     */
1251    @Override
1252    public void skippedEntity( String name )
1253        throws SAXException
1254    {
1255        if ( saxLog.isDebugEnabled() )
1256        {
1257            saxLog.debug( "skippedEntity(" + name + ")" );
1258        }
1259
1260        // No processing required
1261    }
1262
1263    /**
1264     * {@inheritDoc}
1265     */
1266    @Override
1267    public void startDocument()
1268        throws SAXException
1269    {
1270        if ( saxLog.isDebugEnabled() )
1271        {
1272            saxLog.debug( "startDocument()" );
1273        }
1274
1275        // ensure that the digester is properly configured, as
1276        // the digester could be used as a SAX ContentHandler
1277        // rather than via the parse() methods.
1278        configure();
1279    }
1280
1281    /**
1282     * {@inheritDoc}
1283     */
1284    @Override
1285    public void startElement( String namespaceURI, String localName, String qName, Attributes list )
1286        throws SAXException
1287    {
1288        boolean debug = log.isDebugEnabled();
1289
1290        if ( customContentHandler != null )
1291        {
1292            // forward calls instead of handling them here
1293            customContentHandler.startElement( namespaceURI, localName, qName, list );
1294            return;
1295        }
1296
1297        if ( saxLog.isDebugEnabled() )
1298        {
1299            saxLog.debug( "startElement(" + namespaceURI + "," + localName + "," + qName + ")" );
1300        }
1301
1302        // Save the body text accumulated for our surrounding element
1303        bodyTexts.push( bodyText );
1304        if ( debug )
1305        {
1306            log.debug( "  Pushing body text '" + bodyText.toString() + "'" );
1307        }
1308        bodyText = new StringBuilder();
1309
1310        // the actual element name is either in localName or qName, depending
1311        // on whether the parser is namespace aware
1312        String name = localName;
1313        if ( ( name == null ) || ( name.length() < 1 ) )
1314        {
1315            name = qName;
1316        }
1317
1318        // Compute the current matching rule
1319        StringBuilder sb = new StringBuilder( match );
1320        if ( match.length() > 0 )
1321        {
1322            sb.append( '/' );
1323        }
1324        sb.append( name );
1325        match = sb.toString();
1326        if ( debug )
1327        {
1328            log.debug( "  New match='" + match + "'" );
1329        }
1330
1331        // Fire "begin" events for all relevant rules
1332        List<Rule> rules = getRules().match( namespaceURI, match, localName, list );
1333        matches.push( rules );
1334        if ( ( rules != null ) && ( rules.size() > 0 ) )
1335        {
1336            Substitutor substitutor = getSubstitutor();
1337            if ( substitutor != null )
1338            {
1339                list = substitutor.substitute( list );
1340            }
1341            for ( int i = 0; i < rules.size(); i++ )
1342            {
1343                try
1344                {
1345                    Rule rule = rules.get( i );
1346                    if ( debug )
1347                    {
1348                        log.debug( "  Fire begin() for " + rule );
1349                    }
1350                    rule.begin( namespaceURI, name, list );
1351                }
1352                catch ( Exception e )
1353                {
1354                    log.error( "Begin event threw exception", e );
1355                    throw createSAXException( e );
1356                }
1357                catch ( Error e )
1358                {
1359                    log.error( "Begin event threw error", e );
1360                    throw e;
1361                }
1362            }
1363        }
1364        else
1365        {
1366            if ( debug )
1367            {
1368                log.debug( "  No rules found matching '" + match + "'." );
1369            }
1370        }
1371    }
1372
1373    /**
1374     * {@inheritDoc}
1375     */
1376    @Override
1377    public void startPrefixMapping( String prefix, String namespaceURI )
1378        throws SAXException
1379    {
1380        if ( saxLog.isDebugEnabled() )
1381        {
1382            saxLog.debug( "startPrefixMapping(" + prefix + "," + namespaceURI + ")" );
1383        }
1384
1385        // Register this prefix mapping
1386        Stack<String> stack = namespaces.get( prefix );
1387        if ( stack == null )
1388        {
1389            stack = new Stack<String>();
1390            namespaces.put( prefix, stack );
1391        }
1392        stack.push( namespaceURI );
1393    }
1394
1395    // ----------------------------------------------------- DTDHandler Methods
1396
1397    /**
1398     * {@inheritDoc}
1399     */
1400    @Override
1401    public void notationDecl( String name, String publicId, String systemId )
1402    {
1403        if ( saxLog.isDebugEnabled() )
1404        {
1405            saxLog.debug( "notationDecl(" + name + "," + publicId + "," + systemId + ")" );
1406        }
1407    }
1408
1409    /**
1410     * {@inheritDoc}
1411     */
1412    @Override
1413    public void unparsedEntityDecl( String name, String publicId, String systemId, String notation )
1414    {
1415        if ( saxLog.isDebugEnabled() )
1416        {
1417            saxLog.debug( "unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")" );
1418        }
1419    }
1420
1421    // ----------------------------------------------- EntityResolver Methods
1422
1423    /**
1424     * Set the <code>EntityResolver</code> used by SAX when resolving public id and system id. This must be called
1425     * before the first call to <code>parse()</code>.
1426     *
1427     * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
1428     */
1429    public void setEntityResolver( EntityResolver entityResolver )
1430    {
1431        this.entityResolver = entityResolver;
1432    }
1433
1434    /**
1435     * Return the Entity Resolver used by the SAX parser.
1436     *
1437     * @return the Entity Resolver used by the SAX parser.
1438     */
1439    public EntityResolver getEntityResolver()
1440    {
1441        return entityResolver;
1442    }
1443
1444    /**
1445     * {@inheritDoc}
1446     */
1447    @Override
1448    public InputSource resolveEntity( String publicId, String systemId )
1449        throws SAXException
1450    {
1451        if ( saxLog.isDebugEnabled() )
1452        {
1453            saxLog.debug( "resolveEntity('" + publicId + "', '" + systemId + "')" );
1454        }
1455
1456        if ( publicId != null )
1457        {
1458            this.publicId = publicId;
1459        }
1460
1461        // Has this system identifier been registered?
1462        URL entityURL = null;
1463        if ( publicId != null )
1464        {
1465            entityURL = entityValidator.get( publicId );
1466        }
1467
1468        // Redirect the schema location to a local destination
1469        if ( entityURL == null && systemId != null )
1470        {
1471            entityURL = entityValidator.get( systemId );
1472        }
1473
1474        if ( entityURL == null )
1475        {
1476            if ( systemId == null )
1477            {
1478                // cannot resolve
1479                if ( log.isDebugEnabled() )
1480                {
1481                    log.debug( " Cannot resolve null entity, returning null InputSource" );
1482                }
1483                return ( null );
1484
1485            }
1486            // try to resolve using system ID
1487            if ( log.isDebugEnabled() )
1488            {
1489                log.debug( " Trying to resolve using system ID '" + systemId + "'" );
1490            }
1491            try
1492            {
1493                entityURL = new URL( systemId );
1494            }
1495            catch ( MalformedURLException e )
1496            {
1497                throw new IllegalArgumentException( "Malformed URL '" + systemId + "' : " + e.getMessage() );
1498            }
1499        }
1500
1501        // Return an input source to our alternative URL
1502        if ( log.isDebugEnabled() )
1503        {
1504            log.debug( " Resolving to alternate DTD '" + entityURL + "'" );
1505        }
1506
1507        try
1508        {
1509            return createInputSourceFromURL( entityURL );
1510        }
1511        catch ( Exception e )
1512        {
1513            throw createSAXException( e );
1514        }
1515    }
1516
1517    // ------------------------------------------------- ErrorHandler Methods
1518
1519    /**
1520     * {@inheritDoc}
1521     */
1522    @Override
1523    public void error( SAXParseException exception )
1524        throws SAXException
1525    {
1526        log.error( "Parse Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": "
1527            + exception.getMessage(), exception );
1528        if ( errorHandler != null )
1529        {
1530            errorHandler.error( exception );
1531        }
1532    }
1533
1534    /**
1535     * {@inheritDoc}
1536     */
1537    @Override
1538    public void fatalError( SAXParseException exception )
1539        throws SAXException
1540    {
1541        log.error( "Parse Fatal Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber()
1542            + ": " + exception.getMessage(), exception );
1543        if ( errorHandler != null )
1544        {
1545            errorHandler.fatalError( exception );
1546        }
1547    }
1548
1549    /**
1550     * {@inheritDoc}
1551     */
1552    @Override
1553    public void warning( SAXParseException exception )
1554        throws SAXException
1555    {
1556        if ( errorHandler != null )
1557        {
1558            log.warn( "Parse Warning Error at line " + exception.getLineNumber() + " column "
1559                          + exception.getColumnNumber() + ": " + exception.getMessage(), exception );
1560
1561            errorHandler.warning( exception );
1562        }
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        catch ( SAXException e )
1651        {
1652            log.error( format( "An error occurred while parsing XML from '%s', see nested exceptions", systemId ),
1653                       e );
1654            throw e;
1655        }
1656        cleanup();
1657        return this.<T> getRoot();
1658    }
1659
1660    /**
1661     * Creates a Callable instance that parse the content of the specified reader using this Digester.
1662     *
1663     * @param <T> The result type returned by the returned Future's {@code get} method
1664     * @param input Input source containing the XML data to be parsed
1665     * @return a Future that can be used to track when the parse has been fully processed.
1666     * @see Digester#parse(InputSource)
1667     * @since 3.1
1668     */
1669    public <T> Future<T> asyncParse( final InputSource input )
1670    {
1671        return asyncParse( new Callable<T>()
1672        {
1673
1674            public T call()
1675                throws Exception
1676            {
1677                return Digester.this.<T> parse( input );
1678            }
1679
1680        } );
1681    }
1682
1683    /**
1684     * Parse the content of the specified input stream using this Digester. Returns the root element from the object
1685     * stack (if any).
1686     *
1687     * @param <T> the type used to auto-cast the returned object to the assigned variable type
1688     * @param input Input stream containing the XML data to be parsed
1689     * @return the root element from the object stack (if any)
1690     * @exception IOException if an input/output error occurs
1691     * @exception SAXException if a parsing exception occurs
1692     */
1693    public <T> T parse( InputStream input )
1694        throws IOException, SAXException
1695    {
1696        if ( input == null )
1697        {
1698            throw new IllegalArgumentException( "InputStream to parse is null" );
1699        }
1700
1701        return ( this.<T> parse( new InputSource( input ) ) );
1702    }
1703
1704    /**
1705     * Creates a Callable instance that parse the content of the specified reader using this Digester.
1706     *
1707     * @param <T> The result type returned by the returned Future's {@code get} method
1708     * @param input Input stream containing the XML data to be parsed
1709     * @return a Future that can be used to track when the parse has been fully processed.
1710     * @see Digester#parse(InputStream)
1711     * @since 3.1
1712     */
1713    public <T> Future<T> asyncParse( final InputStream input )
1714    {
1715        return asyncParse( new Callable<T>()
1716        {
1717
1718            public T call()
1719                throws Exception
1720            {
1721                return Digester.this.<T> parse( input );
1722            }
1723
1724        } );
1725    }
1726
1727    /**
1728     * Parse the content of the specified reader using this Digester. Returns the root element from the object stack (if
1729     * any).
1730     *
1731     * @param <T> the type used to auto-cast the returned object to the assigned variable type
1732     * @param reader Reader containing the XML data to be parsed
1733     * @return the root element from the object stack (if any)
1734     * @exception IOException if an input/output error occurs
1735     * @exception SAXException if a parsing exception occurs
1736     */
1737    public <T> T parse( Reader reader )
1738        throws IOException, SAXException
1739    {
1740        if ( reader == null )
1741        {
1742            throw new IllegalArgumentException( "Reader to parse is null" );
1743        }
1744
1745        return ( this.<T> parse( new InputSource( reader ) ) );
1746    }
1747
1748    /**
1749     * Creates a Callable instance that parse the content of the specified reader using this Digester.
1750     *
1751     * @param <T> The result type returned by the returned Future's {@code get} method
1752     * @param reader Reader containing the XML data to be parsed
1753     * @return a Future that can be used to track when the parse has been fully processed.
1754     * @see Digester#parse(Reader)
1755     * @since 3.1
1756     */
1757    public <T> Future<T> asyncParse( final Reader reader )
1758    {
1759        return asyncParse( new Callable<T>()
1760        {
1761
1762            public T call()
1763                throws Exception
1764            {
1765                return Digester.this.<T> parse( reader );
1766            }
1767
1768        } );
1769    }
1770
1771    /**
1772     * Parse the content of the specified URI using this Digester. Returns the root element from the object stack (if
1773     * any).
1774     *
1775     * @param <T> the type used to auto-cast the returned object to the assigned variable type
1776     * @param uri URI containing the XML data to be parsed
1777     * @return the root element from the object stack (if any)
1778     * @exception IOException if an input/output error occurs
1779     * @exception SAXException if a parsing exception occurs
1780     */
1781    public <T> T parse( String uri )
1782        throws IOException, SAXException
1783    {
1784        if ( uri == null )
1785        {
1786            throw new IllegalArgumentException( "String URI to parse is null" );
1787        }
1788
1789        return ( this.<T> parse( createInputSourceFromURL( uri ) ) );
1790    }
1791
1792    /**
1793     * Creates a Callable instance that parse the content of the specified reader using this Digester.
1794     *
1795     * @param <T> The result type returned by the returned Future's {@code get} method
1796     * @param uri URI containing the XML data to be parsed
1797     * @return a Future that can be used to track when the parse has been fully processed.
1798     * @see Digester#parse(String)
1799     * @since 3.1
1800     */
1801    public <T> Future<T> asyncParse( final String uri )
1802    {
1803        return asyncParse( new Callable<T>()
1804        {
1805
1806            public T call()
1807                throws Exception
1808            {
1809                return Digester.this.<T> parse( uri );
1810            }
1811
1812        } );
1813    }
1814
1815    /**
1816     * Parse the content of the specified URL using this Digester. Returns the root element from the object stack (if
1817     * any).
1818     *
1819     * @param <T> the type used to auto-cast the returned object to the assigned variable type
1820     * @param url URL containing the XML data to be parsed
1821     * @return the root element from the object stack (if any)
1822     * @exception IOException if an input/output error occurs
1823     * @exception SAXException if a parsing exception occurs
1824     * @since 1.8
1825     */
1826    public <T> T parse( URL url )
1827        throws IOException, SAXException
1828    {
1829        if ( url == null )
1830        {
1831            throw new IllegalArgumentException( "URL to parse is null" );
1832        }
1833
1834        return ( this.<T> parse( createInputSourceFromURL( url ) ) );
1835    }
1836
1837    /**
1838     * Creates a Callable instance that parse the content of the specified reader using this Digester.
1839     *
1840     * @param <T> The result type returned by the returned Future's {@code get} method
1841     * @param url URL containing the XML data to be parsed
1842     * @return a Future that can be used to track when the parse has been fully processed.
1843     * @see Digester#parse(URL)
1844     * @since 3.1
1845     */
1846    public <T> Future<T> asyncParse( final URL url )
1847    {
1848        return asyncParse( new Callable<T>()
1849        {
1850
1851            public T call()
1852                throws Exception
1853            {
1854                return Digester.this.<T> parse( url );
1855            }
1856
1857        } );
1858    }
1859
1860    /**
1861     * Execute the parse in async mode.
1862     *
1863     * @param <T> the type used to auto-cast the returned object to the assigned variable type
1864     * @param callable
1865     * @return a Future that can be used to track when the parse has been fully processed.
1866     * @since 3.1
1867     */
1868    private <T> Future<T> asyncParse( Callable<T> callable )
1869    {
1870        if ( executorService == null )
1871        {
1872            throw new IllegalStateException( "ExecutorService not set" );
1873        }
1874
1875        return executorService.submit( callable );
1876    }
1877
1878    /**
1879     * <p>
1880     * Register the specified DTD URL for the specified public identifier. This must be called before the first call to
1881     * <code>parse()</code>.
1882     * </p>
1883     * <p>
1884     * <code>Digester</code> contains an internal <code>EntityResolver</code> implementation. This maps
1885     * <code>PUBLICID</code>'s to URLs (from which the resource will be loaded). A common use case for this method is to
1886     * register local URLs (possibly computed at runtime by a classloader) for DTDs. This allows the performance
1887     * advantage of using a local version without having to ensure every <code>SYSTEM</code> URI on every processed xml
1888     * document is local. This implementation provides only basic functionality. If more sophisticated features are
1889     * required, using {@link #setEntityResolver} to set a custom resolver is recommended.
1890     * </p>
1891     * <p>
1892     * <strong>Note:</strong> This method will have no effect when a custom <code>EntityResolver</code> has been set.
1893     * (Setting a custom <code>EntityResolver</code> overrides the internal implementation.)
1894     * </p>
1895     *
1896     * @param publicId Public identifier of the DTD to be resolved
1897     * @param entityURL The URL to use for reading this DTD
1898     * @since 1.8
1899     */
1900    public void register( String publicId, URL entityURL )
1901    {
1902        if ( log.isDebugEnabled() )
1903        {
1904            log.debug( "register('" + publicId + "', '" + entityURL + "'" );
1905        }
1906        entityValidator.put( publicId, entityURL );
1907    }
1908
1909    /**
1910     * <p>
1911     * Convenience method that registers the string version of an entity URL instead of a URL version.
1912     * </p>
1913     *
1914     * @param publicId Public identifier of the entity to be resolved
1915     * @param entityURL The URL to use for reading this entity
1916     */
1917    public void register( String publicId, String entityURL )
1918    {
1919        if ( log.isDebugEnabled() )
1920        {
1921            log.debug( "register('" + publicId + "', '" + entityURL + "'" );
1922        }
1923        try
1924        {
1925            entityValidator.put( publicId, new URL( entityURL ) );
1926        }
1927        catch ( MalformedURLException e )
1928        {
1929            throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() );
1930        }
1931    }
1932
1933    /**
1934     * Convenience method that registers DTD URLs for the specified public identifiers.
1935     *
1936     * @param entityValidator The URLs of entityValidator that have been registered, keyed by the public
1937     *                        identifier that corresponds.
1938     * @since 3.0
1939     */
1940    public void registerAll( Map<String, URL> entityValidator )
1941    {
1942        this.entityValidator.putAll( entityValidator );
1943    }
1944
1945    /**
1946     * <p>
1947     * <code>List</code> of <code>InputSource</code> instances created by a <code>createInputSourceFromURL()</code>
1948     * method call. These represent open input streams that need to be closed to avoid resource leaks, as well as
1949     * potentially locked JAR files on Windows.
1950     * </p>
1951     */
1952    protected List<InputSource> inputSources = new ArrayList<InputSource>( 5 );
1953
1954    /**
1955     * Given a URL, return an InputSource that reads from that URL.
1956     * <p>
1957     * Ideally this function would not be needed and code could just use <code>new InputSource(entityURL)</code>.
1958     * Unfortunately it appears that when the entityURL points to a file within a jar archive a caching mechanism inside
1959     * the InputSource implementation causes a file-handle to the jar file to remain open. On Windows systems this then
1960     * causes the jar archive file to be locked on disk ("in use") which makes it impossible to delete the jar file -
1961     * and that really stuffs up "undeploy" in webapps in particular.
1962     * <p>
1963     * In JDK1.4 and later, Apache XercesJ is used as the xml parser. The InputSource object provided is converted into
1964     * an XMLInputSource, and eventually passed to an instance of XMLDocumentScannerImpl to specify the source data to
1965     * be converted into tokens for the rest of the XMLReader code to handle. XMLDocumentScannerImpl calls
1966     * fEntityManager.startDocumentEntity(source), where fEntityManager is declared in ancestor class XMLScanner to be
1967     * an XMLEntityManager. In that class, if the input source stream is null, then:
1968     *
1969     * <pre>
1970     * URL location = new URL( expandedSystemId );
1971     * URLConnection connect = location.openConnection();
1972     * if ( connect instanceof HttpURLConnection )
1973     * {
1974     *     setHttpProperties( connect, xmlInputSource );
1975     * }
1976     * stream = connect.getInputStream();
1977     * </pre>
1978     *
1979     * This method pretty much duplicates the standard behaviour, except that it calls URLConnection.setUseCaches(false)
1980     * before opening the connection.
1981     *
1982     * @param url The URL has to be read
1983     * @return The InputSource that reads from the input URL
1984     * @throws IOException if any error occurs while reading the input URL
1985     * @since 1.8
1986     */
1987    public InputSource createInputSourceFromURL( URL url )
1988        throws IOException
1989    {
1990        URLConnection connection = url.openConnection();
1991        connection.setUseCaches( false );
1992        InputStream stream = connection.getInputStream();
1993        InputSource source = new InputSource( stream );
1994        source.setSystemId( url.toExternalForm() );
1995        inputSources.add( source );
1996        return source;
1997    }
1998
1999    /**
2000     * <p>
2001     * Convenience method that creates an <code>InputSource</code> from the string version of a URL.
2002     * </p>
2003     *
2004     * @param url URL for which to create an <code>InputSource</code>
2005     * @return The InputSource that reads from the input URL
2006     * @throws IOException if any error occurs while reading the input URL
2007     * @since 1.8
2008     */
2009    public InputSource createInputSourceFromURL( String url )
2010        throws IOException
2011    {
2012        return createInputSourceFromURL( new URL( url ) );
2013    }
2014
2015    // --------------------------------------------------------- Rule Methods
2016
2017    /**
2018     * <p>
2019     * Register a new Rule matching the specified pattern. This method sets the <code>Digester</code> property on the
2020     * rule.
2021     * </p>
2022     *
2023     * @param pattern Element matching pattern
2024     * @param rule Rule to be registered
2025     */
2026    public void addRule( String pattern, Rule rule )
2027    {
2028        rule.setDigester( this );
2029        getRules().add( pattern, rule );
2030    }
2031
2032    /**
2033     * Register a set of Rule instances defined in a RuleSet.
2034     *
2035     * @param ruleSet The RuleSet instance to configure from
2036     */
2037    public void addRuleSet( RuleSet ruleSet )
2038    {
2039        String oldNamespaceURI = getRuleNamespaceURI();
2040        String newNamespaceURI = ruleSet.getNamespaceURI();
2041        if ( log.isDebugEnabled() )
2042        {
2043            if ( newNamespaceURI == null )
2044            {
2045                log.debug( "addRuleSet() with no namespace URI" );
2046            }
2047            else
2048            {
2049                log.debug( "addRuleSet() with namespace URI " + newNamespaceURI );
2050            }
2051        }
2052        setRuleNamespaceURI( newNamespaceURI );
2053        ruleSet.addRuleInstances( this );
2054        setRuleNamespaceURI( oldNamespaceURI );
2055    }
2056
2057    /**
2058     * Add a "bean property setter" rule for the specified parameters.
2059     *
2060     * @param pattern Element matching pattern
2061     * @see BeanPropertySetterRule
2062     */
2063    public void addBeanPropertySetter( String pattern )
2064    {
2065        addRule( pattern, new BeanPropertySetterRule() );
2066    }
2067
2068    /**
2069     * Add a "bean property setter" rule for the specified parameters.
2070     *
2071     * @param pattern Element matching pattern
2072     * @param propertyName Name of property to set
2073     * @see BeanPropertySetterRule
2074     */
2075    public void addBeanPropertySetter( String pattern, String propertyName )
2076    {
2077        addRule( pattern, new BeanPropertySetterRule( propertyName ) );
2078    }
2079
2080    /**
2081     * Add an "call method" rule for a method which accepts no arguments.
2082     *
2083     * @param pattern Element matching pattern
2084     * @param methodName Method name to be called
2085     * @see CallMethodRule
2086     */
2087    public void addCallMethod( String pattern, String methodName )
2088    {
2089        addRule( pattern, new CallMethodRule( methodName ) );
2090    }
2091
2092    /**
2093     * Add an "call method" rule for the specified parameters.
2094     *
2095     * @param pattern Element matching pattern
2096     * @param methodName Method name to be called
2097     * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element)
2098     * @see CallMethodRule
2099     */
2100    public void addCallMethod( String pattern, String methodName, int paramCount )
2101    {
2102        addRule( pattern, new CallMethodRule( methodName, paramCount ) );
2103    }
2104
2105    /**
2106     * Add an "call method" rule for the specified parameters. If <code>paramCount</code> is set to zero the rule will
2107     * use the body of the matched element as the single argument of the method, unless <code>paramTypes</code> is null
2108     * or empty, in this case the rule will call the specified method with no arguments.
2109     *
2110     * @param pattern Element matching pattern
2111     * @param methodName Method name to be called
2112     * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element)
2113     * @param paramTypes Set of Java class names for the types of the expected parameters (if you wish to use a
2114     *            primitive type, specify the corresonding Java wrapper class instead, such as
2115     *            <code>java.lang.Boolean</code> for a <code>boolean</code> parameter)
2116     * @see CallMethodRule
2117     */
2118    public void addCallMethod( String pattern, String methodName, int paramCount, String paramTypes[] )
2119    {
2120        addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) );
2121    }
2122
2123    /**
2124     * Add an "call method" rule for the specified parameters. If <code>paramCount</code> is set to zero the rule will
2125     * use the body of the matched element as the single argument of the method, unless <code>paramTypes</code> is null
2126     * or empty, in this case the rule will call the specified method with no arguments.
2127     *
2128     * @param pattern Element matching pattern
2129     * @param methodName Method name to be called
2130     * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element)
2131     * @param paramTypes The Java class names of the arguments (if you wish to use a primitive type, specify the
2132     *            corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a
2133     *            <code>boolean</code> parameter)
2134     * @see CallMethodRule
2135     */
2136    public void addCallMethod( String pattern, String methodName, int paramCount, Class<?> paramTypes[] )
2137    {
2138        addRule( pattern, new CallMethodRule( methodName, paramCount, paramTypes ) );
2139    }
2140
2141    /**
2142     * Add a "call parameter" rule for the specified parameters.
2143     *
2144     * @param pattern Element matching pattern
2145     * @param paramIndex Zero-relative parameter index to set (from the body of this element)
2146     * @see CallParamRule
2147     */
2148    public void addCallParam( String pattern, int paramIndex )
2149    {
2150        addRule( pattern, new CallParamRule( paramIndex ) );
2151    }
2152
2153    /**
2154     * Add a "call parameter" rule for the specified parameters.
2155     *
2156     * @param pattern Element matching pattern
2157     * @param paramIndex Zero-relative parameter index to set (from the specified attribute)
2158     * @param attributeName Attribute whose value is used as the parameter value
2159     * @see CallParamRule
2160     */
2161    public void addCallParam( String pattern, int paramIndex, String attributeName )
2162    {
2163        addRule( pattern, new CallParamRule( paramIndex, attributeName ) );
2164    }
2165
2166    /**
2167     * Add a "call parameter" rule. This will either take a parameter from the stack or from the current element body
2168     * text.
2169     *
2170     * @param pattern Element matching pattern
2171     * @param paramIndex The zero-relative parameter number
2172     * @param fromStack Should the call parameter be taken from the top of the stack?
2173     * @see CallParamRule
2174     */
2175    public void addCallParam( String pattern, int paramIndex, boolean fromStack )
2176    {
2177        addRule( pattern, new CallParamRule( paramIndex, fromStack ) );
2178    }
2179
2180    /**
2181     * Add a "call parameter" rule that sets a parameter from the stack. This takes a parameter from the given position
2182     * on the stack.
2183     *
2184     * @param pattern Element matching pattern
2185     * @param paramIndex The zero-relative parameter number
2186     * @param stackIndex set the call parameter to the stackIndex'th object down the stack, where 0 is the top of the
2187     *            stack, 1 the next element down and so on
2188     * @see CallMethodRule
2189     */
2190    public void addCallParam( String pattern, int paramIndex, int stackIndex )
2191    {
2192        addRule( pattern, new CallParamRule( paramIndex, stackIndex ) );
2193    }
2194
2195    /**
2196     * Add a "call parameter" rule that sets a parameter from the current <code>Digester</code> matching path. This is
2197     * sometimes useful when using rules that support wildcards.
2198     *
2199     * @param pattern the pattern that this rule should match
2200     * @param paramIndex The zero-relative parameter number
2201     * @see CallMethodRule
2202     */
2203    public void addCallParamPath( String pattern, int paramIndex )
2204    {
2205        addRule( pattern, new PathCallParamRule( paramIndex ) );
2206    }
2207
2208    /**
2209     * Add a "call parameter" rule that sets a parameter from a caller-provided object. This can be used to pass
2210     * constants such as strings to methods; it can also be used to pass mutable objects, providing ways for objects to
2211     * do things like "register" themselves with some shared object.
2212     * <p>
2213     * Note that when attempting to locate a matching method to invoke, the true type of the paramObj is used, so that
2214     * despite the paramObj being passed in here as type Object, the target method can declare its parameters as being
2215     * the true type of the object (or some ancestor type, according to the usual type-conversion rules).
2216     *
2217     * @param pattern Element matching pattern
2218     * @param paramIndex The zero-relative parameter number
2219     * @param paramObj Any arbitrary object to be passed to the target method.
2220     * @see CallMethodRule
2221     * @since 1.6
2222     */
2223    public void addObjectParam( String pattern, int paramIndex, Object paramObj )
2224    {
2225        addRule( pattern, new ObjectParamRule( paramIndex, paramObj ) );
2226    }
2227
2228    /**
2229     * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process
2230     * will be propagated.
2231     *
2232     * @param pattern Element matching pattern
2233     * @param className Java class name of the object creation factory class
2234     * @see FactoryCreateRule
2235     */
2236    public void addFactoryCreate( String pattern, String className )
2237    {
2238        addFactoryCreate( pattern, className, false );
2239    }
2240
2241    /**
2242     * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process
2243     * will be propagated.
2244     *
2245     * @param pattern Element matching pattern
2246     * @param clazz Java class of the object creation factory class
2247     * @see FactoryCreateRule
2248     */
2249    public void addFactoryCreate( String pattern, Class<? extends ObjectCreationFactory<?>> clazz )
2250    {
2251        addFactoryCreate( pattern, clazz, false );
2252    }
2253
2254    /**
2255     * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process
2256     * will be propagated.
2257     *
2258     * @param pattern Element matching pattern
2259     * @param className Java class name of the object creation factory class
2260     * @param attributeName Attribute name which, if present, overrides the value specified by <code>className</code>
2261     * @see FactoryCreateRule
2262     */
2263    public void addFactoryCreate( String pattern, String className, String attributeName )
2264    {
2265        addFactoryCreate( pattern, className, attributeName, false );
2266    }
2267
2268    /**
2269     * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process
2270     * will be propagated.
2271     *
2272     * @param pattern Element matching pattern
2273     * @param clazz Java class of the object creation factory class
2274     * @param attributeName Attribute name which, if present, overrides the value specified by <code>className</code>
2275     * @see FactoryCreateRule
2276     */
2277    public void addFactoryCreate( String pattern, Class<? extends ObjectCreationFactory<?>> clazz,
2278                                  String attributeName )
2279    {
2280        addFactoryCreate( pattern, clazz, attributeName, false );
2281    }
2282
2283    /**
2284     * Add a "factory create" rule for the specified parameters. Exceptions thrown during the object creation process
2285     * will be propagated.
2286     *
2287     * @param pattern Element matching pattern
2288     * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized
2289     * @see FactoryCreateRule
2290     */
2291    public void addFactoryCreate( String pattern, ObjectCreationFactory<?> creationFactory )
2292    {
2293        addFactoryCreate( pattern, creationFactory, false );
2294    }
2295
2296    /**
2297     * Add a "factory create" rule for the specified parameters.
2298     *
2299     * @param pattern Element matching pattern
2300     * @param className Java class name of the object creation factory class
2301     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be
2302     *            ignored.
2303     * @see FactoryCreateRule
2304     */
2305    public void addFactoryCreate( String pattern, String className, boolean ignoreCreateExceptions )
2306    {
2307        addRule( pattern, new FactoryCreateRule( className, ignoreCreateExceptions ) );
2308    }
2309
2310    /**
2311     * Add a "factory create" rule for the specified parameters.
2312     *
2313     * @param pattern Element matching pattern
2314     * @param clazz Java class of the object creation factory class
2315     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be
2316     *            ignored.
2317     * @see FactoryCreateRule
2318     */
2319    public void addFactoryCreate( String pattern, Class<? extends ObjectCreationFactory<?>> clazz,
2320                                  boolean ignoreCreateExceptions )
2321    {
2322        addRule( pattern, new FactoryCreateRule( clazz, ignoreCreateExceptions ) );
2323    }
2324
2325    /**
2326     * Add a "factory create" rule for the specified parameters.
2327     *
2328     * @param pattern Element matching pattern
2329     * @param className Java class name of the object creation factory class
2330     * @param attributeName Attribute name which, if present, overrides the value specified by <code>className</code>
2331     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be
2332     *            ignored.
2333     * @see FactoryCreateRule
2334     */
2335    public void addFactoryCreate( String pattern, String className, String attributeName,
2336                                  boolean ignoreCreateExceptions )
2337    {
2338        addRule( pattern, new FactoryCreateRule( className, attributeName, ignoreCreateExceptions ) );
2339    }
2340
2341    /**
2342     * Add a "factory create" rule for the specified parameters.
2343     *
2344     * @param pattern Element matching pattern
2345     * @param clazz Java class of the object creation factory class
2346     * @param attributeName Attribute name which, if present, overrides the value specified by <code>className</code>
2347     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be
2348     *            ignored.
2349     * @see FactoryCreateRule
2350     */
2351    public void addFactoryCreate( String pattern, Class<? extends ObjectCreationFactory<?>> clazz,
2352                                  String attributeName, boolean ignoreCreateExceptions )
2353    {
2354        addRule( pattern, new FactoryCreateRule( clazz, attributeName, ignoreCreateExceptions ) );
2355    }
2356
2357    /**
2358     * Add a "factory create" rule for the specified parameters.
2359     *
2360     * @param pattern Element matching pattern
2361     * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized
2362     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be
2363     *            ignored.
2364     * @see FactoryCreateRule
2365     */
2366    public void addFactoryCreate( String pattern, ObjectCreationFactory<?> creationFactory,
2367                                  boolean ignoreCreateExceptions )
2368    {
2369        creationFactory.setDigester( this );
2370        addRule( pattern, new FactoryCreateRule( creationFactory, ignoreCreateExceptions ) );
2371    }
2372
2373    /**
2374     * Add an "object create" rule for the specified parameters.
2375     *
2376     * @param pattern Element matching pattern
2377     * @param className Java class name to be created
2378     * @see ObjectCreateRule
2379     */
2380    public void addObjectCreate( String pattern, String className )
2381    {
2382        addRule( pattern, new ObjectCreateRule( className ) );
2383    }
2384
2385    /**
2386     * Add an "object create" rule for the specified parameters.
2387     *
2388     * @param pattern Element matching pattern
2389     * @param clazz Java class to be created
2390     * @see ObjectCreateRule
2391     */
2392    public void addObjectCreate( String pattern, Class<?> clazz )
2393    {
2394        addRule( pattern, new ObjectCreateRule( clazz ) );
2395    }
2396
2397    /**
2398     * Add an "object create" rule for the specified parameters.
2399     *
2400     * @param pattern Element matching pattern
2401     * @param className Default Java class name to be created
2402     * @param attributeName Attribute name that optionally overrides the default Java class name to be created
2403     * @see ObjectCreateRule
2404     */
2405    public void addObjectCreate( String pattern, String className, String attributeName )
2406    {
2407        addRule( pattern, new ObjectCreateRule( className, attributeName ) );
2408    }
2409
2410    /**
2411     * Add an "object create" rule for the specified parameters.
2412     *
2413     * @param pattern Element matching pattern
2414     * @param attributeName Attribute name that optionally overrides
2415     * @param clazz Default Java class to be created the default Java class name to be created
2416     * @see ObjectCreateRule
2417     */
2418    public void addObjectCreate( String pattern, String attributeName, Class<?> clazz )
2419    {
2420        addRule( pattern, new ObjectCreateRule( attributeName, clazz ) );
2421    }
2422
2423    /**
2424     * Adds an {@link SetNestedPropertiesRule}.
2425     *
2426     * @param pattern register the rule with this pattern
2427     * @since 1.6
2428     */
2429    public void addSetNestedProperties( String pattern )
2430    {
2431        addRule( pattern, new SetNestedPropertiesRule() );
2432    }
2433
2434    /**
2435     * Adds an {@link SetNestedPropertiesRule}.
2436     *
2437     * @param pattern register the rule with this pattern
2438     * @param elementName elment name that a property maps to
2439     * @param propertyName property name of the element mapped from
2440     * @since 1.6
2441     */
2442    public void addSetNestedProperties( String pattern, String elementName, String propertyName )
2443    {
2444        addRule( pattern, new SetNestedPropertiesRule( elementName, propertyName ) );
2445    }
2446
2447    /**
2448     * Adds an {@link SetNestedPropertiesRule}.
2449     *
2450     * @param pattern register the rule with this pattern
2451     * @param elementNames elment names that (in order) map to properties
2452     * @param propertyNames property names that (in order) elements are mapped to
2453     * @since 1.6
2454     */
2455    public void addSetNestedProperties( String pattern, String[] elementNames, String[] propertyNames )
2456    {
2457        addRule( pattern, new SetNestedPropertiesRule( elementNames, propertyNames ) );
2458    }
2459
2460    /**
2461     * Add a "set next" rule for the specified parameters.
2462     *
2463     * @param pattern Element matching pattern
2464     * @param methodName Method name to call on the parent element
2465     * @see SetNextRule
2466     */
2467    public void addSetNext( String pattern, String methodName )
2468    {
2469        addRule( pattern, new SetNextRule( methodName ) );
2470    }
2471
2472    /**
2473     * Add a "set next" rule for the specified parameters.
2474     *
2475     * @param pattern Element matching pattern
2476     * @param methodName Method name to call on the parent element
2477     * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the
2478     *            corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a
2479     *            <code>boolean</code> parameter)
2480     * @see SetNextRule
2481     */
2482    public void addSetNext( String pattern, String methodName, String paramType )
2483    {
2484        addRule( pattern, new SetNextRule( methodName, paramType ) );
2485    }
2486
2487    /**
2488     * Add {@link SetRootRule} with the specified parameters.
2489     *
2490     * @param pattern Element matching pattern
2491     * @param methodName Method name to call on the root object
2492     * @see SetRootRule
2493     */
2494    public void addSetRoot( String pattern, String methodName )
2495    {
2496        addRule( pattern, new SetRootRule( methodName ) );
2497    }
2498
2499    /**
2500     * Add {@link SetRootRule} with the specified parameters.
2501     *
2502     * @param pattern Element matching pattern
2503     * @param methodName Method name to call on the root object
2504     * @param paramType Java class name of the expected parameter type
2505     * @see SetRootRule
2506     */
2507    public void addSetRoot( String pattern, String methodName, String paramType )
2508    {
2509        addRule( pattern, new SetRootRule( methodName, paramType ) );
2510    }
2511
2512    /**
2513     * Add a "set properties" rule for the specified parameters.
2514     *
2515     * @param pattern Element matching pattern
2516     * @see SetPropertiesRule
2517     */
2518    public void addSetProperties( String pattern )
2519    {
2520        addRule( pattern, new SetPropertiesRule() );
2521    }
2522
2523    /**
2524     * Add a "set properties" rule with a single overridden parameter. See
2525     * {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
2526     *
2527     * @param pattern Element matching pattern
2528     * @param attributeName map this attribute
2529     * @param propertyName to this property
2530     * @see SetPropertiesRule
2531     */
2532    public void addSetProperties( String pattern, String attributeName, String propertyName )
2533    {
2534        addRule( pattern, new SetPropertiesRule( attributeName, propertyName ) );
2535    }
2536
2537    /**
2538     * Add a "set properties" rule with overridden parameters. See
2539     * {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
2540     *
2541     * @param pattern Element matching pattern
2542     * @param attributeNames names of attributes with custom mappings
2543     * @param propertyNames property names these attributes map to
2544     * @see SetPropertiesRule
2545     */
2546    public void addSetProperties( String pattern, String[] attributeNames, String[] propertyNames )
2547    {
2548        addRule( pattern, new SetPropertiesRule( attributeNames, propertyNames ) );
2549    }
2550
2551    /**
2552     * Add a "set property" rule for the specified parameters.
2553     *
2554     * @param pattern Element matching pattern
2555     * @param name Attribute name containing the property name to be set
2556     * @param value Attribute name containing the property value to set
2557     * @see SetPropertyRule
2558     */
2559    public void addSetProperty( String pattern, String name, String value )
2560    {
2561        addRule( pattern, new SetPropertyRule( name, value ) );
2562    }
2563
2564    /**
2565     * Add a "set top" rule for the specified parameters.
2566     *
2567     * @param pattern Element matching pattern
2568     * @param methodName Method name to call on the parent element
2569     * @see SetTopRule
2570     */
2571    public void addSetTop( String pattern, String methodName )
2572    {
2573        addRule( pattern, new SetTopRule( methodName ) );
2574    }
2575
2576    /**
2577     * Add a "set top" rule for the specified parameters.
2578     *
2579     * @param pattern Element matching pattern
2580     * @param methodName Method name to call on the parent element
2581     * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify the
2582     *            corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a
2583     *            <code>boolean</code> parameter)
2584     * @see SetTopRule
2585     */
2586    public void addSetTop( String pattern, String methodName, String paramType )
2587    {
2588        addRule( pattern, new SetTopRule( methodName, paramType ) );
2589    }
2590
2591    // --------------------------------------------------- Object Stack Methods
2592
2593    /**
2594     * Clear the current contents of the default object stack, the param stack, all named stacks, and other internal
2595     * variables.
2596     * <p>
2597     * Calling this method <i>might</i> allow another document of the same type to be correctly parsed. However this
2598     * method was not intended for this purpose (just to tidy up memory usage). In general, a separate Digester object
2599     * should be created for each document to be parsed.
2600     * <p>
2601     * Note that this method is called automatically after a document has been successfully parsed by a Digester
2602     * instance. However it is not invoked automatically when a parse fails, so when reusing a Digester instance (which
2603     * is not recommended) this method <i>must</i> be called manually after a parse failure.
2604     */
2605    public void clear()
2606    {
2607        match = "";
2608        bodyTexts.clear();
2609        params.clear();
2610        publicId = null;
2611        stack.clear();
2612        stacksByName.clear();
2613        customContentHandler = null;
2614    }
2615
2616    /**
2617     * Return the top object on the stack without removing it.
2618     *
2619     * If there are no objects on the stack, return <code>null</code>.
2620     *
2621     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2622     * @return the top object on the stack without removing it.
2623     */
2624    public <T> T peek()
2625    {
2626        try
2627        {
2628            return this.<T> npeSafeCast( stack.peek() );
2629        }
2630        catch ( EmptyStackException e )
2631        {
2632            log.warn( "Empty stack (returning null)" );
2633            return ( null );
2634        }
2635    }
2636
2637    /**
2638     * Return the n'th object down the stack, where 0 is the top element and [getCount()-1] is the bottom element. If
2639     * the specified index is out of range, return <code>null</code>.
2640     *
2641     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2642     * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on.
2643     * @return the n'th object down the stack
2644     */
2645    public <T> T peek( int n )
2646    {
2647        int index = ( stack.size() - 1 ) - n;
2648        if ( index < 0 )
2649        {
2650            log.warn( "Empty stack (returning null)" );
2651            return ( null );
2652        }
2653        try
2654        {
2655            return this.<T> npeSafeCast( stack.get( index ) );
2656        }
2657        catch ( EmptyStackException e )
2658        {
2659            log.warn( "Empty stack (returning null)" );
2660            return ( null );
2661        }
2662    }
2663
2664    /**
2665     * Pop the top object off of the stack, and return it. If there are no objects on the stack, return
2666     * <code>null</code>.
2667     *
2668     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2669     * @return the top object popped off of the stack
2670     */
2671    public <T> T pop()
2672    {
2673        try
2674        {
2675            T popped = this.<T> npeSafeCast( stack.pop() );
2676            if ( stackAction != null )
2677            {
2678                popped = stackAction.onPop( this, null, popped );
2679            }
2680            return popped;
2681        }
2682        catch ( EmptyStackException e )
2683        {
2684            log.warn( "Empty stack (returning null)" );
2685            return ( null );
2686        }
2687    }
2688
2689    /**
2690     * Push a new object onto the top of the object stack.
2691     *
2692     * @param <T> any type of the pushed object
2693     * @param object The new object
2694     */
2695    public <T> void push( T object )
2696    {
2697        if ( stackAction != null )
2698        {
2699            object = stackAction.onPush( this, null, object );
2700        }
2701
2702        if ( stack.size() == 0 )
2703        {
2704            root = object;
2705        }
2706        stack.push( object );
2707    }
2708
2709    /**
2710     * Pushes the given object onto the stack with the given name. If no stack already exists with the given name then
2711     * one will be created.
2712     *
2713     * @param <T> any type of the pushed object
2714     * @param stackName the name of the stack onto which the object should be pushed
2715     * @param value the Object to be pushed onto the named stack.
2716     * @since 1.6
2717     */
2718    public <T> void push( String stackName, T value )
2719    {
2720        if ( stackAction != null )
2721        {
2722            value = stackAction.onPush( this, stackName, value );
2723        }
2724
2725        Stack<Object> namedStack = stacksByName.get( stackName );
2726        if ( namedStack == null )
2727        {
2728            namedStack = new Stack<Object>();
2729            stacksByName.put( stackName, namedStack );
2730        }
2731        namedStack.push( value );
2732    }
2733
2734    /**
2735     * <p>
2736     * Pops (gets and removes) the top object from the stack with the given name.
2737     * </p>
2738     * <p>
2739     * <strong>Note:</strong> a stack is considered empty if no objects have been pushed onto it yet.
2740     * </p>
2741     *
2742     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2743     * @param stackName the name of the stack from which the top value is to be popped.
2744     * @return the top <code>Object</code> on the stack or or null if the stack is either empty or has not been created
2745     *         yet
2746     * @since 1.6
2747     */
2748    public <T> T pop( String stackName )
2749    {
2750        T result = null;
2751        Stack<Object> namedStack = stacksByName.get( stackName );
2752        if ( namedStack == null )
2753        {
2754            if ( log.isDebugEnabled() )
2755            {
2756                log.debug( "Stack '" + stackName + "' is empty" );
2757            }
2758            throw new EmptyStackException();
2759        }
2760
2761        result = this.<T> npeSafeCast( namedStack.pop() );
2762
2763        if ( stackAction != null )
2764        {
2765            result = stackAction.onPop( this, stackName, result );
2766        }
2767
2768        return result;
2769    }
2770
2771    /**
2772     * <p>
2773     * Gets the top object from the stack with the given name. This method does not remove the object from the stack.
2774     * </p>
2775     * <p>
2776     * <strong>Note:</strong> a stack is considered empty if no objects have been pushed onto it yet.
2777     * </p>
2778     *
2779     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2780     * @param stackName the name of the stack to be peeked
2781     * @return the top <code>Object</code> on the stack or null if the stack is either empty or has not been created yet
2782     * @since 1.6
2783     */
2784    public <T> T peek( String stackName )
2785    {
2786        return this.<T> npeSafeCast( peek( stackName, 0 ) );
2787    }
2788
2789    /**
2790     * <p>
2791     * Gets the top object from the stack with the given name. This method does not remove the object from the stack.
2792     * </p>
2793     * <p>
2794     * <strong>Note:</strong> a stack is considered empty if no objects have been pushed onto it yet.
2795     * </p>
2796     *
2797     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2798     * @param stackName the name of the stack to be peeked
2799     * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on.
2800     * @return the specified <code>Object</code> on the stack.
2801     * @since 1.6
2802     */
2803    public <T> T peek( String stackName, int n )
2804    {
2805        T result = null;
2806        Stack<Object> namedStack = stacksByName.get( stackName );
2807        if ( namedStack == null )
2808        {
2809            if ( log.isDebugEnabled() )
2810            {
2811                log.debug( "Stack '" + stackName + "' is empty" );
2812            }
2813            throw new EmptyStackException();
2814        }
2815
2816        int index = ( namedStack.size() - 1 ) - n;
2817        if ( index < 0 )
2818        {
2819            throw new EmptyStackException();
2820        }
2821        result = this.<T> npeSafeCast( namedStack.get( index ) );
2822
2823        return result;
2824    }
2825
2826    /**
2827     * <p>
2828     * Is the stack with the given name empty?
2829     * </p>
2830     * <p>
2831     * <strong>Note:</strong> a stack is considered empty if no objects have been pushed onto it yet.
2832     * </p>
2833     *
2834     * @param stackName the name of the stack whose emptiness should be evaluated
2835     * @return true if the given stack if empty
2836     * @since 1.6
2837     */
2838    public boolean isEmpty( String stackName )
2839    {
2840        boolean result = true;
2841        Stack<Object> namedStack = stacksByName.get( stackName );
2842        if ( namedStack != null )
2843        {
2844            result = namedStack.isEmpty();
2845        }
2846        return result;
2847    }
2848
2849    /**
2850     * Returns the root element of the tree of objects created as a result of applying the rule objects to the input
2851     * XML.
2852     * <p>
2853     * If the digester stack was "primed" by explicitly pushing a root object onto the stack before parsing started,
2854     * then that root object is returned here.
2855     * <p>
2856     * Alternatively, if a Rule which creates an object (eg ObjectCreateRule) matched the root element of the xml, then
2857     * the object created will be returned here.
2858     * <p>
2859     * In other cases, the object most recently pushed onto an empty digester stack is returned. This would be a most
2860     * unusual use of digester, however; one of the previous configurations is much more likely.
2861     * <p>
2862     * Note that when using one of the Digester.parse methods, the return value from the parse method is exactly the
2863     * same as the return value from this method. However when the Digester is being used as a SAXContentHandler, no
2864     * such return value is available; in this case, this method allows you to access the root object that has been
2865     * created after parsing has completed.
2866     *
2867     * @param <T> the type used to auto-cast the returned object to the assigned variable type
2868     * @return the root object that has been created after parsing or null if the digester has not parsed any XML yet.
2869     */
2870    public <T> T getRoot()
2871    {
2872        return this.<T> npeSafeCast( root );
2873    }
2874
2875    /**
2876     * This method allows the "root" variable to be reset to null.
2877     * <p>
2878     * It is not considered safe for a digester instance to be reused to parse multiple xml documents. However if you
2879     * are determined to do so, then you should call both clear() and resetRoot() before each parse.
2880     *
2881     * @since 1.7
2882     */
2883    public void resetRoot()
2884    {
2885        root = null;
2886    }
2887
2888    // ------------------------------------------------ Parameter Stack Methods
2889
2890    // ------------------------------------------------------ Protected Methods
2891
2892    /**
2893     * <p>
2894     * Clean up allocated resources after parsing is complete. The default method closes input streams that have been
2895     * created by Digester itself. If you override this method in a subclass, be sure to call
2896     * <code>super.cleanup()</code> to invoke this logic.
2897     * </p>
2898     *
2899     * @since 1.8
2900     */
2901    protected void cleanup()
2902    {
2903        // If we created any InputSource objects in this instance,
2904        // they each have an input stream that should be closed
2905        for ( InputSource source : inputSources )
2906        {
2907            try
2908            {
2909                source.getByteStream().close();
2910            }
2911            catch ( IOException e )
2912            {
2913                // Fall through so we get them all
2914                if ( log.isWarnEnabled() )
2915                {
2916                    log.warn( format( "An error occurred while closing resource %s (%s)",
2917                                      source.getPublicId(),
2918                                      source.getSystemId() ), e );
2919                }
2920            }
2921        }
2922        inputSources.clear();
2923    }
2924
2925    /**
2926     * <p>
2927     * Provide a hook for lazy configuration of this <code>Digester</code> instance. The default implementation does
2928     * nothing, but subclasses can override as needed.
2929     * </p>
2930     * <p>
2931     * <strong>Note</strong> This method may be called more than once. Once only initialization code should be placed in
2932     * {@link #initialize} or the code should take responsibility by checking and setting the {@link #configured} flag.
2933     * </p>
2934     */
2935    protected void configure()
2936    {
2937        // Do not configure more than once
2938        if ( configured )
2939        {
2940            return;
2941        }
2942
2943        // Perform lazy configuration as needed
2944        initialize(); // call hook method for subclasses that want to be initialized once only
2945        // Nothing else required by default
2946
2947        // Set the configuration flag to avoid repeating
2948        configured = true;
2949    }
2950
2951    /**
2952     * Checks the Digester instance has been configured.
2953     *
2954     * @return true, if the Digester instance has been configured, false otherwise
2955     * @since 3.0
2956     */
2957    public boolean isConfigured()
2958    {
2959        return configured;
2960    }
2961
2962    /**
2963     * <p>
2964     * Provides a hook for lazy initialization of this <code>Digester</code> instance. The default implementation does
2965     * nothing, but subclasses can override as needed. Digester (by default) only calls this method once.
2966     * </p>
2967     * <p>
2968     * <strong>Note</strong> This method will be called by {@link #configure} only when the {@link #configured} flag is
2969     * false. Subclasses that override <code>configure</code> or who set <code>configured</code> may find that this
2970     * method may be called more than once.
2971     * </p>
2972     *
2973     * @since 1.6
2974     */
2975    protected void initialize()
2976    {
2977        // Perform lazy initialization as needed
2978        // Nothing required by default
2979    }
2980
2981    // -------------------------------------------------------- Package Methods
2982
2983    /**
2984     * Return the set of DTD URL registrations, keyed by public identifier. NOTE: the returned map is in read-only mode.
2985     *
2986     * @return the read-only Map of DTD URL registrations.
2987     */
2988    Map<String, URL> getRegistrations()
2989    {
2990        return Collections.unmodifiableMap( entityValidator );
2991    }
2992
2993    /**
2994     * <p>
2995     * Return the top object on the parameters stack without removing it. If there are no objects on the stack, return
2996     * <code>null</code>.
2997     * </p>
2998     * <p>
2999     * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}.
3000     * </p>
3001     *
3002     * @return the top object on the parameters stack without removing it.
3003     */
3004    public Object[] peekParams()
3005    {
3006        try
3007        {
3008            return ( params.peek() );
3009        }
3010        catch ( EmptyStackException e )
3011        {
3012            log.warn( "Empty stack (returning null)" );
3013            return ( null );
3014        }
3015    }
3016
3017    /**
3018     * <p>
3019     * Return the n'th object down the parameters stack, where 0 is the top element and [getCount()-1] is the bottom
3020     * element. If the specified index is out of range, return <code>null</code>.
3021     * </p>
3022     * <p>
3023     * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}.
3024     * </p>
3025     *
3026     * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on.
3027     * @return the n'th object down the parameters stack
3028     */
3029    public Object[] peekParams( int n )
3030    {
3031        int index = ( params.size() - 1 ) - n;
3032        if ( index < 0 )
3033        {
3034            log.warn( "Empty stack (returning null)" );
3035            return ( null );
3036        }
3037        try
3038        {
3039            return ( params.get( index ) );
3040        }
3041        catch ( EmptyStackException e )
3042        {
3043            log.warn( "Empty stack (returning null)" );
3044            return ( null );
3045        }
3046    }
3047
3048    /**
3049     * <p>
3050     * Pop the top object off of the parameters stack, and return it. If there are no objects on the stack, return
3051     * <code>null</code>.
3052     * </p>
3053     * <p>
3054     * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}.
3055     * </p>
3056     *
3057     * @return the top object popped off of the parameters stack
3058     */
3059    public Object[] popParams()
3060    {
3061        try
3062        {
3063            if ( log.isTraceEnabled() )
3064            {
3065                log.trace( "Popping params" );
3066            }
3067            return ( params.pop() );
3068        }
3069        catch ( EmptyStackException e )
3070        {
3071            log.warn( "Empty stack (returning null)" );
3072            return ( null );
3073        }
3074    }
3075
3076    /**
3077     * <p>
3078     * Push a new object onto the top of the parameters stack.
3079     * </p>
3080     * <p>
3081     * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}.
3082     * </p>
3083     *
3084     * @param object The new object
3085     */
3086    public void pushParams( Object... object )
3087    {
3088        if ( log.isTraceEnabled() )
3089        {
3090            log.trace( "Pushing params" );
3091        }
3092        params.push( object );
3093    }
3094
3095    /**
3096     * Create a SAX exception which also understands about the location in the digester file where the exception occurs
3097     *
3098     * @param message the custom SAX exception message
3099     * @param e the exception cause
3100     * @return the new SAX exception
3101     */
3102    public SAXException createSAXException( String message, Exception e )
3103    {
3104        if ( ( e != null ) && ( e instanceof InvocationTargetException ) )
3105        {
3106            Throwable t = ( (InvocationTargetException) e ).getTargetException();
3107            if ( ( t != null ) && ( t instanceof Exception ) )
3108            {
3109                e = (Exception) t;
3110            }
3111        }
3112        if ( locator != null )
3113        {
3114            String error =
3115                "Error at line " + locator.getLineNumber() + " char " + locator.getColumnNumber() + ": " + message;
3116            if ( e != null )
3117            {
3118                return new SAXParseException( error, locator, e );
3119            }
3120            return new SAXParseException( error, locator );
3121        }
3122        log.error( "No Locator!" );
3123        if ( e != null )
3124        {
3125            return new SAXException( message, e );
3126        }
3127        return new SAXException( message );
3128    }
3129
3130    /**
3131     * Create a SAX exception which also understands about the location in the digester file where the exception occurs
3132     *
3133     * @param e the exception cause
3134     * @return the new SAX exception
3135     */
3136    public SAXException createSAXException( Exception e )
3137    {
3138        if ( e instanceof InvocationTargetException )
3139        {
3140            Throwable t = ( (InvocationTargetException) e ).getTargetException();
3141            if ( ( t != null ) && ( t instanceof Exception ) )
3142            {
3143                e = (Exception) t;
3144            }
3145        }
3146        return createSAXException( e.getMessage(), e );
3147    }
3148
3149    /**
3150     * Create a SAX exception which also understands about the location in the digester file where the exception occurs
3151     *
3152     * @param message the custom SAX exception message
3153     * @return the new SAX exception
3154     */
3155    public SAXException createSAXException( String message )
3156    {
3157        return createSAXException( message, null );
3158    }
3159
3160    /**
3161     * Helps casting the input object to given type, avoiding NPEs.
3162     *
3163     * @since 3.0
3164     * @param <T> the type the input object has to be cast.
3165     * @param obj the object has to be cast.
3166     * @return the casted object, if input object is not null, null otherwise.
3167     */
3168    private <T> T npeSafeCast( Object obj )
3169    {
3170        if ( obj == null )
3171        {
3172            return null;
3173        }
3174
3175        @SuppressWarnings( "unchecked" )
3176        T result = (T) obj;
3177        return result;
3178    }
3179
3180}