KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > digester > Digester


1 /* $Id: Digester.java 467222 2006-10-24 03:17:11Z markt $
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one or more
4  * contributor license agreements. See the NOTICE file distributed with
5  * this work for additional information regarding copyright ownership.
6  * The ASF licenses this file to You under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.apache.tomcat.util.digester;
20
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.Reader JavaDoc;
27 import java.lang.reflect.InvocationTargetException JavaDoc;
28 import java.util.EmptyStackException JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Properties JavaDoc;
34
35 import javax.xml.parsers.ParserConfigurationException JavaDoc;
36 import javax.xml.parsers.SAXParser JavaDoc;
37 import javax.xml.parsers.SAXParserFactory JavaDoc;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.tomcat.util.IntrospectionUtils;
42 import org.xml.sax.Attributes JavaDoc;
43 import org.xml.sax.EntityResolver JavaDoc;
44 import org.xml.sax.ErrorHandler JavaDoc;
45 import org.xml.sax.InputSource JavaDoc;
46 import org.xml.sax.Locator JavaDoc;
47 import org.xml.sax.SAXException JavaDoc;
48 import org.xml.sax.SAXNotRecognizedException JavaDoc;
49 import org.xml.sax.SAXNotSupportedException JavaDoc;
50 import org.xml.sax.SAXParseException JavaDoc;
51 import org.xml.sax.XMLReader JavaDoc;
52 import org.xml.sax.helpers.AttributesImpl JavaDoc;
53 import org.xml.sax.helpers.DefaultHandler JavaDoc;
54
55
56
57
58 /**
59  * <p>A <strong>Digester</strong> processes an XML input stream by matching a
60  * series of element nesting patterns to execute Rules that have been added
61  * prior to the start of parsing. This package was inspired by the
62  * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
63  * but is organized somewhat differently.</p>
64  *
65  * <p>See the <a HREF="package-summary.html#package_description">Digester
66  * Developer Guide</a> for more information.</p>
67  *
68  * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
69  * only be used within the context of a single thread at a time, and a call
70  * to <code>parse()</code> must be completed before another can be initiated
71  * even from the same thread.</p>
72  *
73  * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents
74  * the support of XML schema. You need Xerces 2.1/2.3 and up to make
75  * this class working with XML schema</p>
76  */

77
78 public class Digester extends DefaultHandler JavaDoc {
79
80
81     // ---------------------------------------------------------- Static Fields
82

83
84     private static class SystemPropertySource
85         implements IntrospectionUtils.PropertySource {
86         public String JavaDoc getProperty( String JavaDoc key ) {
87             return System.getProperty(key);
88         }
89     }
90
91     protected static IntrospectionUtils.PropertySource source[] =
92         new IntrospectionUtils.PropertySource[] { new SystemPropertySource() };
93
94
95     // --------------------------------------------------------- Constructors
96

97
98     /**
99      * Construct a new Digester with default properties.
100      */

101     public Digester() {
102
103         super();
104
105     }
106
107
108     /**
109      * Construct a new Digester, allowing a SAXParser to be passed in. This
110      * allows Digester to be used in environments which are unfriendly to
111      * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to
112      * James House (james@interobjective.com). This may help in places where
113      * you are able to load JAXP 1.1 classes yourself.
114      */

115     public Digester(SAXParser JavaDoc parser) {
116
117         super();
118
119         this.parser = parser;
120
121     }
122
123
124     /**
125      * Construct a new Digester, allowing an XMLReader to be passed in. This
126      * allows Digester to be used in environments which are unfriendly to
127      * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you
128      * have to configure namespace and validation support yourself, as these
129      * properties only affect the SAXParser and emtpy constructor.
130      */

131     public Digester(XMLReader JavaDoc reader) {
132
133         super();
134
135         this.reader = reader;
136
137     }
138
139
140     // --------------------------------------------------- Instance Variables
141

142
143     /**
144      * The body text of the current element.
145      */

146     protected StringBuffer JavaDoc bodyText = new StringBuffer JavaDoc();
147
148
149     /**
150      * The stack of body text string buffers for surrounding elements.
151      */

152     protected ArrayStack bodyTexts = new ArrayStack();
153
154
155     /**
156      * Stack whose elements are List objects, each containing a list of
157      * Rule objects as returned from Rules.getMatch(). As each xml element
158      * in the input is entered, the matching rules are pushed onto this
159      * stack. After the end tag is reached, the matches are popped again.
160      * The depth of is stack is therefore exactly the same as the current
161      * "nesting" level of the input xml.
162      *
163      * @since 1.6
164      */

165     protected ArrayStack matches = new ArrayStack(10);
166     
167     /**
168      * The class loader to use for instantiating application objects.
169      * If not specified, the context class loader, or the class loader
170      * used to load Digester itself, is used, based on the value of the
171      * <code>useContextClassLoader</code> variable.
172      */

173     protected ClassLoader JavaDoc classLoader = null;
174
175
176     /**
177      * Has this Digester been configured yet.
178      */

179     protected boolean configured = false;
180
181
182     /**
183      * The EntityResolver used by the SAX parser. By default it use this class
184      */

185     protected EntityResolver JavaDoc entityResolver;
186     
187     /**
188      * The URLs of entityValidator that have been registered, keyed by the public
189      * identifier that corresponds.
190      */

191     protected HashMap JavaDoc entityValidator = new HashMap JavaDoc();
192
193
194     /**
195      * The application-supplied error handler that is notified when parsing
196      * warnings, errors, or fatal errors occur.
197      */

198     protected ErrorHandler JavaDoc errorHandler = null;
199
200
201     /**
202      * The SAXParserFactory that is created the first time we need it.
203      */

204     protected SAXParserFactory JavaDoc factory = null;
205
206     /**
207      * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
208      */

209     protected String JavaDoc JAXP_SCHEMA_LANGUAGE =
210         "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
211     
212     
213     /**
214      * The Locator associated with our parser.
215      */

216     protected Locator JavaDoc locator = null;
217
218
219     /**
220      * The current match pattern for nested element processing.
221      */

222     protected String JavaDoc match = "";
223
224
225     /**
226      * Do we want a "namespace aware" parser.
227      */

228     protected boolean namespaceAware = false;
229
230
231     /**
232      * Registered namespaces we are currently processing. The key is the
233      * namespace prefix that was declared in the document. The value is an
234      * ArrayStack of the namespace URIs this prefix has been mapped to --
235      * the top Stack element is the most current one. (This architecture
236      * is required because documents can declare nested uses of the same
237      * prefix for different Namespace URIs).
238      */

239     protected HashMap JavaDoc namespaces = new HashMap JavaDoc();
240
241
242     /**
243      * The parameters stack being utilized by CallMethodRule and
244      * CallParamRule rules.
245      */

246     protected ArrayStack params = new ArrayStack();
247
248
249     /**
250      * The SAXParser we will use to parse the input stream.
251      */

252     protected SAXParser JavaDoc parser = null;
253
254
255     /**
256      * The public identifier of the DTD we are currently parsing under
257      * (if any).
258      */

259     protected String JavaDoc publicId = null;
260
261
262     /**
263      * The XMLReader used to parse digester rules.
264      */

265     protected XMLReader JavaDoc reader = null;
266
267
268     /**
269      * The "root" element of the stack (in other words, the last object
270      * that was popped.
271      */

272     protected Object JavaDoc root = null;
273
274
275     /**
276      * The <code>Rules</code> implementation containing our collection of
277      * <code>Rule</code> instances and associated matching policy. If not
278      * established before the first rule is added, a default implementation
279      * will be provided.
280      */

281     protected Rules rules = null;
282
283    /**
284      * The XML schema language to use for validating an XML instance. By
285      * default this value is set to <code>W3C_XML_SCHEMA</code>
286      */

287     protected String JavaDoc schemaLanguage = W3C_XML_SCHEMA;
288     
289         
290     /**
291      * The XML schema to use for validating an XML instance.
292      */

293     protected String JavaDoc schemaLocation = null;
294     
295     
296     /**
297      * The object stack being constructed.
298      */

299     protected ArrayStack stack = new ArrayStack();
300
301
302     /**
303      * Do we want to use the Context ClassLoader when loading classes
304      * for instantiating new objects. Default is <code>false</code>.
305      */

306     protected boolean useContextClassLoader = false;
307
308
309     /**
310      * Do we want to use a validating parser.
311      */

312     protected boolean validating = false;
313
314
315     /**
316      * The Log to which most logging calls will be made.
317      */

318     protected Log log =
319         LogFactory.getLog("org.apache.commons.digester.Digester");
320
321
322     /**
323      * The Log to which all SAX event related logging calls will be made.
324      */

325     protected Log saxLog =
326         LogFactory.getLog("org.apache.commons.digester.Digester.sax");
327     
328         
329     /**
330      * The schema language supported. By default, we use this one.
331      */

332     protected static final String JavaDoc W3C_XML_SCHEMA =
333         "http://www.w3.org/2001/XMLSchema";
334     
335     /** Stacks used for interrule communication, indexed by name String */
336     private HashMap JavaDoc stacksByName = new HashMap JavaDoc();
337     
338     // ------------------------------------------------------------- Properties
339

340     /**
341      * Return the currently mapped namespace URI for the specified prefix,
342      * if any; otherwise return <code>null</code>. These mappings come and
343      * go dynamically as the document is parsed.
344      *
345      * @param prefix Prefix to look up
346      */

347     public String JavaDoc findNamespaceURI(String JavaDoc prefix) {
348         
349         ArrayStack stack = (ArrayStack) namespaces.get(prefix);
350         if (stack == null) {
351             return (null);
352         }
353         try {
354             return ((String JavaDoc) stack.peek());
355         } catch (EmptyStackException JavaDoc e) {
356             return (null);
357         }
358
359     }
360
361
362     /**
363      * Return the class loader to be used for instantiating application objects
364      * when required. This is determined based upon the following rules:
365      * <ul>
366      * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
367      * <li>The thread context class loader, if it exists and the
368      * <code>useContextClassLoader</code> property is set to true</li>
369      * <li>The class loader used to load the Digester class itself.
370      * </ul>
371      */

372     public ClassLoader JavaDoc getClassLoader() {
373
374         if (this.classLoader != null) {
375             return (this.classLoader);
376         }
377         if (this.useContextClassLoader) {
378             ClassLoader JavaDoc classLoader =
379                     Thread.currentThread().getContextClassLoader();
380             if (classLoader != null) {
381                 return (classLoader);
382             }
383         }
384         return (this.getClass().getClassLoader());
385
386     }
387
388
389     /**
390      * Set the class loader to be used for instantiating application objects
391      * when required.
392      *
393      * @param classLoader The new class loader to use, or <code>null</code>
394      * to revert to the standard rules
395      */

396     public void setClassLoader(ClassLoader JavaDoc classLoader) {
397
398         this.classLoader = classLoader;
399
400     }
401
402
403     /**
404      * Return the current depth of the element stack.
405      */

406     public int getCount() {
407
408         return (stack.size());
409
410     }
411
412
413     /**
414      * Return the name of the XML element that is currently being processed.
415      */

416     public String JavaDoc getCurrentElementName() {
417
418         String JavaDoc elementName = match;
419         int lastSlash = elementName.lastIndexOf('/');
420         if (lastSlash >= 0) {
421             elementName = elementName.substring(lastSlash + 1);
422         }
423         return (elementName);
424
425     }
426
427
428     /**
429      * Return the debugging detail level of our currently enabled logger.
430      *
431      * @deprecated This method now always returns 0. Digester uses the apache
432      * jakarta commons-logging library; see the documentation for that library
433      * for more information.
434      */

435     public int getDebug() {
436
437         return (0);
438
439     }
440
441
442     /**
443      * Set the debugging detail level of our currently enabled logger.
444      *
445      * @param debug New debugging detail level (0=off, increasing integers
446      * for more detail)
447      *
448      * @deprecated This method now has no effect at all. Digester uses
449      * the apache jakarta comons-logging library; see the documentation
450      * for that library for more information.
451      */

452     public void setDebug(int debug) {
453
454         ; // No action is taken
455

456     }
457
458
459     /**
460      * Return the error handler for this Digester.
461      */

462     public ErrorHandler JavaDoc getErrorHandler() {
463
464         return (this.errorHandler);
465
466     }
467
468
469     /**
470      * Set the error handler for this Digester.
471      *
472      * @param errorHandler The new error handler
473      */

474     public void setErrorHandler(ErrorHandler JavaDoc errorHandler) {
475
476         this.errorHandler = errorHandler;
477
478     }
479
480
481     /**
482      * Return the SAXParserFactory we will use, creating one if necessary.
483      */

484     public SAXParserFactory JavaDoc getFactory() {
485
486         if (factory == null) {
487             factory = SAXParserFactory.newInstance();
488             factory.setNamespaceAware(namespaceAware);
489             factory.setValidating(validating);
490         }
491         return (factory);
492
493     }
494
495
496     /**
497      * Returns a flag indicating whether the requested feature is supported
498      * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
499      * See <a HREF="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
500      * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
501      * for information about the standard SAX2 feature flags.
502      *
503      * @param feature Name of the feature to inquire about
504      *
505      * @exception ParserConfigurationException if a parser configuration error
506      * occurs
507      * @exception SAXNotRecognizedException if the property name is
508      * not recognized
509      * @exception SAXNotSupportedException if the property name is
510      * recognized but not supported
511      */

512     public boolean getFeature(String JavaDoc feature)
513         throws ParserConfigurationException JavaDoc, SAXNotRecognizedException JavaDoc,
514         SAXNotSupportedException JavaDoc {
515
516         return (getFactory().getFeature(feature));
517
518     }
519
520
521     /**
522      * Sets a flag indicating whether the requested feature is supported
523      * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
524      * See <a HREF="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
525      * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
526      * for information about the standard SAX2 feature flags. In order to be
527      * effective, this method must be called <strong>before</strong> the
528      * <code>getParser()</code> method is called for the first time, either
529      * directly or indirectly.
530      *
531      * @param feature Name of the feature to set the status for
532      * @param value The new value for this feature
533      *
534      * @exception ParserConfigurationException if a parser configuration error
535      * occurs
536      * @exception SAXNotRecognizedException if the property name is
537      * not recognized
538      * @exception SAXNotSupportedException if the property name is
539      * recognized but not supported
540      */

541     public void setFeature(String JavaDoc feature, boolean value)
542         throws ParserConfigurationException JavaDoc, SAXNotRecognizedException JavaDoc,
543         SAXNotSupportedException JavaDoc {
544
545         getFactory().setFeature(feature, value);
546
547     }
548
549
550     /**
551      * Return the current Logger associated with this instance of the Digester
552      */

553     public Log getLogger() {
554
555         return log;
556
557     }
558
559
560     /**
561      * Set the current logger for this Digester.
562      */

563     public void setLogger(Log log) {
564
565         this.log = log;
566
567     }
568
569     /**
570      * Gets the logger used for logging SAX-related information.
571      * <strong>Note</strong> the output is finely grained.
572      *
573      * @since 1.6
574      */

575     public Log getSAXLogger() {
576         
577         return saxLog;
578     }
579     
580
581     /**
582      * Sets the logger used for logging SAX-related information.
583      * <strong>Note</strong> the output is finely grained.
584      * @param saxLog Log, not null
585      *
586      * @since 1.6
587      */

588     public void setSAXLogger(Log saxLog) {
589     
590         this.saxLog = saxLog;
591     }
592
593     /**
594      * Return the current rule match path
595      */

596     public String JavaDoc getMatch() {
597
598         return match;
599
600     }
601
602
603     /**
604      * Return the "namespace aware" flag for parsers we create.
605      */

606     public boolean getNamespaceAware() {
607
608         return (this.namespaceAware);
609
610     }
611
612
613     /**
614      * Set the "namespace aware" flag for parsers we create.
615      *
616      * @param namespaceAware The new "namespace aware" flag
617      */

618     public void setNamespaceAware(boolean namespaceAware) {
619
620         this.namespaceAware = namespaceAware;
621
622     }
623
624     
625     /**
626      * Set the publid id of the current file being parse.
627      * @param publicId the DTD/Schema public's id.
628      */

629     public void setPublicId(String JavaDoc publicId){
630         this.publicId = publicId;
631     }
632     
633     
634     /**
635      * Return the public identifier of the DTD we are currently
636      * parsing under, if any.
637      */

638     public String JavaDoc getPublicId() {
639
640         return (this.publicId);
641
642     }
643
644
645     /**
646      * Return the namespace URI that will be applied to all subsequently
647      * added <code>Rule</code> objects.
648      */

649     public String JavaDoc getRuleNamespaceURI() {
650
651         return (getRules().getNamespaceURI());
652
653     }
654
655
656     /**
657      * Set the namespace URI that will be applied to all subsequently
658      * added <code>Rule</code> objects.
659      *
660      * @param ruleNamespaceURI Namespace URI that must match on all
661      * subsequently added rules, or <code>null</code> for matching
662      * regardless of the current namespace URI
663      */

664     public void setRuleNamespaceURI(String JavaDoc ruleNamespaceURI) {
665
666         getRules().setNamespaceURI(ruleNamespaceURI);
667
668     }
669
670
671     /**
672      * Return the SAXParser we will use to parse the input stream. If there
673      * is a problem creating the parser, return <code>null</code>.
674      */

675     public SAXParser JavaDoc getParser() {
676
677         // Return the parser we already created (if any)
678
if (parser != null) {
679             return (parser);
680         }
681
682         // Create a new parser
683
try {
684             if (validating) {
685                 Properties JavaDoc properties = new Properties JavaDoc();
686                 properties.put("SAXParserFactory", getFactory());
687                 if (schemaLocation != null) {
688                     properties.put("schemaLocation", schemaLocation);
689                     properties.put("schemaLanguage", schemaLanguage);
690                 }
691                 parser = ParserFeatureSetterFactory.newSAXParser(properties); } else {
692                 parser = getFactory().newSAXParser();
693             }
694         } catch (Exception JavaDoc e) {
695             log.error("Digester.getParser: ", e);
696             return (null);
697         }
698
699         return (parser);
700
701     }
702
703
704     /**
705      * Return the current value of the specified property for the underlying
706      * <code>XMLReader</code> implementation.
707      * See <a HREF="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
708      * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
709      * for information about the standard SAX2 properties.
710      *
711      * @param property Property name to be retrieved
712      *
713      * @exception SAXNotRecognizedException if the property name is
714      * not recognized
715      * @exception SAXNotSupportedException if the property name is
716      * recognized but not supported
717      */

718     public Object JavaDoc getProperty(String JavaDoc property)
719         throws SAXNotRecognizedException JavaDoc, SAXNotSupportedException JavaDoc {
720
721         return (getParser().getProperty(property));
722
723     }
724
725
726     /**
727      * Set the current value of the specified property for the underlying
728      * <code>XMLReader</code> implementation.
729      * See <a HREF="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
730      * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
731      * for information about the standard SAX2 properties.
732      *
733      * @param property Property name to be set
734      * @param value Property value to be set
735      *
736      * @exception SAXNotRecognizedException if the property name is
737      * not recognized
738      * @exception SAXNotSupportedException if the property name is
739      * recognized but not supported
740      */

741     public void setProperty(String JavaDoc property, Object JavaDoc value)
742         throws SAXNotRecognizedException JavaDoc, SAXNotSupportedException JavaDoc {
743
744         getParser().setProperty(property, value);
745
746     }
747
748
749     /**
750      * By setting the reader in the constructor, you can bypass JAXP and
751      * be able to use digester in Weblogic 6.0.
752      *
753      * @deprecated Use getXMLReader() instead, which can throw a
754      * SAXException if the reader cannot be instantiated
755      */

756     public XMLReader JavaDoc getReader() {
757
758         try {
759             return (getXMLReader());
760         } catch (SAXException JavaDoc e) {
761             log.error("Cannot get XMLReader", e);
762             return (null);
763         }
764
765     }
766
767
768     /**
769      * Return the <code>Rules</code> implementation object containing our
770      * rules collection and associated matching policy. If none has been
771      * established, a default implementation will be created and returned.
772      */

773     public Rules getRules() {
774
775         if (this.rules == null) {
776             this.rules = new RulesBase();
777             this.rules.setDigester(this);
778         }
779         return (this.rules);
780
781     }
782
783     
784     /**
785      * Set the <code>Rules</code> implementation object containing our
786      * rules collection and associated matching policy.
787      *
788      * @param rules New Rules implementation
789      */

790     public void setRules(Rules rules) {
791
792         this.rules = rules;
793         this.rules.setDigester(this);
794
795     }
796
797
798     /**
799      * Return the XML Schema URI used for validating an XML instance.
800      */

801     public String JavaDoc getSchema() {
802
803         return (this.schemaLocation);
804
805     }
806
807
808     /**
809      * Set the XML Schema URI used for validating a XML Instance.
810      *
811      * @param schemaLocation a URI to the schema.
812      */

813     public void setSchema(String JavaDoc schemaLocation){
814
815         this.schemaLocation = schemaLocation;
816
817     }
818     
819
820     /**
821      * Return the XML Schema language used when parsing.
822      */

823     public String JavaDoc getSchemaLanguage() {
824
825         return (this.schemaLanguage);
826
827     }
828
829
830     /**
831      * Set the XML Schema language used when parsing. By default, we use W3C.
832      *
833      * @param schemaLanguage a URI to the schema language.
834      */

835     public void setSchemaLanguage(String JavaDoc schemaLanguage){
836
837         this.schemaLanguage = schemaLanguage;
838
839     }
840
841
842     /**
843      * Return the boolean as to whether the context classloader should be used.
844      */

845     public boolean getUseContextClassLoader() {
846
847         return useContextClassLoader;
848
849     }
850
851
852     /**
853      * Determine whether to use the Context ClassLoader (the one found by
854      * calling <code>Thread.currentThread().getContextClassLoader()</code>)
855      * to resolve/load classes that are defined in various rules. If not
856      * using Context ClassLoader, then the class-loading defaults to
857      * using the calling-class' ClassLoader.
858      *
859      * @param use determines whether to use Context ClassLoader.
860      */

861     public void setUseContextClassLoader(boolean use) {
862
863         useContextClassLoader = use;
864
865     }
866
867
868     /**
869      * Return the validating parser flag.
870      */

871     public boolean getValidating() {
872
873         return (this.validating);
874
875     }
876
877
878     /**
879      * Set the validating parser flag. This must be called before
880      * <code>parse()</code> is called the first time.
881      *
882      * @param validating The new validating parser flag.
883      */

884     public void setValidating(boolean validating) {
885
886         this.validating = validating;
887
888     }
889
890
891     /**
892      * Return the XMLReader to be used for parsing the input document.
893      *
894      * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a
895      * parser that contains a schema with a DTD.
896      * @exception SAXException if no XMLReader can be instantiated
897      */

898     public XMLReader JavaDoc getXMLReader() throws SAXException JavaDoc {
899         if (reader == null){
900             reader = getParser().getXMLReader();
901         }
902                                
903         reader.setDTDHandler(this);
904         reader.setContentHandler(this);
905         
906         if (entityResolver == null){
907             reader.setEntityResolver(this);
908         } else {
909             reader.setEntityResolver(entityResolver);
910         }
911         
912         reader.setErrorHandler(this);
913         return reader;
914     }
915
916     // ------------------------------------------------- ContentHandler Methods
917

918
919     /**
920      * Process notification of character data received from the body of
921      * an XML element.
922      *
923      * @param buffer The characters from the XML document
924      * @param start Starting offset into the buffer
925      * @param length Number of characters from the buffer
926      *
927      * @exception SAXException if a parsing error is to be reported
928      */

929     public void characters(char buffer[], int start, int length)
930             throws SAXException JavaDoc {
931
932         if (saxLog.isDebugEnabled()) {
933             saxLog.debug("characters(" + new String JavaDoc(buffer, start, length) + ")");
934         }
935
936         bodyText.append(buffer, start, length);
937
938     }
939
940
941     /**
942      * Process notification of the end of the document being reached.
943      *
944      * @exception SAXException if a parsing error is to be reported
945      */

946     public void endDocument() throws SAXException JavaDoc {
947
948         if (saxLog.isDebugEnabled()) {
949             if (getCount() > 1) {
950                 saxLog.debug("endDocument(): " + getCount() +
951                              " elements left");
952             } else {
953                 saxLog.debug("endDocument()");
954             }
955         }
956
957         while (getCount() > 1) {
958             pop();
959         }
960
961         // Fire "finish" events for all defined rules
962
Iterator JavaDoc rules = getRules().rules().iterator();
963         while (rules.hasNext()) {
964             Rule rule = (Rule) rules.next();
965             try {
966                 rule.finish();
967             } catch (Exception JavaDoc e) {
968                 log.error("Finish event threw exception", e);
969                 throw createSAXException(e);
970             } catch (Error JavaDoc e) {
971                 log.error("Finish event threw error", e);
972                 throw e;
973             }
974         }
975
976         // Perform final cleanup
977
clear();
978
979     }
980
981
982     /**
983      * Process notification of the end of an XML element being reached.
984      *
985      * @param namespaceURI - The Namespace URI, or the empty string if the
986      * element has no Namespace URI or if Namespace processing is not
987      * being performed.
988      * @param localName - The local name (without prefix), or the empty
989      * string if Namespace processing is not being performed.
990      * @param qName - The qualified XML 1.0 name (with prefix), or the
991      * empty string if qualified names are not available.
992      * @exception SAXException if a parsing error is to be reported
993      */

994     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
995                            String JavaDoc qName) throws SAXException JavaDoc {
996
997         boolean debug = log.isDebugEnabled();
998
999         if (debug) {
1000            if (saxLog.isDebugEnabled()) {
1001                saxLog.debug("endElement(" + namespaceURI + "," + localName +
1002                        "," + qName + ")");
1003            }
1004            log.debug(" match='" + match + "'");
1005            log.debug(" bodyText='" + bodyText + "'");
1006        }
1007
1008        // Parse system properties
1009
bodyText = updateBodyText(bodyText);
1010
1011        // the actual element name is either in localName or qName, depending
1012
// on whether the parser is namespace aware
1013
String JavaDoc name = localName;
1014        if ((name == null) || (name.length() < 1)) {
1015            name = qName;
1016        }
1017
1018        // Fire "body" events for all relevant rules
1019
List JavaDoc rules = (List JavaDoc) matches.pop();
1020        if ((rules != null) && (rules.size() > 0)) {
1021            String JavaDoc bodyText = this.bodyText.toString();
1022            for (int i = 0; i < rules.size(); i++) {
1023                try {
1024                    Rule rule = (Rule) rules.get(i);
1025                    if (debug) {
1026                        log.debug(" Fire body() for " + rule);
1027                    }
1028                    rule.body(namespaceURI, name, bodyText);
1029                } catch (Exception JavaDoc e) {
1030                    log.error("Body event threw exception", e);
1031                    throw createSAXException(e);
1032                } catch (Error JavaDoc e) {
1033                    log.error("Body event threw error", e);
1034                    throw e;
1035                }
1036            }
1037        } else {
1038            if (debug) {
1039                log.debug(" No rules found matching '" + match + "'.");
1040            }
1041        }
1042
1043        // Recover the body text from the surrounding element
1044
bodyText = (StringBuffer JavaDoc) bodyTexts.pop();
1045        if (debug) {
1046            log.debug(" Popping body text '" + bodyText.toString() + "'");
1047        }
1048
1049        // Fire "end" events for all relevant rules in reverse order
1050
if (rules != null) {
1051            for (int i = 0; i < rules.size(); i++) {
1052                int j = (rules.size() - i) - 1;
1053                try {
1054                    Rule rule = (Rule) rules.get(j);
1055                    if (debug) {
1056                        log.debug(" Fire end() for " + rule);
1057                    }
1058                    rule.end(namespaceURI, name);
1059                } catch (Exception JavaDoc e) {
1060                    log.error("End event threw exception", e);
1061                    throw createSAXException(e);
1062                } catch (Error JavaDoc e) {
1063                    log.error("End event threw error", e);
1064                    throw e;
1065                }
1066            }
1067        }
1068
1069        // Recover the previous match expression
1070
int slash = match.lastIndexOf('/');
1071        if (slash >= 0) {
1072            match = match.substring(0, slash);
1073        } else {
1074            match = "";
1075        }
1076
1077    }
1078
1079
1080    /**
1081     * Process notification that a namespace prefix is going out of scope.
1082     *
1083     * @param prefix Prefix that is going out of scope
1084     *
1085     * @exception SAXException if a parsing error is to be reported
1086     */

1087    public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
1088
1089        if (saxLog.isDebugEnabled()) {
1090            saxLog.debug("endPrefixMapping(" + prefix + ")");
1091        }
1092
1093        // Deregister this prefix mapping
1094
ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1095        if (stack == null) {
1096            return;
1097        }
1098        try {
1099            stack.pop();
1100            if (stack.empty())
1101                namespaces.remove(prefix);
1102        } catch (EmptyStackException JavaDoc e) {
1103            throw createSAXException("endPrefixMapping popped too many times");
1104        }
1105
1106    }
1107
1108
1109    /**
1110     * Process notification of ignorable whitespace received from the body of
1111     * an XML element.
1112     *
1113     * @param buffer The characters from the XML document
1114     * @param start Starting offset into the buffer
1115     * @param len Number of characters from the buffer
1116     *
1117     * @exception SAXException if a parsing error is to be reported
1118     */

1119    public void ignorableWhitespace(char buffer[], int start, int len)
1120            throws SAXException JavaDoc {
1121
1122        if (saxLog.isDebugEnabled()) {
1123            saxLog.debug("ignorableWhitespace(" +
1124                    new String JavaDoc(buffer, start, len) + ")");
1125        }
1126
1127        ; // No processing required
1128

1129    }
1130
1131
1132    /**
1133     * Process notification of a processing instruction that was encountered.
1134     *
1135     * @param target The processing instruction target
1136     * @param data The processing instruction data (if any)
1137     *
1138     * @exception SAXException if a parsing error is to be reported
1139     */

1140    public void processingInstruction(String JavaDoc target, String JavaDoc data)
1141            throws SAXException JavaDoc {
1142
1143        if (saxLog.isDebugEnabled()) {
1144            saxLog.debug("processingInstruction('" + target + "','" + data + "')");
1145        }
1146
1147        ; // No processing is required
1148

1149    }
1150
1151
1152    /**
1153     * Gets the document locator associated with our parser.
1154     *
1155     * @return the Locator supplied by the document parser
1156     */

1157    public Locator JavaDoc getDocumentLocator() {
1158
1159        return locator;
1160
1161    }
1162
1163    /**
1164     * Sets the document locator associated with our parser.
1165     *
1166     * @param locator The new locator
1167     */

1168    public void setDocumentLocator(Locator JavaDoc locator) {
1169
1170        if (saxLog.isDebugEnabled()) {
1171            saxLog.debug("setDocumentLocator(" + locator + ")");
1172        }
1173
1174        this.locator = locator;
1175
1176    }
1177
1178
1179    /**
1180     * Process notification of a skipped entity.
1181     *
1182     * @param name Name of the skipped entity
1183     *
1184     * @exception SAXException if a parsing error is to be reported
1185     */

1186    public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
1187
1188        if (saxLog.isDebugEnabled()) {
1189            saxLog.debug("skippedEntity(" + name + ")");
1190        }
1191
1192        ; // No processing required
1193

1194    }
1195
1196
1197    /**
1198     * Process notification of the beginning of the document being reached.
1199     *
1200     * @exception SAXException if a parsing error is to be reported
1201     */

1202    public void startDocument() throws SAXException JavaDoc {
1203
1204        if (saxLog.isDebugEnabled()) {
1205            saxLog.debug("startDocument()");
1206        }
1207
1208        // ensure that the digester is properly configured, as
1209
// the digester could be used as a SAX ContentHandler
1210
// rather than via the parse() methods.
1211
configure();
1212    }
1213
1214
1215    /**
1216     * Process notification of the start of an XML element being reached.
1217     *
1218     * @param namespaceURI The Namespace URI, or the empty string if the element
1219     * has no Namespace URI or if Namespace processing is not being performed.
1220     * @param localName The local name (without prefix), or the empty
1221     * string if Namespace processing is not being performed.
1222     * @param qName The qualified name (with prefix), or the empty
1223     * string if qualified names are not available.\
1224     * @param list The attributes attached to the element. If there are
1225     * no attributes, it shall be an empty Attributes object.
1226     * @exception SAXException if a parsing error is to be reported
1227     */

1228    public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
1229                             String JavaDoc qName, Attributes JavaDoc list)
1230            throws SAXException JavaDoc {
1231        boolean debug = log.isDebugEnabled();
1232        
1233        if (saxLog.isDebugEnabled()) {
1234            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
1235                    qName + ")");
1236        }
1237        
1238        // Parse system properties
1239
list = updateAttributes(list);
1240        
1241        // Save the body text accumulated for our surrounding element
1242
bodyTexts.push(bodyText);
1243        if (debug) {
1244            log.debug(" Pushing body text '" + bodyText.toString() + "'");
1245        }
1246        bodyText = new StringBuffer JavaDoc();
1247
1248        // the actual element name is either in localName or qName, depending
1249
// on whether the parser is namespace aware
1250
String JavaDoc name = localName;
1251        if ((name == null) || (name.length() < 1)) {
1252            name = qName;
1253        }
1254
1255        // Compute the current matching rule
1256
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(match);
1257        if (match.length() > 0) {
1258            sb.append('/');
1259        }
1260        sb.append(name);
1261        match = sb.toString();
1262        if (debug) {
1263            log.debug(" New match='" + match + "'");
1264        }
1265
1266        // Fire "begin" events for all relevant rules
1267
List JavaDoc rules = getRules().match(namespaceURI, match);
1268        matches.push(rules);
1269        if ((rules != null) && (rules.size() > 0)) {
1270            for (int i = 0; i < rules.size(); i++) {
1271                try {
1272                    Rule rule = (Rule) rules.get(i);
1273                    if (debug) {
1274                        log.debug(" Fire begin() for " + rule);
1275                    }
1276                    rule.begin(namespaceURI, name, list);
1277                } catch (Exception JavaDoc e) {
1278                    log.error("Begin event threw exception", e);
1279                    throw createSAXException(e);
1280                } catch (Error JavaDoc e) {
1281                    log.error("Begin event threw error", e);
1282                    throw e;
1283                }
1284            }
1285        } else {
1286            if (debug) {
1287                log.debug(" No rules found matching '" + match + "'.");
1288            }
1289        }
1290
1291    }
1292
1293
1294    /**
1295     * Process notification that a namespace prefix is coming in to scope.
1296     *
1297     * @param prefix Prefix that is being declared
1298     * @param namespaceURI Corresponding namespace URI being mapped to
1299     *
1300     * @exception SAXException if a parsing error is to be reported
1301     */

1302    public void startPrefixMapping(String JavaDoc prefix, String JavaDoc namespaceURI)
1303            throws SAXException JavaDoc {
1304
1305        if (saxLog.isDebugEnabled()) {
1306            saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
1307        }
1308
1309        // Register this prefix mapping
1310
ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1311        if (stack == null) {
1312            stack = new ArrayStack();
1313            namespaces.put(prefix, stack);
1314        }
1315        stack.push(namespaceURI);
1316
1317    }
1318
1319
1320    // ----------------------------------------------------- DTDHandler Methods
1321

1322
1323    /**
1324     * Receive notification of a notation declaration event.
1325     *
1326     * @param name The notation name
1327     * @param publicId The public identifier (if any)
1328     * @param systemId The system identifier (if any)
1329     */

1330    public void notationDecl(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) {
1331
1332        if (saxLog.isDebugEnabled()) {
1333            saxLog.debug("notationDecl(" + name + "," + publicId + "," +
1334                    systemId + ")");
1335        }
1336
1337    }
1338
1339
1340    /**
1341     * Receive notification of an unparsed entity declaration event.
1342     *
1343     * @param name The unparsed entity name
1344     * @param publicId The public identifier (if any)
1345     * @param systemId The system identifier (if any)
1346     * @param notation The name of the associated notation
1347     */

1348    public void unparsedEntityDecl(String JavaDoc name, String JavaDoc publicId,
1349                                   String JavaDoc systemId, String JavaDoc notation) {
1350
1351        if (saxLog.isDebugEnabled()) {
1352            saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," +
1353                    systemId + "," + notation + ")");
1354        }
1355
1356    }
1357
1358
1359    // ----------------------------------------------- EntityResolver Methods
1360

1361    /**
1362     * Set the <code>EntityResolver</code> used by SAX when resolving
1363     * public id and system id.
1364     * This must be called before the first call to <code>parse()</code>.
1365     * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
1366     */

1367    public void setEntityResolver(EntityResolver JavaDoc entityResolver){
1368        this.entityResolver = entityResolver;
1369    }
1370    
1371    
1372    /**
1373     * Return the Entity Resolver used by the SAX parser.
1374     * @return Return the Entity Resolver used by the SAX parser.
1375     */

1376    public EntityResolver JavaDoc getEntityResolver(){
1377        return entityResolver;
1378    }
1379
1380    /**
1381     * Resolve the requested external entity.
1382     *
1383     * @param publicId The public identifier of the entity being referenced
1384     * @param systemId The system identifier of the entity being referenced
1385     *
1386     * @exception SAXException if a parsing exception occurs
1387     *
1388     */

1389    public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
1390            throws SAXException JavaDoc {
1391                
1392        if (saxLog.isDebugEnabled()) {
1393            saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')");
1394        }
1395        
1396        if (publicId != null)
1397            this.publicId = publicId;
1398                                       
1399        // Has this system identifier been registered?
1400
String JavaDoc entityURL = null;
1401        if (publicId != null) {
1402            entityURL = (String JavaDoc) entityValidator.get(publicId);
1403        }
1404         
1405        // Redirect the schema location to a local destination
1406
if (schemaLocation != null && entityURL == null && systemId != null){
1407            entityURL = (String JavaDoc)entityValidator.get(systemId);
1408        }
1409
1410        if (entityURL == null) {
1411            if (systemId == null) {
1412                // cannot resolve
1413
if (log.isDebugEnabled()) {
1414                    log.debug(" Cannot resolve entity: '" + entityURL + "'");
1415                }
1416                return (null);
1417                
1418            } else {
1419                // try to resolve using system ID
1420
if (log.isDebugEnabled()) {
1421                    log.debug(" Trying to resolve using system ID '" + systemId + "'");
1422                }
1423                entityURL = systemId;
1424            }
1425        }
1426        
1427        // Return an input source to our alternative URL
1428
if (log.isDebugEnabled()) {
1429            log.debug(" Resolving to alternate DTD '" + entityURL + "'");
1430        }
1431        
1432        try {
1433            return (new InputSource JavaDoc(entityURL));
1434        } catch (Exception JavaDoc e) {
1435            throw createSAXException(e);
1436        }
1437    }
1438
1439
1440    // ------------------------------------------------- ErrorHandler Methods
1441

1442
1443    /**
1444     * Forward notification of a parsing error to the application supplied
1445     * error handler (if any).
1446     *
1447     * @param exception The error information
1448     *
1449     * @exception SAXException if a parsing exception occurs
1450     */

1451    public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1452
1453        log.error("Parse Error at line " + exception.getLineNumber() +
1454                " column " + exception.getColumnNumber() + ": " +
1455                exception.getMessage(), exception);
1456        if (errorHandler != null) {
1457            errorHandler.error(exception);
1458        }
1459
1460    }
1461
1462
1463    /**
1464     * Forward notification of a fatal parsing error to the application
1465     * supplied error handler (if any).
1466     *
1467     * @param exception The fatal error information
1468     *
1469     * @exception SAXException if a parsing exception occurs
1470     */

1471    public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1472
1473        log.error("Parse Fatal Error at line " + exception.getLineNumber() +
1474                " column " + exception.getColumnNumber() + ": " +
1475                exception.getMessage(), exception);
1476        if (errorHandler != null) {
1477            errorHandler.fatalError(exception);
1478        }
1479
1480    }
1481
1482
1483    /**
1484     * Forward notification of a parse warning to the application supplied
1485     * error handler (if any).
1486     *
1487     * @param exception The warning information
1488     *
1489     * @exception SAXException if a parsing exception occurs
1490     */

1491    public void warning(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1492         if (errorHandler != null) {
1493            log.warn("Parse Warning Error at line " + exception.getLineNumber() +
1494                " column " + exception.getColumnNumber() + ": " +
1495                exception.getMessage(), exception);
1496            
1497            errorHandler.warning(exception);
1498        }
1499
1500    }
1501
1502
1503    // ------------------------------------------------------- Public Methods
1504

1505
1506    /**
1507     * Log a message to our associated logger.
1508     *
1509     * @param message The message to be logged
1510     * @deprecated Call getLogger() and use it's logging methods
1511     */

1512    public void log(String JavaDoc message) {
1513
1514        log.info(message);
1515
1516    }
1517
1518
1519    /**
1520     * Log a message and exception to our associated logger.
1521     *
1522     * @param message The message to be logged
1523     * @deprecated Call getLogger() and use it's logging methods
1524     */

1525    public void log(String JavaDoc message, Throwable JavaDoc exception) {
1526
1527        log.error(message, exception);
1528
1529    }
1530
1531
1532    /**
1533     * Parse the content of the specified file using this Digester. Returns
1534     * the root element from the object stack (if any).
1535     *
1536     * @param file File containing the XML data to be parsed
1537     *
1538     * @exception IOException if an input/output error occurs
1539     * @exception SAXException if a parsing exception occurs
1540     */

1541    public Object JavaDoc parse(File JavaDoc file) throws IOException JavaDoc, SAXException JavaDoc {
1542
1543        configure();
1544        InputSource JavaDoc input = new InputSource JavaDoc(new FileInputStream JavaDoc(file));
1545        input.setSystemId("file://" + file.getAbsolutePath());
1546        getXMLReader().parse(input);
1547        return (root);
1548
1549    }
1550    /**
1551     * Parse the content of the specified input source using this Digester.
1552     * Returns the root element from the object stack (if any).
1553     *
1554     * @param input Input source containing the XML data to be parsed
1555     *
1556     * @exception IOException if an input/output error occurs
1557     * @exception SAXException if a parsing exception occurs
1558     */

1559    public Object JavaDoc parse(InputSource JavaDoc input) throws IOException JavaDoc, SAXException JavaDoc {
1560 
1561        configure();
1562        getXMLReader().parse(input);
1563        return (root);
1564
1565    }
1566
1567
1568    /**
1569     * Parse the content of the specified input stream using this Digester.
1570     * Returns the root element from the object stack (if any).
1571     *
1572     * @param input Input stream containing the XML data to be parsed
1573     *
1574     * @exception IOException if an input/output error occurs
1575     * @exception SAXException if a parsing exception occurs
1576     */

1577    public Object JavaDoc parse(InputStream JavaDoc input) throws IOException JavaDoc, SAXException JavaDoc {
1578
1579        configure();
1580        InputSource JavaDoc is = new InputSource JavaDoc(input);
1581        getXMLReader().parse(is);
1582        return (root);
1583
1584    }
1585
1586
1587    /**
1588     * Parse the content of the specified reader using this Digester.
1589     * Returns the root element from the object stack (if any).
1590     *
1591     * @param reader Reader containing the XML data to be parsed
1592     *
1593     * @exception IOException if an input/output error occurs
1594     * @exception SAXException if a parsing exception occurs
1595     */

1596    public Object JavaDoc parse(Reader JavaDoc reader) throws IOException JavaDoc, SAXException JavaDoc {
1597
1598        configure();
1599        InputSource JavaDoc is = new InputSource JavaDoc(reader);
1600        getXMLReader().parse(is);
1601        return (root);
1602
1603    }
1604
1605
1606    /**
1607     * Parse the content of the specified URI using this Digester.
1608     * Returns the root element from the object stack (if any).
1609     *
1610     * @param uri URI containing the XML data to be parsed
1611     *
1612     * @exception IOException if an input/output error occurs
1613     * @exception SAXException if a parsing exception occurs
1614     */

1615    public Object JavaDoc parse(String JavaDoc uri) throws IOException JavaDoc, SAXException JavaDoc {
1616
1617        configure();
1618        InputSource JavaDoc is = new InputSource JavaDoc(uri);
1619        getXMLReader().parse(is);
1620        return (root);
1621
1622    }
1623
1624
1625    /**
1626     * <p>Register the specified DTD URL for the specified public identifier.
1627     * This must be called before the first call to <code>parse()</code>.
1628     * </p><p>
1629     * <code>Digester</code> contains an internal <code>EntityResolver</code>
1630     * implementation. This maps <code>PUBLICID</code>'s to URLs
1631     * (from which the resource will be loaded). A common use case for this
1632     * method is to register local URLs (possibly computed at runtime by a
1633     * classloader) for DTDs. This allows the performance advantage of using
1634     * a local version without having to ensure every <code>SYSTEM</code>
1635     * URI on every processed xml document is local. This implementation provides
1636     * only basic functionality. If more sophisticated features are required,
1637     * using {@link #setEntityResolver} to set a custom resolver is recommended.
1638     * </p><p>
1639     * <strong>Note:</strong> This method will have no effect when a custom
1640     * <code>EntityResolver</code> has been set. (Setting a custom
1641     * <code>EntityResolver</code> overrides the internal implementation.)
1642     * </p>
1643     * @param publicId Public identifier of the DTD to be resolved
1644     * @param entityURL The URL to use for reading this DTD
1645     */

1646    public void register(String JavaDoc publicId, String JavaDoc entityURL) {
1647
1648        if (log.isDebugEnabled()) {
1649            log.debug("register('" + publicId + "', '" + entityURL + "'");
1650        }
1651        entityValidator.put(publicId, entityURL);
1652
1653    }
1654
1655
1656    // --------------------------------------------------------- Rule Methods
1657

1658
1659    /**
1660     * <p>Register a new Rule matching the specified pattern.
1661     * This method sets the <code>Digester</code> property on the rule.</p>
1662     *
1663     * @param pattern Element matching pattern
1664     * @param rule Rule to be registered
1665     */

1666    public void addRule(String JavaDoc pattern, Rule rule) {
1667
1668        rule.setDigester(this);
1669        getRules().add(pattern, rule);
1670
1671    }
1672
1673
1674    /**
1675     * Register a set of Rule instances defined in a RuleSet.
1676     *
1677     * @param ruleSet The RuleSet instance to configure from
1678     */

1679    public void addRuleSet(RuleSet ruleSet) {
1680
1681        String JavaDoc oldNamespaceURI = getRuleNamespaceURI();
1682        String JavaDoc newNamespaceURI = ruleSet.getNamespaceURI();
1683        if (log.isDebugEnabled()) {
1684            if (newNamespaceURI == null) {
1685                log.debug("addRuleSet() with no namespace URI");
1686            } else {
1687                log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
1688            }
1689        }
1690        setRuleNamespaceURI(newNamespaceURI);
1691        ruleSet.addRuleInstances(this);
1692        setRuleNamespaceURI(oldNamespaceURI);
1693
1694    }
1695
1696
1697    /**
1698     * Add an "call method" rule for a method which accepts no arguments.
1699     *
1700     * @param pattern Element matching pattern
1701     * @param methodName Method name to be called
1702     * @see CallMethodRule
1703     */

1704    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName) {
1705
1706        addRule(
1707                pattern,
1708                new CallMethodRule(methodName));
1709
1710    }
1711
1712    /**
1713     * Add an "call method" rule for the specified parameters.
1714     *
1715     * @param pattern Element matching pattern
1716     * @param methodName Method name to be called
1717     * @param paramCount Number of expected parameters (or zero
1718     * for a single parameter from the body of this element)
1719     * @see CallMethodRule
1720     */

1721    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1722                              int paramCount) {
1723
1724        addRule(pattern,
1725                new CallMethodRule(methodName, paramCount));
1726
1727    }
1728
1729
1730    /**
1731     * Add an "call method" rule for the specified parameters.
1732     * If <code>paramCount</code> is set to zero the rule will use
1733     * the body of the matched element as the single argument of the
1734     * method, unless <code>paramTypes</code> is null or empty, in this
1735     * case the rule will call the specified method with no arguments.
1736     *
1737     * @param pattern Element matching pattern
1738     * @param methodName Method name to be called
1739     * @param paramCount Number of expected parameters (or zero
1740     * for a single parameter from the body of this element)
1741     * @param paramTypes Set of Java class names for the types
1742     * of the expected parameters
1743     * (if you wish to use a primitive type, specify the corresonding
1744     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1745     * for a <code>boolean</code> parameter)
1746     * @see CallMethodRule
1747     */

1748    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1749                              int paramCount, String JavaDoc paramTypes[]) {
1750
1751        addRule(pattern,
1752                new CallMethodRule(
1753                                    methodName,
1754                                    paramCount,
1755                                    paramTypes));
1756
1757    }
1758
1759
1760    /**
1761     * Add an "call method" rule for the specified parameters.
1762     * If <code>paramCount</code> is set to zero the rule will use
1763     * the body of the matched element as the single argument of the
1764     * method, unless <code>paramTypes</code> is null or empty, in this
1765     * case the rule will call the specified method with no arguments.
1766     *
1767     * @param pattern Element matching pattern
1768     * @param methodName Method name to be called
1769     * @param paramCount Number of expected parameters (or zero
1770     * for a single parameter from the body of this element)
1771     * @param paramTypes The Java class names of the arguments
1772     * (if you wish to use a primitive type, specify the corresonding
1773     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1774     * for a <code>boolean</code> parameter)
1775     * @see CallMethodRule
1776     */

1777    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1778                              int paramCount, Class JavaDoc paramTypes[]) {
1779
1780        addRule(pattern,
1781                new CallMethodRule(
1782                                    methodName,
1783                                    paramCount,
1784                                    paramTypes));
1785
1786    }
1787
1788
1789    /**
1790     * Add a "call parameter" rule for the specified parameters.
1791     *
1792     * @param pattern Element matching pattern
1793     * @param paramIndex Zero-relative parameter index to set
1794     * (from the body of this element)
1795     * @see CallParamRule
1796     */

1797    public void addCallParam(String JavaDoc pattern, int paramIndex) {
1798
1799        addRule(pattern,
1800                new CallParamRule(paramIndex));
1801
1802    }
1803
1804
1805    /**
1806     * Add a "call parameter" rule for the specified parameters.
1807     *
1808     * @param pattern Element matching pattern
1809     * @param paramIndex Zero-relative parameter index to set
1810     * (from the specified attribute)
1811     * @param attributeName Attribute whose value is used as the
1812     * parameter value
1813     * @see CallParamRule
1814     */

1815    public void addCallParam(String JavaDoc pattern, int paramIndex,
1816                             String JavaDoc attributeName) {
1817
1818        addRule(pattern,
1819                new CallParamRule(paramIndex, attributeName));
1820
1821    }
1822
1823
1824    /**
1825     * Add a "call parameter" rule.
1826     * This will either take a parameter from the stack
1827     * or from the current element body text.
1828     *
1829     * @param paramIndex The zero-relative parameter number
1830     * @param fromStack Should the call parameter be taken from the top of the stack?
1831     * @see CallParamRule
1832     */

1833    public void addCallParam(String JavaDoc pattern, int paramIndex, boolean fromStack) {
1834    
1835        addRule(pattern,
1836                new CallParamRule(paramIndex, fromStack));
1837      
1838    }
1839
1840    /**
1841     * Add a "call parameter" rule that sets a parameter from the stack.
1842     * This takes a parameter from the given position on the stack.
1843     *
1844     * @param paramIndex The zero-relative parameter number
1845     * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
1846     * where 0 is the top of the stack, 1 the next element down and so on
1847     * @see CallMethodRule
1848     */

1849    public void addCallParam(String JavaDoc pattern, int paramIndex, int stackIndex) {
1850    
1851        addRule(pattern,
1852                new CallParamRule(paramIndex, stackIndex));
1853      
1854    }
1855    
1856    /**
1857     * Add a "call parameter" rule that sets a parameter from the current
1858     * <code>Digester</code> matching path.
1859     * This is sometimes useful when using rules that support wildcards.
1860     *
1861     * @param pattern the pattern that this rule should match
1862     * @param paramIndex The zero-relative parameter number
1863     * @see CallMethodRule
1864     */

1865    public void addCallParamPath(String JavaDoc pattern,int paramIndex) {
1866        addRule(pattern, new PathCallParamRule(paramIndex));
1867    }
1868    
1869    /**
1870     * Add a "call parameter" rule that sets a parameter from a
1871     * caller-provided object. This can be used to pass constants such as
1872     * strings to methods; it can also be used to pass mutable objects,
1873     * providing ways for objects to do things like "register" themselves
1874     * with some shared object.
1875     * <p>
1876     * Note that when attempting to locate a matching method to invoke,
1877     * the true type of the paramObj is used, so that despite the paramObj
1878     * being passed in here as type Object, the target method can declare
1879     * its parameters as being the true type of the object (or some ancestor
1880     * type, according to the usual type-conversion rules).
1881     *
1882     * @param paramIndex The zero-relative parameter number
1883     * @param paramObj Any arbitrary object to be passed to the target
1884     * method.
1885     * @see CallMethodRule
1886     *
1887     * @since 1.6
1888     */

1889    public void addObjectParam(String JavaDoc pattern, int paramIndex,
1890                               Object JavaDoc paramObj) {
1891    
1892        addRule(pattern,
1893                new ObjectParamRule(paramIndex, paramObj));
1894      
1895    }
1896    
1897    /**
1898     * Add a "factory create" rule for the specified parameters.
1899     * Exceptions thrown during the object creation process will be propagated.
1900     *
1901     * @param pattern Element matching pattern
1902     * @param className Java class name of the object creation factory class
1903     * @see FactoryCreateRule
1904     */

1905    public void addFactoryCreate(String JavaDoc pattern, String JavaDoc className) {
1906
1907        addFactoryCreate(pattern, className, false);
1908
1909    }
1910
1911
1912    /**
1913     * Add a "factory create" rule for the specified parameters.
1914     * Exceptions thrown during the object creation process will be propagated.
1915     *
1916     * @param pattern Element matching pattern
1917     * @param clazz Java class of the object creation factory class
1918     * @see FactoryCreateRule
1919     */

1920    public void addFactoryCreate(String JavaDoc pattern, Class JavaDoc clazz) {
1921
1922        addFactoryCreate(pattern, clazz, false);
1923
1924    }
1925
1926
1927    /**
1928     * Add a "factory create" rule for the specified parameters.
1929     * Exceptions thrown during the object creation process will be propagated.
1930     *
1931     * @param pattern Element matching pattern
1932     * @param className Java class name of the object creation factory class
1933     * @param attributeName Attribute name which, if present, overrides the
1934     * value specified by <code>className</code>
1935     * @see FactoryCreateRule
1936     */

1937    public void addFactoryCreate(String JavaDoc pattern, String JavaDoc className,
1938                                 String JavaDoc attributeName) {
1939
1940        addFactoryCreate(pattern, className, attributeName, false);
1941
1942    }
1943
1944
1945    /**
1946     * Add a "factory create" rule for the specified parameters.
1947     * Exceptions thrown during the object creation process will be propagated.
1948     *
1949     * @param pattern Element matching pattern
1950     * @param clazz Java class of the object creation factory class
1951     * @param attributeName Attribute name which, if present, overrides the
1952     * value specified by <code>className</code>
1953     * @see FactoryCreateRule
1954     */

1955    public void addFactoryCreate(String JavaDoc pattern, Class JavaDoc clazz,
1956                                 String JavaDoc attributeName) {
1957
1958        addFactoryCreate(pattern, clazz, attributeName, false);
1959
1960    }
1961
1962
1963    /**
1964     * Add a "factory create" rule for the specified parameters.
1965     * Exceptions thrown during the object creation process will be propagated.
1966     *
1967     * @param pattern Element matching pattern
1968     * @param creationFactory Previously instantiated ObjectCreationFactory
1969     * to be utilized
1970     * @see FactoryCreateRule
1971     */

1972    public void addFactoryCreate(String JavaDoc pattern,
1973                                 ObjectCreationFactory creationFactory) {
1974
1975        addFactoryCreate(pattern, creationFactory, false);
1976
1977    }
1978
1979    /**
1980     * Add a "factory create" rule for the specified parameters.
1981     *
1982     * @param pattern Element matching pattern
1983     * @param className Java class name of the object creation factory class
1984     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
1985     * object creation will be ignored.
1986     * @see FactoryCreateRule
1987     */

1988    public void addFactoryCreate(
1989                                    String JavaDoc pattern,
1990                                    String JavaDoc className,
1991                                    boolean ignoreCreateExceptions) {
1992
1993        addRule(
1994                pattern,
1995                new FactoryCreateRule(className, ignoreCreateExceptions));
1996
1997    }
1998
1999
2000    /**
2001     * Add a "factory create" rule for the specified parameters.
2002     *
2003     * @param pattern Element matching pattern
2004     * @param clazz Java class of the object creation factory class
2005     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2006     * object creation will be ignored.
2007     * @see FactoryCreateRule
2008     */

2009    public void addFactoryCreate(
2010                                    String JavaDoc pattern,
2011                                    Class JavaDoc clazz,
2012                                    boolean ignoreCreateExceptions) {
2013
2014        addRule(
2015                pattern,
2016                new FactoryCreateRule(clazz, ignoreCreateExceptions));
2017
2018    }
2019
2020
2021    /**
2022     * Add a "factory create" rule for the specified parameters.
2023     *
2024     * @param pattern Element matching pattern
2025     * @param className Java class name of the object creation factory class
2026     * @param attributeName Attribute name which, if present, overrides the
2027     * value specified by <code>className</code>
2028     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2029     * object creation will be ignored.
2030     * @see FactoryCreateRule
2031     */

2032    public void addFactoryCreate(
2033                                String JavaDoc pattern,
2034                                String JavaDoc className,
2035                                String JavaDoc attributeName,
2036                                boolean ignoreCreateExceptions) {
2037
2038        addRule(
2039                pattern,
2040                new FactoryCreateRule(className, attributeName, ignoreCreateExceptions));
2041
2042    }
2043
2044
2045    /**
2046     * Add a "factory create" rule for the specified parameters.
2047     *
2048     * @param pattern Element matching pattern
2049     * @param clazz Java class of the object creation factory class
2050     * @param attributeName Attribute name which, if present, overrides the
2051     * value specified by <code>className</code>
2052     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2053     * object creation will be ignored.
2054     * @see FactoryCreateRule
2055     */

2056    public void addFactoryCreate(
2057                                    String JavaDoc pattern,
2058                                    Class JavaDoc clazz,
2059                                    String JavaDoc attributeName,
2060                                    boolean ignoreCreateExceptions) {
2061
2062        addRule(
2063                pattern,
2064                new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions));
2065
2066    }
2067
2068
2069    /**
2070     * Add a "factory create" rule for the specified parameters.
2071     *
2072     * @param pattern Element matching pattern
2073     * @param creationFactory Previously instantiated ObjectCreationFactory
2074     * to be utilized
2075     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2076     * object creation will be ignored.
2077     * @see FactoryCreateRule
2078     */

2079    public void addFactoryCreate(String JavaDoc pattern,
2080                                 ObjectCreationFactory creationFactory,
2081                                 boolean ignoreCreateExceptions) {
2082
2083        creationFactory.setDigester(this);
2084        addRule(pattern,
2085                new FactoryCreateRule(creationFactory, ignoreCreateExceptions));
2086
2087    }
2088
2089    /**
2090     * Add an "object create" rule for the specified parameters.
2091     *
2092     * @param pattern Element matching pattern
2093     * @param className Java class name to be created
2094     * @see ObjectCreateRule
2095     */

2096    public void addObjectCreate(String JavaDoc pattern, String JavaDoc className) {
2097
2098        addRule(pattern,
2099                new ObjectCreateRule(className));
2100
2101    }
2102
2103
2104    /**
2105     * Add an "object create" rule for the specified parameters.
2106     *
2107     * @param pattern Element matching pattern
2108     * @param clazz Java class to be created
2109     * @see ObjectCreateRule
2110     */

2111    public void addObjectCreate(String JavaDoc pattern, Class JavaDoc clazz) {
2112
2113        addRule(pattern,
2114                new ObjectCreateRule(clazz));
2115
2116    }
2117
2118
2119    /**
2120     * Add an "object create" rule for the specified parameters.
2121     *
2122     * @param pattern Element matching pattern
2123     * @param className Default Java class name to be created
2124     * @param attributeName Attribute name that optionally overrides
2125     * the default Java class name to be created
2126     * @see ObjectCreateRule
2127     */

2128    public void addObjectCreate(String JavaDoc pattern, String JavaDoc className,
2129                                String JavaDoc attributeName) {
2130
2131        addRule(pattern,
2132                new ObjectCreateRule(className, attributeName));
2133
2134    }
2135
2136
2137    /**
2138     * Add an "object create" rule for the specified parameters.
2139     *
2140     * @param pattern Element matching pattern
2141     * @param attributeName Attribute name that optionally overrides
2142     * @param clazz Default Java class to be created
2143     * the default Java class name to be created
2144     * @see ObjectCreateRule
2145     */

2146    public void addObjectCreate(String JavaDoc pattern,
2147                                String JavaDoc attributeName,
2148                                Class JavaDoc clazz) {
2149
2150        addRule(pattern,
2151                new ObjectCreateRule(attributeName, clazz));
2152
2153    }
2154
2155    /**
2156     * Add a "set next" rule for the specified parameters.
2157     *
2158     * @param pattern Element matching pattern
2159     * @param methodName Method name to call on the parent element
2160     * @see SetNextRule
2161     */

2162    public void addSetNext(String JavaDoc pattern, String JavaDoc methodName) {
2163
2164        addRule(pattern,
2165                new SetNextRule(methodName));
2166
2167    }
2168
2169
2170    /**
2171     * Add a "set next" rule for the specified parameters.
2172     *
2173     * @param pattern Element matching pattern
2174     * @param methodName Method name to call on the parent element
2175     * @param paramType Java class name of the expected parameter type
2176     * (if you wish to use a primitive type, specify the corresonding
2177     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2178     * for a <code>boolean</code> parameter)
2179     * @see SetNextRule
2180     */

2181    public void addSetNext(String JavaDoc pattern, String JavaDoc methodName,
2182                           String JavaDoc paramType) {
2183
2184        addRule(pattern,
2185                new SetNextRule(methodName, paramType));
2186
2187    }
2188
2189
2190    /**
2191     * Add {@link SetRootRule} with the specified parameters.
2192     *
2193     * @param pattern Element matching pattern
2194     * @param methodName Method name to call on the root object
2195     * @see SetRootRule
2196     */

2197    public void addSetRoot(String JavaDoc pattern, String JavaDoc methodName) {
2198
2199        addRule(pattern,
2200                new SetRootRule(methodName));
2201
2202    }
2203
2204
2205    /**
2206     * Add {@link SetRootRule} with the specified parameters.
2207     *
2208     * @param pattern Element matching pattern
2209     * @param methodName Method name to call on the root object
2210     * @param paramType Java class name of the expected parameter type
2211     * @see SetRootRule
2212     */

2213    public void addSetRoot(String JavaDoc pattern, String JavaDoc methodName,
2214                           String JavaDoc paramType) {
2215
2216        addRule(pattern,
2217                new SetRootRule(methodName, paramType));
2218
2219    }
2220
2221    /**
2222     * Add a "set properties" rule for the specified parameters.
2223     *
2224     * @param pattern Element matching pattern
2225     * @see SetPropertiesRule
2226     */

2227    public void addSetProperties(String JavaDoc pattern) {
2228
2229        addRule(pattern,
2230                new SetPropertiesRule());
2231
2232    }
2233
2234    /**
2235     * Add a "set properties" rule with a single overridden parameter.
2236     * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
2237     *
2238     * @param pattern Element matching pattern
2239     * @param attributeName map this attribute
2240     * @param propertyName to this property
2241     * @see SetPropertiesRule
2242     */

2243    public void addSetProperties(
2244                                String JavaDoc pattern,
2245                                String JavaDoc attributeName,
2246                                String JavaDoc propertyName) {
2247
2248        addRule(pattern,
2249                new SetPropertiesRule(attributeName, propertyName));
2250
2251    }
2252
2253    /**
2254     * Add a "set properties" rule with overridden parameters.
2255     * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
2256     *
2257     * @param pattern Element matching pattern
2258     * @param attributeNames names of attributes with custom mappings
2259     * @param propertyNames property names these attributes map to
2260     * @see SetPropertiesRule
2261     */

2262    public void addSetProperties(
2263                                String JavaDoc pattern,
2264                                String JavaDoc [] attributeNames,
2265                                String JavaDoc [] propertyNames) {
2266
2267        addRule(pattern,
2268                new SetPropertiesRule(attributeNames, propertyNames));
2269
2270    }
2271
2272
2273    /**
2274     * Add a "set property" rule for the specified parameters.
2275     *
2276     * @param pattern Element matching pattern
2277     * @param name Attribute name containing the property name to be set
2278     * @param value Attribute name containing the property value to set
2279     * @see SetPropertyRule
2280     */

2281    public void addSetProperty(String JavaDoc pattern, String JavaDoc name, String JavaDoc value) {
2282
2283        addRule(pattern,
2284                new SetPropertyRule(name, value));
2285
2286    }
2287
2288
2289    /**
2290     * Add a "set top" rule for the specified parameters.
2291     *
2292     * @param pattern Element matching pattern
2293     * @param methodName Method name to call on the parent element
2294     * @see SetTopRule
2295     */

2296    public void addSetTop(String JavaDoc pattern, String JavaDoc methodName) {
2297
2298        addRule(pattern,
2299                new SetTopRule(methodName));
2300
2301    }
2302
2303
2304    /**
2305     * Add a "set top" rule for the specified parameters.
2306     *
2307     * @param pattern Element matching pattern
2308     * @param methodName Method name to call on the parent element
2309     * @param paramType Java class name of the expected parameter type
2310     * (if you wish to use a primitive type, specify the corresonding
2311     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2312     * for a <code>boolean</code> parameter)
2313     * @see SetTopRule
2314     */

2315    public void addSetTop(String JavaDoc pattern, String JavaDoc methodName,
2316                          String JavaDoc paramType) {
2317
2318        addRule(pattern,
2319                new SetTopRule(methodName, paramType));
2320
2321    }
2322
2323
2324    // --------------------------------------------------- Object Stack Methods
2325

2326
2327    /**
2328     * Clear the current contents of the object stack.
2329     * <p>
2330     * Calling this method <i>might</i> allow another document of the same type
2331     * to be correctly parsed. However this method was not intended for this
2332     * purpose. In general, a separate Digester object should be created for
2333     * each document to be parsed.
2334     */

2335    public void clear() {
2336
2337        match = "";
2338        bodyTexts.clear();
2339        params.clear();
2340        publicId = null;
2341        stack.clear();
2342        log = null;
2343        saxLog = null;
2344        configured = false;
2345        
2346    }
2347
2348    
2349    public void reset() {
2350        root = null;
2351        setErrorHandler(null);
2352        clear();
2353    }
2354
2355
2356    /**
2357     * Return the top object on the stack without removing it. If there are
2358     * no objects on the stack, return <code>null</code>.
2359     */

2360    public Object JavaDoc peek() {
2361
2362        try {
2363            return (stack.peek());
2364        } catch (EmptyStackException JavaDoc e) {
2365            log.warn("Empty stack (returning null)");
2366            return (null);
2367        }
2368
2369    }
2370
2371
2372    /**
2373     * Return the n'th object down the stack, where 0 is the top element
2374     * and [getCount()-1] is the bottom element. If the specified index
2375     * is out of range, return <code>null</code>.
2376     *
2377     * @param n Index of the desired element, where 0 is the top of the stack,
2378     * 1 is the next element down, and so on.
2379     */

2380    public Object JavaDoc peek(int n) {
2381
2382        try {
2383            return (stack.peek(n));
2384        } catch (EmptyStackException JavaDoc e) {
2385            log.warn("Empty stack (returning null)");
2386            return (null);
2387        }
2388
2389    }
2390
2391
2392    /**
2393     * Pop the top object off of the stack, and return it. If there are
2394     * no objects on the stack, return <code>null</code>.
2395     */

2396    public Object JavaDoc pop() {
2397
2398        try {
2399            return (stack.pop());
2400        } catch (EmptyStackException JavaDoc e) {
2401            log.warn("Empty stack (returning null)");
2402            return (null);
2403        }
2404
2405    }
2406
2407
2408    /**
2409     * Push a new object onto the top of the object stack.
2410     *
2411     * @param object The new object
2412     */

2413    public void push(Object JavaDoc object) {
2414
2415        if (stack.size() == 0) {
2416            root = object;
2417        }
2418        stack.push(object);
2419
2420    }
2421
2422    /**
2423     * Pushes the given object onto the stack with the given name.
2424     * If no stack already exists with the given name then one will be created.
2425     *
2426     * @param stackName the name of the stack onto which the object should be pushed
2427     * @param value the Object to be pushed onto the named stack.
2428     *
2429     * @since 1.6
2430     */

2431    public void push(String JavaDoc stackName, Object JavaDoc value) {
2432        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2433        if (namedStack == null) {
2434            namedStack = new ArrayStack();
2435            stacksByName.put(stackName, namedStack);
2436        }
2437        namedStack.push(value);
2438    }
2439
2440    /**
2441     * <p>Pops (gets and removes) the top object from the stack with the given name.</p>
2442     *
2443     * <p><strong>Note:</strong> a stack is considered empty
2444     * if no objects have been pushed onto it yet.</p>
2445     *
2446     * @param stackName the name of the stack from which the top value is to be popped
2447     * @return the top <code>Object</code> on the stack or or null if the stack is either
2448     * empty or has not been created yet
2449     * @throws EmptyStackException if the named stack is empty
2450     *
2451     * @since 1.6
2452     */

2453    public Object JavaDoc pop(String JavaDoc stackName) {
2454        Object JavaDoc result = null;
2455        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2456        if (namedStack == null) {
2457            if (log.isDebugEnabled()) {
2458                log.debug("Stack '" + stackName + "' is empty");
2459            }
2460            throw new EmptyStackException JavaDoc();
2461            
2462        } else {
2463        
2464            result = namedStack.pop();
2465        }
2466        return result;
2467    }
2468    
2469    /**
2470     * <p>Gets the top object from the stack with the given name.
2471     * This method does not remove the object from the stack.
2472     * </p>
2473     * <p><strong>Note:</strong> a stack is considered empty
2474     * if no objects have been pushed onto it yet.</p>
2475     *
2476     * @param stackName the name of the stack to be peeked
2477     * @return the top <code>Object</code> on the stack or null if the stack is either
2478     * empty or has not been created yet
2479     * @throws EmptyStackException if the named stack is empty
2480     *
2481     * @since 1.6
2482     */

2483    public Object JavaDoc peek(String JavaDoc stackName) {
2484        Object JavaDoc result = null;
2485        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2486        if (namedStack == null ) {
2487            if (log.isDebugEnabled()) {
2488                log.debug("Stack '" + stackName + "' is empty");
2489            }
2490            throw new EmptyStackException JavaDoc();
2491        
2492        } else {
2493        
2494            result = namedStack.peek();
2495        }
2496        return result;
2497    }
2498
2499    /**
2500     * <p>Is the stack with the given name empty?</p>
2501     * <p><strong>Note:</strong> a stack is considered empty
2502     * if no objects have been pushed onto it yet.</p>
2503     * @param stackName the name of the stack whose emptiness
2504     * should be evaluated
2505     * @return true if the given stack if empty
2506     *
2507     * @since 1.6
2508     */

2509    public boolean isEmpty(String JavaDoc stackName) {
2510        boolean result = true;
2511        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2512        if (namedStack != null ) {
2513            result = namedStack.isEmpty();
2514        }
2515        return result;
2516    }
2517    
2518    /**
2519     * When the Digester is being used as a SAXContentHandler,
2520     * this method allows you to access the root object that has been
2521     * created after parsing.
2522     *
2523     * @return the root object that has been created after parsing
2524     * or null if the digester has not parsed any XML yet.
2525     */

2526    public Object JavaDoc getRoot() {
2527        return root;
2528    }
2529    
2530
2531    // ------------------------------------------------ Parameter Stack Methods
2532

2533
2534    // ------------------------------------------------------ Protected Methods
2535

2536
2537    /**
2538     * <p>
2539     * Provide a hook for lazy configuration of this <code>Digester</code>
2540     * instance. The default implementation does nothing, but subclasses
2541     * can override as needed.
2542     * </p>
2543     *
2544     * <p>
2545     * <strong>Note</strong> This method may be called more than once.
2546     * Once only initialization code should be placed in {@link #initialize}
2547     * or the code should take responsibility by checking and setting the
2548     * {@link #configured} flag.
2549     * </p>
2550     */

2551    protected void configure() {
2552
2553        // Do not configure more than once
2554
if (configured) {
2555            return;
2556        }
2557
2558        log = LogFactory.getLog("org.apache.commons.digester.Digester");
2559        saxLog = LogFactory.getLog("org.apache.commons.digester.Digester.sax");
2560
2561        // Perform lazy configuration as needed
2562
initialize(); // call hook method for subclasses that want to be initialized once only
2563
// Nothing else required by default
2564

2565        // Set the configuration flag to avoid repeating
2566
configured = true;
2567
2568    }
2569    
2570    /**
2571     * <p>
2572     * Provides a hook for lazy initialization of this <code>Digester</code>
2573     * instance.
2574     * The default implementation does nothing, but subclasses
2575     * can override as needed.
2576     * Digester (by default) only calls this method once.
2577     * </p>
2578     *
2579     * <p>
2580     * <strong>Note</strong> This method will be called by {@link #configure}
2581     * only when the {@link #configured} flag is false.
2582     * Subclasses that override <code>configure</code> or who set <code>configured</code>
2583     * may find that this method may be called more than once.
2584     * </p>
2585     *
2586     * @since 1.6
2587     */

2588    protected void initialize() {
2589
2590        // Perform lazy initialization as needed
2591
; // Nothing required by default
2592

2593    }
2594
2595    // -------------------------------------------------------- Package Methods
2596

2597
2598    /**
2599     * Return the set of DTD URL registrations, keyed by public identifier.
2600     */

2601    Map JavaDoc getRegistrations() {
2602
2603        return (entityValidator);
2604
2605    }
2606
2607
2608    /**
2609     * Return the set of rules that apply to the specified match position.
2610     * The selected rules are those that match exactly, or those rules
2611     * that specify a suffix match and the tail of the rule matches the
2612     * current match position. Exact matches have precedence over
2613     * suffix matches, then (among suffix matches) the longest match
2614     * is preferred.
2615     *
2616     * @param match The current match position
2617     *
2618     * @deprecated Call <code>match()</code> on the <code>Rules</code>
2619     * implementation returned by <code>getRules()</code>
2620     */

2621    List JavaDoc getRules(String JavaDoc match) {
2622
2623        return (getRules().match(match));
2624
2625    }
2626
2627
2628    /**
2629     * <p>Return the top object on the parameters stack without removing it. If there are
2630     * no objects on the stack, return <code>null</code>.</p>
2631     *
2632     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2633     * See {@link #params}.</p>
2634     */

2635    public Object JavaDoc peekParams() {
2636
2637        try {
2638            return (params.peek());
2639        } catch (EmptyStackException JavaDoc e) {
2640            log.warn("Empty stack (returning null)");
2641            return (null);
2642        }
2643
2644    }
2645
2646
2647    /**
2648     * <p>Return the n'th object down the parameters stack, where 0 is the top element
2649     * and [getCount()-1] is the bottom element. If the specified index
2650     * is out of range, return <code>null</code>.</p>
2651     *
2652     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2653     * See {@link #params}.</p>
2654     *
2655     * @param n Index of the desired element, where 0 is the top of the stack,
2656     * 1 is the next element down, and so on.
2657     */

2658    public Object JavaDoc peekParams(int n) {
2659
2660        try {
2661            return (params.peek(n));
2662        } catch (EmptyStackException JavaDoc e) {
2663            log.warn("Empty stack (returning null)");
2664            return (null);
2665        }
2666
2667    }
2668
2669
2670    /**
2671     * <p>Pop the top object off of the parameters stack, and return it. If there are
2672     * no objects on the stack, return <code>null</code>.</p>
2673     *
2674     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2675     * See {@link #params}.</p>
2676     */

2677    public Object JavaDoc popParams() {
2678
2679        try {
2680            if (log.isTraceEnabled()) {
2681                log.trace("Popping params");
2682            }
2683            return (params.pop());
2684        } catch (EmptyStackException JavaDoc e) {
2685            log.warn("Empty stack (returning null)");
2686            return (null);
2687        }
2688
2689    }
2690
2691
2692    /**
2693     * <p>Push a new object onto the top of the parameters stack.</p>
2694     *
2695     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2696     * See {@link #params}.</p>
2697     *
2698     * @param object The new object
2699     */

2700    public void pushParams(Object JavaDoc object) {
2701        if (log.isTraceEnabled()) {
2702            log.trace("Pushing params");
2703        }
2704        params.push(object);
2705
2706    }
2707
2708    /**
2709     * Create a SAX exception which also understands about the location in
2710     * the digester file where the exception occurs
2711     *
2712     * @return the new exception
2713     */

2714    public SAXException JavaDoc createSAXException(String JavaDoc message, Exception JavaDoc e) {
2715        if ((e != null) &&
2716            (e instanceof InvocationTargetException JavaDoc)) {
2717            Throwable JavaDoc t = ((InvocationTargetException JavaDoc) e).getTargetException();
2718            if ((t != null) && (t instanceof Exception JavaDoc)) {
2719                e = (Exception JavaDoc) t;
2720            }
2721        }
2722        if (locator != null) {
2723            String JavaDoc error = "Error at (" + locator.getLineNumber() + ", " +
2724                    locator.getColumnNumber() + ": " + message;
2725            if (e != null) {
2726                return new SAXParseException JavaDoc(error, locator, e);
2727            } else {
2728                return new SAXParseException JavaDoc(error, locator);
2729            }
2730        }
2731        log.error("No Locator!");
2732        if (e != null) {
2733            return new SAXException JavaDoc(message, e);
2734        } else {
2735            return new SAXException JavaDoc(message);
2736        }
2737    }
2738
2739    /**
2740     * Create a SAX exception which also understands about the location in
2741     * the digester file where the exception occurs
2742     *
2743     * @return the new exception
2744     */

2745    public SAXException JavaDoc createSAXException(Exception JavaDoc e) {
2746        if (e instanceof InvocationTargetException JavaDoc) {
2747            Throwable JavaDoc t = ((InvocationTargetException JavaDoc) e).getTargetException();
2748            if ((t != null) && (t instanceof Exception JavaDoc)) {
2749                e = (Exception JavaDoc) t;
2750            }
2751        }
2752        return createSAXException(e.getMessage(), e);
2753    }
2754
2755    /**
2756     * Create a SAX exception which also understands about the location in
2757     * the digester file where the exception occurs
2758     *
2759     * @return the new exception
2760     */

2761    public SAXException JavaDoc createSAXException(String JavaDoc message) {
2762        return createSAXException(message, null);
2763    }
2764    
2765
2766    // ------------------------------------------------------- Private Methods
2767

2768
2769   /**
2770     * Returns an attributes list which contains all the attributes
2771     * passed in, with any text of form "${xxx}" in an attribute value
2772     * replaced by the appropriate value from the system property.
2773     */

2774    private Attributes JavaDoc updateAttributes(Attributes JavaDoc list) {
2775
2776        if (list.getLength() == 0) {
2777            return list;
2778        }
2779        
2780        AttributesImpl JavaDoc newAttrs = new AttributesImpl JavaDoc(list);
2781        int nAttributes = newAttrs.getLength();
2782        for (int i = 0; i < nAttributes; ++i) {
2783            String JavaDoc value = newAttrs.getValue(i);
2784            try {
2785                String JavaDoc newValue =
2786                    IntrospectionUtils.replaceProperties(value, null, source);
2787                if (value != newValue) {
2788                    newAttrs.setValue(i, newValue);
2789                }
2790            }
2791            catch (Exception JavaDoc e) {
2792                // ignore - let the attribute have its original value
2793
}
2794        }
2795
2796        return newAttrs;
2797
2798    }
2799
2800
2801    /**
2802     * Return a new StringBuffer containing the same contents as the
2803     * input buffer, except that data of form ${varname} have been
2804     * replaced by the value of that var as defined in the system property.
2805     */

2806    private StringBuffer JavaDoc updateBodyText(StringBuffer JavaDoc bodyText) {
2807        String JavaDoc in = bodyText.toString();
2808        String JavaDoc out;
2809        try {
2810            out = IntrospectionUtils.replaceProperties(in, null, source);
2811        } catch(Exception JavaDoc e) {
2812            return bodyText; // return unchanged data
2813
}
2814
2815        if (out == in) {
2816            // No substitutions required. Don't waste memory creating
2817
// a new buffer
2818
return bodyText;
2819        } else {
2820            return new StringBuffer JavaDoc(out);
2821        }
2822    }
2823
2824
2825}
2826
Popular Tags