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