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