KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > digester > Digester


1 /* $Id: Digester.java 178371 2005-05-25 05:13:27Z skitching $
2  *
3  * Copyright 2001-2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.commons.digester;
19
20
21 import java.io.File JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.Reader JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.util.EmptyStackException JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Properties JavaDoc;
33
34 import javax.xml.parsers.ParserConfigurationException JavaDoc;
35 import javax.xml.parsers.SAXParser JavaDoc;
36 import javax.xml.parsers.SAXParserFactory JavaDoc;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.commons.collections.ArrayStack;
41
42 import org.xml.sax.Attributes JavaDoc;
43 import org.xml.sax.ContentHandler JavaDoc;
44 import org.xml.sax.EntityResolver JavaDoc;
45 import org.xml.sax.ErrorHandler JavaDoc;
46 import org.xml.sax.InputSource JavaDoc;
47 import org.xml.sax.Locator JavaDoc;
48 import org.xml.sax.SAXException JavaDoc;
49 import org.xml.sax.SAXNotRecognizedException JavaDoc;
50 import org.xml.sax.SAXNotSupportedException JavaDoc;
51 import org.xml.sax.SAXParseException JavaDoc;
52 import org.xml.sax.XMLReader 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     // --------------------------------------------------------- Constructors
82

83
84     /**
85      * Construct a new Digester with default properties.
86      */

87     public Digester() {
88
89         super();
90
91     }
92
93
94     /**
95      * Construct a new Digester, allowing a SAXParser to be passed in. This
96      * allows Digester to be used in environments which are unfriendly to
97      * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to
98      * James House (james@interobjective.com). This may help in places where
99      * you are able to load JAXP 1.1 classes yourself.
100      */

101     public Digester(SAXParser JavaDoc parser) {
102
103         super();
104
105         this.parser = parser;
106
107     }
108
109
110     /**
111      * Construct a new Digester, allowing an XMLReader to be passed in. This
112      * allows Digester to be used in environments which are unfriendly to
113      * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you
114      * have to configure namespace and validation support yourself, as these
115      * properties only affect the SAXParser and emtpy constructor.
116      */

117     public Digester(XMLReader JavaDoc reader) {
118
119         super();
120
121         this.reader = reader;
122
123     }
124
125
126     // --------------------------------------------------- Instance Variables
127

128
129     /**
130      * The body text of the current element.
131      */

132     protected StringBuffer JavaDoc bodyText = new StringBuffer JavaDoc();
133
134
135     /**
136      * The stack of body text string buffers for surrounding elements.
137      */

138     protected ArrayStack bodyTexts = new ArrayStack();
139
140
141     /**
142      * Stack whose elements are List objects, each containing a list of
143      * Rule objects as returned from Rules.getMatch(). As each xml element
144      * in the input is entered, the matching rules are pushed onto this
145      * stack. After the end tag is reached, the matches are popped again.
146      * The depth of is stack is therefore exactly the same as the current
147      * "nesting" level of the input xml.
148      *
149      * @since 1.6
150      */

151     protected ArrayStack matches = new ArrayStack(10);
152     
153     /**
154      * The class loader to use for instantiating application objects.
155      * If not specified, the context class loader, or the class loader
156      * used to load Digester itself, is used, based on the value of the
157      * <code>useContextClassLoader</code> variable.
158      */

159     protected ClassLoader JavaDoc classLoader = null;
160
161
162     /**
163      * Has this Digester been configured yet.
164      */

165     protected boolean configured = false;
166
167
168     /**
169      * The EntityResolver used by the SAX parser. By default it use this class
170      */

171     protected EntityResolver JavaDoc entityResolver;
172     
173     /**
174      * The URLs of entityValidator that have been registered, keyed by the public
175      * identifier that corresponds.
176      */

177     protected HashMap JavaDoc entityValidator = new HashMap JavaDoc();
178
179
180     /**
181      * The application-supplied error handler that is notified when parsing
182      * warnings, errors, or fatal errors occur.
183      */

184     protected ErrorHandler JavaDoc errorHandler = null;
185
186
187     /**
188      * The SAXParserFactory that is created the first time we need it.
189      */

190     protected SAXParserFactory JavaDoc factory = null;
191
192     /**
193      * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
194      */

195     protected String JavaDoc JAXP_SCHEMA_LANGUAGE =
196         "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
197     
198     
199     /**
200      * The Locator associated with our parser.
201      */

202     protected Locator JavaDoc locator = null;
203
204
205     /**
206      * The current match pattern for nested element processing.
207      */

208     protected String JavaDoc match = "";
209
210
211     /**
212      * Do we want a "namespace aware" parser.
213      */

214     protected boolean namespaceAware = false;
215
216
217     /**
218      * Registered namespaces we are currently processing. The key is the
219      * namespace prefix that was declared in the document. The value is an
220      * ArrayStack of the namespace URIs this prefix has been mapped to --
221      * the top Stack element is the most current one. (This architecture
222      * is required because documents can declare nested uses of the same
223      * prefix for different Namespace URIs).
224      */

225     protected HashMap JavaDoc namespaces = new HashMap JavaDoc();
226
227
228     /**
229      * The parameters stack being utilized by CallMethodRule and
230      * CallParamRule rules.
231      */

232     protected ArrayStack params = new ArrayStack();
233
234
235     /**
236      * The SAXParser we will use to parse the input stream.
237      */

238     protected SAXParser JavaDoc parser = null;
239
240
241     /**
242      * The public identifier of the DTD we are currently parsing under
243      * (if any).
244      */

245     protected String JavaDoc publicId = null;
246
247
248     /**
249      * The XMLReader used to parse digester rules.
250      */

251     protected XMLReader JavaDoc reader = null;
252
253
254     /**
255      * The "root" element of the stack (in other words, the last object
256      * that was popped.
257      */

258     protected Object JavaDoc root = null;
259
260
261     /**
262      * The <code>Rules</code> implementation containing our collection of
263      * <code>Rule</code> instances and associated matching policy. If not
264      * established before the first rule is added, a default implementation
265      * will be provided.
266      */

267     protected Rules rules = null;
268
269    /**
270      * The XML schema language to use for validating an XML instance. By
271      * default this value is set to <code>W3C_XML_SCHEMA</code>
272      */

273     protected String JavaDoc schemaLanguage = W3C_XML_SCHEMA;
274     
275         
276     /**
277      * The XML schema to use for validating an XML instance.
278      */

279     protected String JavaDoc schemaLocation = null;
280     
281     
282     /**
283      * The object stack being constructed.
284      */

285     protected ArrayStack stack = new ArrayStack();
286
287
288     /**
289      * Do we want to use the Context ClassLoader when loading classes
290      * for instantiating new objects. Default is <code>false</code>.
291      */

292     protected boolean useContextClassLoader = false;
293
294
295     /**
296      * Do we want to use a validating parser.
297      */

298     protected boolean validating = false;
299
300
301     /**
302      * The Log to which most logging calls will be made.
303      */

304     protected Log log =
305         LogFactory.getLog("org.apache.commons.digester.Digester");
306
307
308     /**
309      * The Log to which all SAX event related logging calls will be made.
310      */

311     protected Log saxLog =
312         LogFactory.getLog("org.apache.commons.digester.Digester.sax");
313     
314         
315     /**
316      * The schema language supported. By default, we use this one.
317      */

318     protected static final String JavaDoc W3C_XML_SCHEMA =
319         "http://www.w3.org/2001/XMLSchema";
320     
321     /**
322      * An optional class that substitutes values in attributes and body text.
323      * This may be null and so a null check is always required before use.
324      */

325     protected Substitutor substitutor;
326     
327     /** Stacks used for interrule communication, indexed by name String */
328     private HashMap JavaDoc stacksByName = new HashMap JavaDoc();
329     
330     /**
331      * If not null, then calls by the parser to this object's characters,
332      * startElement, endElement and processingInstruction methods are
333      * forwarded to the specified object. This is intended to allow rules
334      * to temporarily "take control" of the sax events. In particular,
335      * this is used by NodeCreateRule.
336      * <p>
337      * See setCustomContentHandler.
338      */

339     private ContentHandler JavaDoc customContentHandler = null;
340
341     // ------------------------------------------------------------- Properties
342

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

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

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

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

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

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

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

455     public void setDebug(int debug) {
456
457         ; // No action is taken
458

459     }
460
461
462     /**
463      * Return the error handler for this Digester.
464      */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

676     public SAXParser JavaDoc getParser() {
677
678         // Return the parser we already created (if any)
679
if (parser != null) {
680             return (parser);
681         }
682
683         // Create a new parser
684
try {
685             if (validating) {
686                 Properties JavaDoc properties = new Properties JavaDoc();
687                 properties.put("SAXParserFactory", getFactory());
688                 if (schemaLocation != null) {
689                     properties.put("schemaLocation", schemaLocation);
690                     properties.put("schemaLanguage", schemaLanguage);
691                 }
692                 parser = ParserFeatureSetterFactory.newSAXParser(properties); } else {
693                 parser = getFactory().newSAXParser();
694             }
695         } catch (Exception JavaDoc e) {
696             log.error("Digester.getParser: ", e);
697             return (null);
698         }
699
700         return (parser);
701
702     }
703
704
705     /**
706      * Return the current value of the specified property for the underlying
707      * <code>XMLReader</code> implementation.
708      * See <a HREF="http://www.saxproject.org">the saxproject website</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">the saxproject website</a>
730      * for information about the standard SAX2 properties.
731      *
732      * @param property Property name to be set
733      * @param value Property value to be set
734      *
735      * @exception SAXNotRecognizedException if the property name is
736      * not recognized
737      * @exception SAXNotSupportedException if the property name is
738      * recognized but not supported
739      */

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

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

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

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

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

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

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

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

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

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

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

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

897     public XMLReader JavaDoc getXMLReader() throws SAXException JavaDoc {
898         if (reader == null){
899             reader = getParser().getXMLReader();
900         }
901                                
902         reader.setDTDHandler(this);
903         reader.setContentHandler(this);
904         
905         if (entityResolver == null){
906             reader.setEntityResolver(this);
907         } else {
908             reader.setEntityResolver(entityResolver);
909         }
910         
911         reader.setErrorHandler(this);
912         return reader;
913     }
914
915     /**
916      * Gets the <code>Substitutor</code> used to convert attributes and body text.
917      * @return Substitutor, null if not substitutions are to be performed.
918      */

919     public Substitutor getSubstitutor() {
920         return substitutor;
921     }
922     
923     /**
924      * Sets the <code>Substitutor</code> to be used to convert attributes and body text.
925      * @param substitutor the Substitutor to be used to convert attributes and body text
926      * or null if not substitution of these values is to be performed.
927      */

928     public void setSubstitutor(Substitutor substitutor) {
929         this.substitutor = substitutor;
930     }
931
932     /*
933      * See setCustomContentHandler.
934      *
935      * @since 1.7
936      */

937     public ContentHandler JavaDoc getCustomContentHandler() {
938         return customContentHandler;
939     }
940
941     /**
942      * Redirects (or cancels redirecting) of SAX ContentHandler events to an
943      * external object.
944      * <p>
945      * When this object's customContentHandler is non-null, any SAX events
946      * received from the parser will simply be passed on to the specified
947      * object instead of this object handling them. This allows Rule classes
948      * to take control of the SAX event stream for a while in order to do
949      * custom processing. Such a rule should save the old value before setting
950      * a new one, and restore the old value in order to resume normal digester
951      * processing.
952      * <p>
953      * An example of a Rule which needs this feature is NodeCreateRule.
954      * <p>
955      * Note that saving the old value is probably not needed as it should always
956      * be null; a custom rule that wants to take control could only have been
957      * called when there was no custom content handler. But it seems cleaner
958      * to properly save/restore the value and maybe some day this will come in
959      * useful.
960      * <p>
961      * Note also that this is not quite equivalent to
962      * <pre>
963      * digester.getXMLReader().setContentHandler(handler)
964      * </pre>
965      * for these reasons:
966      * <ul>
967      * <li>Some xml parsers don't like having setContentHandler called after
968      * parsing has started. The Aelfred parser is one example.</li>
969      * <li>Directing the events via the Digester object potentially allows
970      * us to log information about those SAX events at the digester level.</li>
971      * </ul>
972      *
973      * @since 1.7
974      */

975     public void setCustomContentHandler(ContentHandler JavaDoc handler) {
976         customContentHandler = handler;
977     }
978
979     // ------------------------------------------------- ContentHandler Methods
980

981
982     /**
983      * Process notification of character data received from the body of
984      * an XML element.
985      *
986      * @param buffer The characters from the XML document
987      * @param start Starting offset into the buffer
988      * @param length Number of characters from the buffer
989      *
990      * @exception SAXException if a parsing error is to be reported
991      */

992     public void characters(char buffer[], int start, int length)
993             throws SAXException JavaDoc {
994
995         if (customContentHandler != null) {
996             // forward calls instead of handling them here
997
customContentHandler.characters(buffer, start, length);
998             return;
999         }
1000
1001        if (saxLog.isDebugEnabled()) {
1002            saxLog.debug("characters(" + new String JavaDoc(buffer, start, length) + ")");
1003        }
1004
1005        bodyText.append(buffer, start, length);
1006
1007    }
1008
1009
1010    /**
1011     * Process notification of the end of the document being reached.
1012     *
1013     * @exception SAXException if a parsing error is to be reported
1014     */

1015    public void endDocument() throws SAXException JavaDoc {
1016
1017        if (saxLog.isDebugEnabled()) {
1018            if (getCount() > 1) {
1019                saxLog.debug("endDocument(): " + getCount() +
1020                             " elements left");
1021            } else {
1022                saxLog.debug("endDocument()");
1023            }
1024        }
1025
1026        // Fire "finish" events for all defined rules
1027
Iterator JavaDoc rules = getRules().rules().