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().iterator();
1028        while (rules.hasNext()) {
1029            Rule rule = (Rule) rules.next();
1030            try {
1031                rule.finish();
1032            } catch (Exception JavaDoc e) {
1033                log.error("Finish event threw exception", e);
1034                throw createSAXException(e);
1035            } catch (Error JavaDoc e) {
1036                log.error("Finish event threw error", e);
1037                throw e;
1038            }
1039        }
1040
1041        // Perform final cleanup
1042
clear();
1043
1044    }
1045
1046
1047    /**
1048     * Process notification of the end of an XML element being reached.
1049     *
1050     * @param namespaceURI - The Namespace URI, or the empty string if the
1051     * element has no Namespace URI or if Namespace processing is not
1052     * being performed.
1053     * @param localName - The local name (without prefix), or the empty
1054     * string if Namespace processing is not being performed.
1055     * @param qName - The qualified XML 1.0 name (with prefix), or the
1056     * empty string if qualified names are not available.
1057     * @exception SAXException if a parsing error is to be reported
1058     */

1059    public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
1060                           String JavaDoc qName) throws SAXException JavaDoc {
1061
1062        if (customContentHandler != null) {
1063            // forward calls instead of handling them here
1064
customContentHandler.endElement(namespaceURI, localName, qName);
1065            return;
1066        }
1067
1068        boolean debug = log.isDebugEnabled();
1069
1070        if (debug) {
1071            if (saxLog.isDebugEnabled()) {
1072                saxLog.debug("endElement(" + namespaceURI + "," + localName +
1073                        "," + qName + ")");
1074            }
1075            log.debug(" match='" + match + "'");
1076            log.debug(" bodyText='" + bodyText + "'");
1077        }
1078
1079        // the actual element name is either in localName or qName, depending
1080
// on whether the parser is namespace aware
1081
String JavaDoc name = localName;
1082        if ((name == null) || (name.length() < 1)) {
1083            name = qName;
1084        }
1085
1086        // Fire "body" events for all relevant rules
1087
List JavaDoc rules = (List JavaDoc) matches.pop();
1088        if ((rules != null) && (rules.size() > 0)) {
1089            String JavaDoc bodyText = this.bodyText.toString();
1090            Substitutor substitutor = getSubstitutor();
1091            if (substitutor!= null) {
1092                bodyText = substitutor.substitute(bodyText);
1093            }
1094            for (int i = 0; i < rules.size(); i++) {
1095                try {
1096                    Rule rule = (Rule) rules.get(i);
1097                    if (debug) {
1098                        log.debug(" Fire body() for " + rule);
1099                    }
1100                    rule.body(namespaceURI, name, bodyText);
1101                } catch (Exception JavaDoc e) {
1102                    log.error("Body event threw exception", e);
1103                    throw createSAXException(e);
1104                } catch (Error JavaDoc e) {
1105                    log.error("Body event threw error", e);
1106                    throw e;
1107                }
1108            }
1109        } else {
1110            if (debug) {
1111                log.debug(" No rules found matching '" + match + "'.");
1112            }
1113        }
1114
1115        // Recover the body text from the surrounding element
1116
bodyText = (StringBuffer JavaDoc) bodyTexts.pop();
1117        if (debug) {
1118            log.debug(" Popping body text '" + bodyText.toString() + "'");
1119        }
1120
1121        // Fire "end" events for all relevant rules in reverse order
1122
if (rules != null) {
1123            for (int i = 0; i < rules.size(); i++) {
1124                int j = (rules.size() - i) - 1;
1125                try {
1126                    Rule rule = (Rule) rules.get(j);
1127                    if (debug) {
1128                        log.debug(" Fire end() for " + rule);
1129                    }
1130                    rule.end(namespaceURI, name);
1131                } catch (Exception JavaDoc e) {
1132                    log.error("End event threw exception", e);
1133                    throw createSAXException(e);
1134                } catch (Error JavaDoc e) {
1135                    log.error("End event threw error", e);
1136                    throw e;
1137                }
1138            }
1139        }
1140
1141        // Recover the previous match expression
1142
int slash = match.lastIndexOf('/');
1143        if (slash >= 0) {
1144            match = match.substring(0, slash);
1145        } else {
1146            match = "";
1147        }
1148
1149    }
1150
1151
1152    /**
1153     * Process notification that a namespace prefix is going out of scope.
1154     *
1155     * @param prefix Prefix that is going out of scope
1156     *
1157     * @exception SAXException if a parsing error is to be reported
1158     */

1159    public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
1160
1161        if (saxLog.isDebugEnabled()) {
1162            saxLog.debug("endPrefixMapping(" + prefix + ")");
1163        }
1164
1165        // Deregister this prefix mapping
1166
ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1167        if (stack == null) {
1168            return;
1169        }
1170        try {
1171            stack.pop();
1172            if (stack.empty())
1173                namespaces.remove(prefix);
1174        } catch (EmptyStackException JavaDoc e) {
1175            throw createSAXException("endPrefixMapping popped too many times");
1176        }
1177
1178    }
1179
1180
1181    /**
1182     * Process notification of ignorable whitespace received from the body of
1183     * an XML element.
1184     *
1185     * @param buffer The characters from the XML document
1186     * @param start Starting offset into the buffer
1187     * @param len Number of characters from the buffer
1188     *
1189     * @exception SAXException if a parsing error is to be reported
1190     */

1191    public void ignorableWhitespace(char buffer[], int start, int len)
1192            throws SAXException JavaDoc {
1193
1194        if (saxLog.isDebugEnabled()) {
1195            saxLog.debug("ignorableWhitespace(" +
1196                    new String JavaDoc(buffer, start, len) + ")");
1197        }
1198
1199        ; // No processing required
1200

1201    }
1202
1203
1204    /**
1205     * Process notification of a processing instruction that was encountered.
1206     *
1207     * @param target The processing instruction target
1208     * @param data The processing instruction data (if any)
1209     *
1210     * @exception SAXException if a parsing error is to be reported
1211     */

1212    public void processingInstruction(String JavaDoc target, String JavaDoc data)
1213            throws SAXException JavaDoc {
1214
1215        if (customContentHandler != null) {
1216            // forward calls instead of handling them here
1217
customContentHandler.processingInstruction(target, data);
1218            return;
1219        }
1220
1221        if (saxLog.isDebugEnabled()) {
1222            saxLog.debug("processingInstruction('" + target + "','" + data + "')");
1223        }
1224
1225        ; // No processing is required
1226

1227    }
1228
1229
1230    /**
1231     * Gets the document locator associated with our parser.
1232     *
1233     * @return the Locator supplied by the document parser
1234     */

1235    public Locator JavaDoc getDocumentLocator() {
1236
1237        return locator;
1238
1239    }
1240
1241    /**
1242     * Sets the document locator associated with our parser.
1243     *
1244     * @param locator The new locator
1245     */

1246    public void setDocumentLocator(Locator JavaDoc locator) {
1247
1248        if (saxLog.isDebugEnabled()) {
1249            saxLog.debug("setDocumentLocator(" + locator + ")");
1250        }
1251
1252        this.locator = locator;
1253
1254    }
1255
1256
1257    /**
1258     * Process notification of a skipped entity.
1259     *
1260     * @param name Name of the skipped entity
1261     *
1262     * @exception SAXException if a parsing error is to be reported
1263     */

1264    public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
1265
1266        if (saxLog.isDebugEnabled()) {
1267            saxLog.debug("skippedEntity(" + name + ")");
1268        }
1269
1270        ; // No processing required
1271

1272    }
1273
1274
1275    /**
1276     * Process notification of the beginning of the document being reached.
1277     *
1278     * @exception SAXException if a parsing error is to be reported
1279     */

1280    public void startDocument() throws SAXException JavaDoc {
1281
1282        if (saxLog.isDebugEnabled()) {
1283            saxLog.debug("startDocument()");
1284        }
1285
1286        // ensure that the digester is properly configured, as
1287
// the digester could be used as a SAX ContentHandler
1288
// rather than via the parse() methods.
1289
configure();
1290    }
1291
1292
1293    /**
1294     * Process notification of the start of an XML element being reached.
1295     *
1296     * @param namespaceURI The Namespace URI, or the empty string if the element
1297     * has no Namespace URI or if Namespace processing is not being performed.
1298     * @param localName The local name (without prefix), or the empty
1299     * string if Namespace processing is not being performed.
1300     * @param qName The qualified name (with prefix), or the empty
1301     * string if qualified names are not available.\
1302     * @param list The attributes attached to the element. If there are
1303     * no attributes, it shall be an empty Attributes object.
1304     * @exception SAXException if a parsing error is to be reported
1305     */

1306    public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
1307                             String JavaDoc qName, Attributes JavaDoc list)
1308            throws SAXException JavaDoc {
1309        boolean debug = log.isDebugEnabled();
1310        
1311        if (customContentHandler != null) {
1312            // forward calls instead of handling them here
1313
customContentHandler.startElement(namespaceURI, localName, qName, list);
1314            return;
1315        }
1316
1317        if (saxLog.isDebugEnabled()) {
1318            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
1319                    qName + ")");
1320        }
1321        
1322        // Save the body text accumulated for our surrounding element
1323
bodyTexts.push(bodyText);
1324        if (debug) {
1325            log.debug(" Pushing body text '" + bodyText.toString() + "'");
1326        }
1327        bodyText = new StringBuffer JavaDoc();
1328
1329        // the actual element name is either in localName or qName, depending
1330
// on whether the parser is namespace aware
1331
String JavaDoc name = localName;
1332        if ((name == null) || (name.length() < 1)) {
1333            name = qName;
1334        }
1335
1336        // Compute the current matching rule
1337
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(match);
1338        if (match.length() > 0) {
1339            sb.append('/');
1340        }
1341        sb.append(name);
1342        match = sb.toString();
1343        if (debug) {
1344            log.debug(" New match='" + match + "'");
1345        }
1346
1347        // Fire "begin" events for all relevant rules
1348
List JavaDoc rules = getRules().match(namespaceURI, match);
1349        matches.push(rules);
1350        if ((rules != null) && (rules.size() > 0)) {
1351            Substitutor substitutor = getSubstitutor();
1352            if (substitutor!= null) {
1353                list = substitutor.substitute(list);
1354            }
1355            for (int i = 0; i < rules.size(); i++) {
1356                try {
1357                    Rule rule = (Rule) rules.get(i);
1358                    if (debug) {
1359                        log.debug(" Fire begin() for " + rule);
1360                    }
1361                    rule.begin(namespaceURI, name, list);
1362                } catch (Exception JavaDoc e) {
1363                    log.error("Begin event threw exception", e);
1364                    throw createSAXException(e);
1365                } catch (Error JavaDoc e) {
1366                    log.error("Begin event threw error", e);
1367                    throw e;
1368                }
1369            }
1370        } else {
1371            if (debug) {
1372                log.debug(" No rules found matching '" + match + "'.");
1373            }
1374        }
1375
1376    }
1377
1378
1379    /**
1380     * Process notification that a namespace prefix is coming in to scope.
1381     *
1382     * @param prefix Prefix that is being declared
1383     * @param namespaceURI Corresponding namespace URI being mapped to
1384     *
1385     * @exception SAXException if a parsing error is to be reported
1386     */

1387    public void startPrefixMapping(String JavaDoc prefix, String JavaDoc namespaceURI)
1388            throws SAXException JavaDoc {
1389
1390        if (saxLog.isDebugEnabled()) {
1391            saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
1392        }
1393
1394        // Register this prefix mapping
1395
ArrayStack stack = (ArrayStack) namespaces.get(prefix);
1396        if (stack == null) {
1397            stack = new ArrayStack();
1398            namespaces.put(prefix, stack);
1399        }
1400        stack.push(namespaceURI);
1401
1402    }
1403
1404
1405    // ----------------------------------------------------- DTDHandler Methods
1406

1407
1408    /**
1409     * Receive notification of a notation declaration event.
1410     *
1411     * @param name The notation name
1412     * @param publicId The public identifier (if any)
1413     * @param systemId The system identifier (if any)
1414     */

1415    public void notationDecl(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) {
1416
1417        if (saxLog.isDebugEnabled()) {
1418            saxLog.debug("notationDecl(" + name + "," + publicId + "," +
1419                    systemId + ")");
1420        }
1421
1422    }
1423
1424
1425    /**
1426     * Receive notification of an unparsed entity declaration event.
1427     *
1428     * @param name The unparsed entity name
1429     * @param publicId The public identifier (if any)
1430     * @param systemId The system identifier (if any)
1431     * @param notation The name of the associated notation
1432     */

1433    public void unparsedEntityDecl(String JavaDoc name, String JavaDoc publicId,
1434                                   String JavaDoc systemId, String JavaDoc notation) {
1435
1436        if (saxLog.isDebugEnabled()) {
1437            saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," +
1438                    systemId + "," + notation + ")");
1439        }
1440
1441    }
1442
1443
1444    // ----------------------------------------------- EntityResolver Methods
1445

1446    /**
1447     * Set the <code>EntityResolver</code> used by SAX when resolving
1448     * public id and system id.
1449     * This must be called before the first call to <code>parse()</code>.
1450     * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
1451     */

1452    public void setEntityResolver(EntityResolver JavaDoc entityResolver){
1453        this.entityResolver = entityResolver;
1454    }
1455    
1456    
1457    /**
1458     * Return the Entity Resolver used by the SAX parser.
1459     * @return Return the Entity Resolver used by the SAX parser.
1460     */

1461    public EntityResolver JavaDoc getEntityResolver(){
1462        return entityResolver;
1463    }
1464
1465    /**
1466     * Resolve the requested external entity.
1467     *
1468     * @param publicId The public identifier of the entity being referenced
1469     * @param systemId The system identifier of the entity being referenced
1470     *
1471     * @exception SAXException if a parsing exception occurs
1472     *
1473     */

1474    public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
1475            throws SAXException JavaDoc {
1476                
1477        if (saxLog.isDebugEnabled()) {
1478            saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')");
1479        }
1480        
1481        if (publicId != null)
1482            this.publicId = publicId;
1483                                       
1484        // Has this system identifier been registered?
1485
String JavaDoc entityURL = null;
1486        if (publicId != null) {
1487            entityURL = (String JavaDoc) entityValidator.get(publicId);
1488        }
1489         
1490        // Redirect the schema location to a local destination
1491
if (schemaLocation != null && entityURL == null && systemId != null){
1492            entityURL = (String JavaDoc)entityValidator.get(systemId);
1493        }
1494
1495        if (entityURL == null) {
1496            if (systemId == null) {
1497                // cannot resolve
1498
if (log.isDebugEnabled()) {
1499                    log.debug(" Cannot resolve entity: '" + entityURL + "'");
1500                }
1501                return (null);
1502                
1503            } else {
1504                // try to resolve using system ID
1505
if (log.isDebugEnabled()) {
1506                    log.debug(" Trying to resolve using system ID '" + systemId + "'");
1507                }
1508                entityURL = systemId;
1509            }
1510        }
1511        
1512        // Return an input source to our alternative URL
1513
if (log.isDebugEnabled()) {
1514            log.debug(" Resolving to alternate DTD '" + entityURL + "'");
1515        }
1516        
1517        try {
1518            return (new InputSource JavaDoc(entityURL));
1519        } catch (Exception JavaDoc e) {
1520            throw createSAXException(e);
1521        }
1522    }
1523
1524
1525    // ------------------------------------------------- ErrorHandler Methods
1526

1527
1528    /**
1529     * Forward notification of a parsing error to the application supplied
1530     * error handler (if any).
1531     *
1532     * @param exception The error information
1533     *
1534     * @exception SAXException if a parsing exception occurs
1535     */

1536    public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1537
1538        log.error("Parse Error at line " + exception.getLineNumber() +
1539                " column " + exception.getColumnNumber() + ": " +
1540                exception.getMessage(), exception);
1541        if (errorHandler != null) {
1542            errorHandler.error(exception);
1543        }
1544
1545    }
1546
1547
1548    /**
1549     * Forward notification of a fatal parsing error to the application
1550     * supplied error handler (if any).
1551     *
1552     * @param exception The fatal error information
1553     *
1554     * @exception SAXException if a parsing exception occurs
1555     */

1556    public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1557
1558        log.error("Parse Fatal Error at line " + exception.getLineNumber() +
1559                " column " + exception.getColumnNumber() + ": " +
1560                exception.getMessage(), exception);
1561        if (errorHandler != null) {
1562            errorHandler.fatalError(exception);
1563        }
1564
1565    }
1566
1567
1568    /**
1569     * Forward notification of a parse warning to the application supplied
1570     * error handler (if any).
1571     *
1572     * @param exception The warning information
1573     *
1574     * @exception SAXException if a parsing exception occurs
1575     */

1576    public void warning(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1577         if (errorHandler != null) {
1578            log.warn("Parse Warning Error at line " + exception.getLineNumber() +
1579                " column " + exception.getColumnNumber() + ": " +
1580                exception.getMessage(), exception);
1581            
1582            errorHandler.warning(exception);
1583        }
1584
1585    }
1586
1587
1588    // ------------------------------------------------------- Public Methods
1589

1590
1591    /**
1592     * Log a message to our associated logger.
1593     *
1594     * @param message The message to be logged
1595     * @deprecated Call getLogger() and use it's logging methods
1596     */

1597    public void log(String JavaDoc message) {
1598
1599        log.info(message);
1600
1601    }
1602
1603
1604    /**
1605     * Log a message and exception to our associated logger.
1606     *
1607     * @param message The message to be logged
1608     * @deprecated Call getLogger() and use it's logging methods
1609     */

1610    public void log(String JavaDoc message, Throwable JavaDoc exception) {
1611
1612        log.error(message, exception);
1613
1614    }
1615
1616
1617    /**
1618     * Parse the content of the specified file using this Digester. Returns
1619     * the root element from the object stack (if any).
1620     *
1621     * @param file File containing the XML data to be parsed
1622     *
1623     * @exception IOException if an input/output error occurs
1624     * @exception SAXException if a parsing exception occurs
1625     */

1626    public Object JavaDoc parse(File JavaDoc file) throws IOException JavaDoc, SAXException JavaDoc {
1627
1628        configure();
1629        InputSource JavaDoc input = new InputSource JavaDoc(new FileInputStream JavaDoc(file));
1630        input.setSystemId(file.toURL().toString());
1631        getXMLReader().parse(input);
1632        return (root);
1633
1634    }
1635    /**
1636     * Parse the content of the specified input source using this Digester.
1637     * Returns the root element from the object stack (if any).
1638     *
1639     * @param input Input source containing the XML data to be parsed
1640     *
1641     * @exception IOException if an input/output error occurs
1642     * @exception SAXException if a parsing exception occurs
1643     */

1644    public Object JavaDoc parse(InputSource JavaDoc input) throws IOException JavaDoc, SAXException JavaDoc {
1645 
1646        configure();
1647        getXMLReader().parse(input);
1648        return (root);
1649
1650    }
1651
1652
1653    /**
1654     * Parse the content of the specified input stream using this Digester.
1655     * Returns the root element from the object stack (if any).
1656     *
1657     * @param input Input stream containing the XML data to be parsed
1658     *
1659     * @exception IOException if an input/output error occurs
1660     * @exception SAXException if a parsing exception occurs
1661     */

1662    public Object JavaDoc parse(InputStream JavaDoc input) throws IOException JavaDoc, SAXException JavaDoc {
1663
1664        configure();
1665        InputSource JavaDoc is = new InputSource JavaDoc(input);
1666        getXMLReader().parse(is);
1667        return (root);
1668
1669    }
1670
1671
1672    /**
1673     * Parse the content of the specified reader using this Digester.
1674     * Returns the root element from the object stack (if any).
1675     *
1676     * @param reader Reader containing the XML data to be parsed
1677     *
1678     * @exception IOException if an input/output error occurs
1679     * @exception SAXException if a parsing exception occurs
1680     */

1681    public Object JavaDoc parse(Reader JavaDoc reader) throws IOException JavaDoc, SAXException JavaDoc {
1682
1683        configure();
1684        InputSource JavaDoc is = new InputSource JavaDoc(reader);
1685        getXMLReader().parse(is);
1686        return (root);
1687
1688    }
1689
1690
1691    /**
1692     * Parse the content of the specified URI using this Digester.
1693     * Returns the root element from the object stack (if any).
1694     *
1695     * @param uri URI containing the XML data to be parsed
1696     *
1697     * @exception IOException if an input/output error occurs
1698     * @exception SAXException if a parsing exception occurs
1699     */

1700    public Object JavaDoc parse(String JavaDoc uri) throws IOException JavaDoc, SAXException JavaDoc {
1701
1702        configure();
1703        InputSource JavaDoc is = new InputSource JavaDoc(uri);
1704        getXMLReader().parse(is);
1705        return (root);
1706
1707    }
1708
1709
1710    /**
1711     * <p>Register the specified DTD URL for the specified public identifier.
1712     * This must be called before the first call to <code>parse()</code>.
1713     * </p><p>
1714     * <code>Digester</code> contains an internal <code>EntityResolver</code>
1715     * implementation. This maps <code>PUBLICID</code>'s to URLs
1716     * (from which the resource will be loaded). A common use case for this
1717     * method is to register local URLs (possibly computed at runtime by a
1718     * classloader) for DTDs. This allows the performance advantage of using
1719     * a local version without having to ensure every <code>SYSTEM</code>
1720     * URI on every processed xml document is local. This implementation provides
1721     * only basic functionality. If more sophisticated features are required,
1722     * using {@link #setEntityResolver} to set a custom resolver is recommended.
1723     * </p><p>
1724     * <strong>Note:</strong> This method will have no effect when a custom
1725     * <code>EntityResolver</code> has been set. (Setting a custom
1726     * <code>EntityResolver</code> overrides the internal implementation.)
1727     * </p>
1728     * @param publicId Public identifier of the DTD to be resolved
1729     * @param entityURL The URL to use for reading this DTD
1730     */

1731    public void register(String JavaDoc publicId, String JavaDoc entityURL) {
1732
1733        if (log.isDebugEnabled()) {
1734            log.debug("register('" + publicId + "', '" + entityURL + "'");
1735        }
1736        entityValidator.put(publicId, entityURL);
1737
1738    }
1739
1740
1741    // --------------------------------------------------------- Rule Methods
1742

1743
1744    /**
1745     * <p>Register a new Rule matching the specified pattern.
1746     * This method sets the <code>Digester</code> property on the rule.</p>
1747     *
1748     * @param pattern Element matching pattern
1749     * @param rule Rule to be registered
1750     */

1751    public void addRule(String JavaDoc pattern, Rule rule) {
1752
1753        rule.setDigester(this);
1754        getRules().add(pattern, rule);
1755
1756    }
1757
1758
1759    /**
1760     * Register a set of Rule instances defined in a RuleSet.
1761     *
1762     * @param ruleSet The RuleSet instance to configure from
1763     */

1764    public void addRuleSet(RuleSet ruleSet) {
1765
1766        String JavaDoc oldNamespaceURI = getRuleNamespaceURI();
1767        String JavaDoc newNamespaceURI = ruleSet.getNamespaceURI();
1768        if (log.isDebugEnabled()) {
1769            if (newNamespaceURI == null) {
1770                log.debug("addRuleSet() with no namespace URI");
1771            } else {
1772                log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
1773            }
1774        }
1775        setRuleNamespaceURI(newNamespaceURI);
1776        ruleSet.addRuleInstances(this);
1777        setRuleNamespaceURI(oldNamespaceURI);
1778
1779    }
1780
1781
1782    /**
1783     * Add a "bean property setter" rule for the specified parameters.
1784     *
1785     * @param pattern Element matching pattern
1786     * @see BeanPropertySetterRule
1787     */

1788    public void addBeanPropertySetter(String JavaDoc pattern) {
1789
1790        addRule(pattern,
1791                new BeanPropertySetterRule());
1792
1793    }
1794
1795
1796    /**
1797     * Add a "bean property setter" rule for the specified parameters.
1798     *
1799     * @param pattern Element matching pattern
1800     * @param propertyName Name of property to set
1801     * @see BeanPropertySetterRule
1802     */

1803    public void addBeanPropertySetter(String JavaDoc pattern,
1804                                      String JavaDoc propertyName) {
1805
1806        addRule(pattern,
1807                new BeanPropertySetterRule(propertyName));
1808
1809    }
1810
1811    /**
1812     * Add an "call method" rule for a method which accepts no arguments.
1813     *
1814     * @param pattern Element matching pattern
1815     * @param methodName Method name to be called
1816     * @see CallMethodRule
1817     */

1818    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName) {
1819
1820        addRule(
1821                pattern,
1822                new CallMethodRule(methodName));
1823
1824    }
1825
1826    /**
1827     * Add an "call method" rule for the specified parameters.
1828     *
1829     * @param pattern Element matching pattern
1830     * @param methodName Method name to be called
1831     * @param paramCount Number of expected parameters (or zero
1832     * for a single parameter from the body of this element)
1833     * @see CallMethodRule
1834     */

1835    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1836                              int paramCount) {
1837
1838        addRule(pattern,
1839                new CallMethodRule(methodName, paramCount));
1840
1841    }
1842
1843
1844    /**
1845     * Add an "call method" rule for the specified parameters.
1846     * If <code>paramCount</code> is set to zero the rule will use
1847     * the body of the matched element as the single argument of the
1848     * method, unless <code>paramTypes</code> is null or empty, in this
1849     * case the rule will call the specified method with no arguments.
1850     *
1851     * @param pattern Element matching pattern
1852     * @param methodName Method name to be called
1853     * @param paramCount Number of expected parameters (or zero
1854     * for a single parameter from the body of this element)
1855     * @param paramTypes Set of Java class names for the types
1856     * of the expected parameters
1857     * (if you wish to use a primitive type, specify the corresonding
1858     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1859     * for a <code>boolean</code> parameter)
1860     * @see CallMethodRule
1861     */

1862    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1863                              int paramCount, String JavaDoc paramTypes[]) {
1864
1865        addRule(pattern,
1866                new CallMethodRule(
1867                                    methodName,
1868                                    paramCount,
1869                                    paramTypes));
1870
1871    }
1872
1873
1874    /**
1875     * Add an "call method" rule for the specified parameters.
1876     * If <code>paramCount</code> is set to zero the rule will use
1877     * the body of the matched element as the single argument of the
1878     * method, unless <code>paramTypes</code> is null or empty, in this
1879     * case the rule will call the specified method with no arguments.
1880     *
1881     * @param pattern Element matching pattern
1882     * @param methodName Method name to be called
1883     * @param paramCount Number of expected parameters (or zero
1884     * for a single parameter from the body of this element)
1885     * @param paramTypes The Java class names of the arguments
1886     * (if you wish to use a primitive type, specify the corresonding
1887     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
1888     * for a <code>boolean</code> parameter)
1889     * @see CallMethodRule
1890     */

1891    public void addCallMethod(String JavaDoc pattern, String JavaDoc methodName,
1892                              int paramCount, Class JavaDoc paramTypes[]) {
1893
1894        addRule(pattern,
1895                new CallMethodRule(
1896                                    methodName,
1897                                    paramCount,
1898                                    paramTypes));
1899
1900    }
1901
1902
1903    /**
1904     * Add a "call parameter" rule for the specified parameters.
1905     *
1906     * @param pattern Element matching pattern
1907     * @param paramIndex Zero-relative parameter index to set
1908     * (from the body of this element)
1909     * @see CallParamRule
1910     */

1911    public void addCallParam(String JavaDoc pattern, int paramIndex) {
1912
1913        addRule(pattern,
1914                new CallParamRule(paramIndex));
1915
1916    }
1917
1918
1919    /**
1920     * Add a "call parameter" rule for the specified parameters.
1921     *
1922     * @param pattern Element matching pattern
1923     * @param paramIndex Zero-relative parameter index to set
1924     * (from the specified attribute)
1925     * @param attributeName Attribute whose value is used as the
1926     * parameter value
1927     * @see CallParamRule
1928     */

1929    public void addCallParam(String JavaDoc pattern, int paramIndex,
1930                             String JavaDoc attributeName) {
1931
1932        addRule(pattern,
1933                new CallParamRule(paramIndex, attributeName));
1934
1935    }
1936
1937
1938    /**
1939     * Add a "call parameter" rule.
1940     * This will either take a parameter from the stack
1941     * or from the current element body text.
1942     *
1943     * @param paramIndex The zero-relative parameter number
1944     * @param fromStack Should the call parameter be taken from the top of the stack?
1945     * @see CallParamRule
1946     */

1947    public void addCallParam(String JavaDoc pattern, int paramIndex, boolean fromStack) {
1948    
1949        addRule(pattern,
1950                new CallParamRule(paramIndex, fromStack));
1951      
1952    }
1953
1954    /**
1955     * Add a "call parameter" rule that sets a parameter from the stack.
1956     * This takes a parameter from the given position on the stack.
1957     *
1958     * @param paramIndex The zero-relative parameter number
1959     * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
1960     * where 0 is the top of the stack, 1 the next element down and so on
1961     * @see CallMethodRule
1962     */

1963    public void addCallParam(String JavaDoc pattern, int paramIndex, int stackIndex) {
1964    
1965        addRule(pattern,
1966                new CallParamRule(paramIndex, stackIndex));
1967      
1968    }
1969    
1970    /**
1971     * Add a "call parameter" rule that sets a parameter from the current
1972     * <code>Digester</code> matching path.
1973     * This is sometimes useful when using rules that support wildcards.
1974     *
1975     * @param pattern the pattern that this rule should match
1976     * @param paramIndex The zero-relative parameter number
1977     * @see CallMethodRule
1978     */

1979    public void addCallParamPath(String JavaDoc pattern,int paramIndex) {
1980        addRule(pattern, new PathCallParamRule(paramIndex));
1981    }
1982    
1983    /**
1984     * Add a "call parameter" rule that sets a parameter from a
1985     * caller-provided object. This can be used to pass constants such as
1986     * strings to methods; it can also be used to pass mutable objects,
1987     * providing ways for objects to do things like "register" themselves
1988     * with some shared object.
1989     * <p>
1990     * Note that when attempting to locate a matching method to invoke,
1991     * the true type of the paramObj is used, so that despite the paramObj
1992     * being passed in here as type Object, the target method can declare
1993     * its parameters as being the true type of the object (or some ancestor
1994     * type, according to the usual type-conversion rules).
1995     *
1996     * @param paramIndex The zero-relative parameter number
1997     * @param paramObj Any arbitrary object to be passed to the target
1998     * method.
1999     * @see CallMethodRule
2000     *
2001     * @since 1.6
2002     */

2003    public void addObjectParam(String JavaDoc pattern, int paramIndex,
2004                               Object JavaDoc paramObj) {
2005    
2006        addRule(pattern,
2007                new ObjectParamRule(paramIndex, paramObj));
2008      
2009    }
2010    
2011    /**
2012     * Add a "factory create" rule for the specified parameters.
2013     * Exceptions thrown during the object creation process will be propagated.
2014     *
2015     * @param pattern Element matching pattern
2016     * @param className Java class name of the object creation factory class
2017     * @see FactoryCreateRule
2018     */

2019    public void addFactoryCreate(String JavaDoc pattern, String JavaDoc className) {
2020
2021        addFactoryCreate(pattern, className, false);
2022
2023    }
2024
2025
2026    /**
2027     * Add a "factory create" rule for the specified parameters.
2028     * Exceptions thrown during the object creation process will be propagated.
2029     *
2030     * @param pattern Element matching pattern
2031     * @param clazz Java class of the object creation factory class
2032     * @see FactoryCreateRule
2033     */

2034    public void addFactoryCreate(String JavaDoc pattern, Class JavaDoc clazz) {
2035
2036        addFactoryCreate(pattern, clazz, false);
2037
2038    }
2039
2040
2041    /**
2042     * Add a "factory create" rule for the specified parameters.
2043     * Exceptions thrown during the object creation process will be propagated.
2044     *
2045     * @param pattern Element matching pattern
2046     * @param className Java class name of the object creation factory class
2047     * @param attributeName Attribute name which, if present, overrides the
2048     * value specified by <code>className</code>
2049     * @see FactoryCreateRule
2050     */

2051    public void addFactoryCreate(String JavaDoc pattern, String JavaDoc className,
2052                                 String JavaDoc attributeName) {
2053
2054        addFactoryCreate(pattern, className, attributeName, false);
2055
2056    }
2057
2058
2059    /**
2060     * Add a "factory create" rule for the specified parameters.
2061     * Exceptions thrown during the object creation process will be propagated.
2062     *
2063     * @param pattern Element matching pattern
2064     * @param clazz Java class of the object creation factory class
2065     * @param attributeName Attribute name which, if present, overrides the
2066     * value specified by <code>className</code>
2067     * @see FactoryCreateRule
2068     */

2069    public void addFactoryCreate(String JavaDoc pattern, Class JavaDoc clazz,
2070                                 String JavaDoc attributeName) {
2071
2072        addFactoryCreate(pattern, clazz, attributeName, false);
2073
2074    }
2075
2076
2077    /**
2078     * Add a "factory create" rule for the specified parameters.
2079     * Exceptions thrown during the object creation process will be propagated.
2080     *
2081     * @param pattern Element matching pattern
2082     * @param creationFactory Previously instantiated ObjectCreationFactory
2083     * to be utilized
2084     * @see FactoryCreateRule
2085     */

2086    public void addFactoryCreate(String JavaDoc pattern,
2087                                 ObjectCreationFactory creationFactory) {
2088
2089        addFactoryCreate(pattern, creationFactory, false);
2090
2091    }
2092
2093    /**
2094     * Add a "factory create" rule for the specified parameters.
2095     *
2096     * @param pattern Element matching pattern
2097     * @param className Java class name of the object creation factory class
2098     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2099     * object creation will be ignored.
2100     * @see FactoryCreateRule
2101     */

2102    public void addFactoryCreate(
2103                                    String JavaDoc pattern,
2104                                    String JavaDoc className,
2105                                    boolean ignoreCreateExceptions) {
2106
2107        addRule(
2108                pattern,
2109                new FactoryCreateRule(className, ignoreCreateExceptions));
2110
2111    }
2112
2113
2114    /**
2115     * Add a "factory create" rule for the specified parameters.
2116     *
2117     * @param pattern Element matching pattern
2118     * @param clazz Java class of the object creation factory class
2119     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2120     * object creation will be ignored.
2121     * @see FactoryCreateRule
2122     */

2123    public void addFactoryCreate(
2124                                    String JavaDoc pattern,
2125                                    Class JavaDoc clazz,
2126                                    boolean ignoreCreateExceptions) {
2127
2128        addRule(
2129                pattern,
2130                new FactoryCreateRule(clazz, ignoreCreateExceptions));
2131
2132    }
2133
2134
2135    /**
2136     * Add a "factory create" rule for the specified parameters.
2137     *
2138     * @param pattern Element matching pattern
2139     * @param className Java class name of the object creation factory class
2140     * @param attributeName Attribute name which, if present, overrides the
2141     * value specified by <code>className</code>
2142     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2143     * object creation will be ignored.
2144     * @see FactoryCreateRule
2145     */

2146    public void addFactoryCreate(
2147                                String JavaDoc pattern,
2148                                String JavaDoc className,
2149                                String JavaDoc attributeName,
2150                                boolean ignoreCreateExceptions) {
2151
2152        addRule(
2153                pattern,
2154                new FactoryCreateRule(className, attributeName, ignoreCreateExceptions));
2155
2156    }
2157
2158
2159    /**
2160     * Add a "factory create" rule for the specified parameters.
2161     *
2162     * @param pattern Element matching pattern
2163     * @param clazz Java class of the object creation factory class
2164     * @param attributeName Attribute name which, if present, overrides the
2165     * value specified by <code>className</code>
2166     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2167     * object creation will be ignored.
2168     * @see FactoryCreateRule
2169     */

2170    public void addFactoryCreate(
2171                                    String JavaDoc pattern,
2172                                    Class JavaDoc clazz,
2173                                    String JavaDoc attributeName,
2174                                    boolean ignoreCreateExceptions) {
2175
2176        addRule(
2177                pattern,
2178                new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions));
2179
2180    }
2181
2182
2183    /**
2184     * Add a "factory create" rule for the specified parameters.
2185     *
2186     * @param pattern Element matching pattern
2187     * @param creationFactory Previously instantiated ObjectCreationFactory
2188     * to be utilized
2189     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
2190     * object creation will be ignored.
2191     * @see FactoryCreateRule
2192     */

2193    public void addFactoryCreate(String JavaDoc pattern,
2194                                 ObjectCreationFactory creationFactory,
2195                                 boolean ignoreCreateExceptions) {
2196
2197        creationFactory.setDigester(this);
2198        addRule(pattern,
2199                new FactoryCreateRule(creationFactory, ignoreCreateExceptions));
2200
2201    }
2202
2203    /**
2204     * Add an "object create" rule for the specified parameters.
2205     *
2206     * @param pattern Element matching pattern
2207     * @param className Java class name to be created
2208     * @see ObjectCreateRule
2209     */

2210    public void addObjectCreate(String JavaDoc pattern, String JavaDoc className) {
2211
2212        addRule(pattern,
2213                new ObjectCreateRule(className));
2214
2215    }
2216
2217
2218    /**
2219     * Add an "object create" rule for the specified parameters.
2220     *
2221     * @param pattern Element matching pattern
2222     * @param clazz Java class to be created
2223     * @see ObjectCreateRule
2224     */

2225    public void addObjectCreate(String JavaDoc pattern, Class JavaDoc clazz) {
2226
2227        addRule(pattern,
2228                new ObjectCreateRule(clazz));
2229
2230    }
2231
2232
2233    /**
2234     * Add an "object create" rule for the specified parameters.
2235     *
2236     * @param pattern Element matching pattern
2237     * @param className Default Java class name to be created
2238     * @param attributeName Attribute name that optionally overrides
2239     * the default Java class name to be created
2240     * @see ObjectCreateRule
2241     */

2242    public void addObjectCreate(String JavaDoc pattern, String JavaDoc className,
2243                                String JavaDoc attributeName) {
2244
2245        addRule(pattern,
2246                new ObjectCreateRule(className, attributeName));
2247
2248    }
2249
2250
2251    /**
2252     * Add an "object create" rule for the specified parameters.
2253     *
2254     * @param pattern Element matching pattern
2255     * @param attributeName Attribute name that optionally overrides
2256     * @param clazz Default Java class to be created
2257     * the default Java class name to be created
2258     * @see ObjectCreateRule
2259     */

2260    public void addObjectCreate(String JavaDoc pattern,
2261                                String JavaDoc attributeName,
2262                                Class JavaDoc clazz) {
2263
2264        addRule(pattern,
2265                new ObjectCreateRule(attributeName, clazz));
2266
2267    }
2268
2269    /**
2270     * Adds an {@link SetNestedPropertiesRule}.
2271     *
2272     * @param pattern register the rule with this pattern
2273     *
2274     * @since 1.6
2275     */

2276    public void addSetNestedProperties(String JavaDoc pattern) {
2277    
2278        addRule(pattern, new SetNestedPropertiesRule());
2279    }
2280
2281    /**
2282     * Adds an {@link SetNestedPropertiesRule}.
2283     *
2284     * @param pattern register the rule with this pattern
2285     * @param elementName elment name that a property maps to
2286     * @param propertyName property name of the element mapped from
2287     *
2288     * @since 1.6
2289     */

2290    public void addSetNestedProperties(String JavaDoc pattern, String JavaDoc elementName, String JavaDoc propertyName) {
2291    
2292        addRule(pattern, new SetNestedPropertiesRule(elementName, propertyName));
2293    }
2294
2295    /**
2296     * Adds an {@link SetNestedPropertiesRule}.
2297     *
2298     * @param pattern register the rule with this pattern
2299     * @param elementNames elment names that (in order) map to properties
2300     * @param propertyNames property names that (in order) elements are mapped to
2301     *
2302     * @since 1.6
2303     */

2304    public void addSetNestedProperties(String JavaDoc pattern, String JavaDoc[] elementNames, String JavaDoc[] propertyNames) {
2305    
2306        addRule(pattern, new SetNestedPropertiesRule(elementNames, propertyNames));
2307    }
2308
2309
2310    /**
2311     * Add a "set next" rule for the specified parameters.
2312     *
2313     * @param pattern Element matching pattern
2314     * @param methodName Method name to call on the parent element
2315     * @see SetNextRule
2316     */

2317    public void addSetNext(String JavaDoc pattern, String JavaDoc methodName) {
2318
2319        addRule(pattern,
2320                new SetNextRule(methodName));
2321
2322    }
2323
2324
2325    /**
2326     * Add a "set next" rule for the specified parameters.
2327     *
2328     * @param pattern Element matching pattern
2329     * @param methodName Method name to call on the parent element
2330     * @param paramType Java class name of the expected parameter type
2331     * (if you wish to use a primitive type, specify the corresonding
2332     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2333     * for a <code>boolean</code> parameter)
2334     * @see SetNextRule
2335     */

2336    public void addSetNext(String JavaDoc pattern, String JavaDoc methodName,
2337                           String JavaDoc paramType) {
2338
2339        addRule(pattern,
2340                new SetNextRule(methodName, paramType));
2341
2342    }
2343
2344
2345    /**
2346     * Add {@link SetRootRule} with the specified parameters.
2347     *
2348     * @param pattern Element matching pattern
2349     * @param methodName Method name to call on the root object
2350     * @see SetRootRule
2351     */

2352    public void addSetRoot(String JavaDoc pattern, String JavaDoc methodName) {
2353
2354        addRule(pattern,
2355                new SetRootRule(methodName));
2356
2357    }
2358
2359
2360    /**
2361     * Add {@link SetRootRule} with the specified parameters.
2362     *
2363     * @param pattern Element matching pattern
2364     * @param methodName Method name to call on the root object
2365     * @param paramType Java class name of the expected parameter type
2366     * @see SetRootRule
2367     */

2368    public void addSetRoot(String JavaDoc pattern, String JavaDoc methodName,
2369                           String JavaDoc paramType) {
2370
2371        addRule(pattern,
2372                new SetRootRule(methodName, paramType));
2373
2374    }
2375
2376    /**
2377     * Add a "set properties" rule for the specified parameters.
2378     *
2379     * @param pattern Element matching pattern
2380     * @see SetPropertiesRule
2381     */

2382    public void addSetProperties(String JavaDoc pattern) {
2383
2384        addRule(pattern,
2385                new SetPropertiesRule());
2386
2387    }
2388
2389    /**
2390     * Add a "set properties" rule with a single overridden parameter.
2391     * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
2392     *
2393     * @param pattern Element matching pattern
2394     * @param attributeName map this attribute
2395     * @param propertyName to this property
2396     * @see SetPropertiesRule
2397     */

2398    public void addSetProperties(
2399                                String JavaDoc pattern,
2400                                String JavaDoc attributeName,
2401                                String JavaDoc propertyName) {
2402
2403        addRule(pattern,
2404                new SetPropertiesRule(attributeName, propertyName));
2405
2406    }
2407
2408    /**
2409     * Add a "set properties" rule with overridden parameters.
2410     * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
2411     *
2412     * @param pattern Element matching pattern
2413     * @param attributeNames names of attributes with custom mappings
2414     * @param propertyNames property names these attributes map to
2415     * @see SetPropertiesRule
2416     */

2417    public void addSetProperties(
2418                                String JavaDoc pattern,
2419                                String JavaDoc [] attributeNames,
2420                                String JavaDoc [] propertyNames) {
2421
2422        addRule(pattern,
2423                new SetPropertiesRule(attributeNames, propertyNames));
2424
2425    }
2426
2427
2428    /**
2429     * Add a "set property" rule for the specified parameters.
2430     *
2431     * @param pattern Element matching pattern
2432     * @param name Attribute name containing the property name to be set
2433     * @param value Attribute name containing the property value to set
2434     * @see SetPropertyRule
2435     */

2436    public void addSetProperty(String JavaDoc pattern, String JavaDoc name, String JavaDoc value) {
2437
2438        addRule(pattern,
2439                new SetPropertyRule(name, value));
2440
2441    }
2442
2443
2444    /**
2445     * Add a "set top" rule for the specified parameters.
2446     *
2447     * @param pattern Element matching pattern
2448     * @param methodName Method name to call on the parent element
2449     * @see SetTopRule
2450     */

2451    public void addSetTop(String JavaDoc pattern, String JavaDoc methodName) {
2452
2453        addRule(pattern,
2454                new SetTopRule(methodName));
2455
2456    }
2457
2458
2459    /**
2460     * Add a "set top" rule for the specified parameters.
2461     *
2462     * @param pattern Element matching pattern
2463     * @param methodName Method name to call on the parent element
2464     * @param paramType Java class name of the expected parameter type
2465     * (if you wish to use a primitive type, specify the corresonding
2466     * Java wrapper class instead, such as <code>java.lang.Boolean</code>
2467     * for a <code>boolean</code> parameter)
2468     * @see SetTopRule
2469     */

2470    public void addSetTop(String JavaDoc pattern, String JavaDoc methodName,
2471                          String JavaDoc paramType) {
2472
2473        addRule(pattern,
2474                new SetTopRule(methodName, paramType));
2475
2476    }
2477
2478
2479    // --------------------------------------------------- Object Stack Methods
2480

2481
2482    /**
2483     * Clear the current contents of the object stack.
2484     * <p>
2485     * Calling this method <i>might</i> allow another document of the same type
2486     * to be correctly parsed. However this method was not intended for this
2487     * purpose. In general, a separate Digester object should be created for
2488     * each document to be parsed.
2489     */

2490    public void clear() {
2491
2492        match = "";
2493        bodyTexts.clear();
2494        params.clear();
2495        publicId = null;
2496        stack.clear();
2497        stacksByName.clear();
2498        customContentHandler = null;
2499    }
2500
2501
2502    /**
2503     * Return the top object on the stack without removing it. If there are
2504     * no objects on the stack, return <code>null</code>.
2505     */

2506    public Object JavaDoc peek() {
2507
2508        try {
2509            return (stack.peek());
2510        } catch (EmptyStackException JavaDoc e) {
2511            log.warn("Empty stack (returning null)");
2512            return (null);
2513        }
2514
2515    }
2516
2517
2518    /**
2519     * Return the n'th object down the stack, where 0 is the top element
2520     * and [getCount()-1] is the bottom element. If the specified index
2521     * is out of range, return <code>null</code>.
2522     *
2523     * @param n Index of the desired element, where 0 is the top of the stack,
2524     * 1 is the next element down, and so on.
2525     */

2526    public Object JavaDoc peek(int n) {
2527
2528        try {
2529            return (stack.peek(n));
2530        } catch (EmptyStackException JavaDoc e) {
2531            log.warn("Empty stack (returning null)");
2532            return (null);
2533        }
2534
2535    }
2536
2537
2538    /**
2539     * Pop the top object off of the stack, and return it. If there are
2540     * no objects on the stack, return <code>null</code>.
2541     */

2542    public Object JavaDoc pop() {
2543
2544        try {
2545            return (stack.pop());
2546        } catch (EmptyStackException JavaDoc e) {
2547            log.warn("Empty stack (returning null)");
2548            return (null);
2549        }
2550
2551    }
2552
2553
2554    /**
2555     * Push a new object onto the top of the object stack.
2556     *
2557     * @param object The new object
2558     */

2559    public void push(Object JavaDoc object) {
2560
2561        if (stack.size() == 0) {
2562            root = object;
2563        }
2564        stack.push(object);
2565
2566    }
2567
2568    /**
2569     * Pushes the given object onto the stack with the given name.
2570     * If no stack already exists with the given name then one will be created.
2571     *
2572     * @param stackName the name of the stack onto which the object should be pushed
2573     * @param value the Object to be pushed onto the named stack.
2574     *
2575     * @since 1.6
2576     */

2577    public void push(String JavaDoc stackName, Object JavaDoc value) {
2578        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2579        if (namedStack == null) {
2580            namedStack = new ArrayStack();
2581            stacksByName.put(stackName, namedStack);
2582        }
2583        namedStack.push(value);
2584    }
2585
2586    /**
2587     * <p>Pops (gets and removes) the top object from the stack with the given name.</p>
2588     *
2589     * <p><strong>Note:</strong> a stack is considered empty
2590     * if no objects have been pushed onto it yet.</p>
2591     *
2592     * @param stackName the name of the stack from which the top value is to be popped
2593     * @return the top <code>Object</code> on the stack or or null if the stack is either
2594     * empty or has not been created yet
2595     * @throws EmptyStackException if the named stack is empty
2596     *
2597     * @since 1.6
2598     */

2599    public Object JavaDoc pop(String JavaDoc stackName) {
2600        Object JavaDoc result = null;
2601        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2602        if (namedStack == null) {
2603            if (log.isDebugEnabled()) {
2604                log.debug("Stack '" + stackName + "' is empty");
2605            }
2606            throw new EmptyStackException JavaDoc();
2607            
2608        } else {
2609        
2610            result = namedStack.pop();
2611        }
2612        return result;
2613    }
2614    
2615    /**
2616     * <p>Gets the top object from the stack with the given name.
2617     * This method does not remove the object from the stack.
2618     * </p>
2619     * <p><strong>Note:</strong> a stack is considered empty
2620     * if no objects have been pushed onto it yet.</p>
2621     *
2622     * @param stackName the name of the stack to be peeked
2623     * @return the top <code>Object</code> on the stack or null if the stack is either
2624     * empty or has not been created yet
2625     * @throws EmptyStackException if the named stack is empty
2626     *
2627     * @since 1.6
2628     */

2629    public Object JavaDoc peek(String JavaDoc stackName) {
2630        return peek(stackName, 0);
2631    }
2632
2633    /**
2634     * <p>Gets the top object from the stack with the given name.
2635     * This method does not remove the object from the stack.
2636     * </p>
2637     * <p><strong>Note:</strong> a stack is considered empty
2638     * if no objects have been pushed onto it yet.</p>
2639     *
2640     * @param stackName the name of the stack to be peeked
2641     * @param n Index of the desired element, where 0 is the top of the stack,
2642     * 1 is the next element down, and so on.
2643     * @return the specified <code>Object</code> on the stack.
2644     * @throws EmptyStackException if the named stack is empty
2645     *
2646     * @since 1.6
2647     */

2648    public Object JavaDoc peek(String JavaDoc stackName, int n) {
2649        Object JavaDoc result = null;
2650        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2651        if (namedStack == null ) {
2652            if (log.isDebugEnabled()) {
2653                log.debug("Stack '" + stackName + "' is empty");
2654            }
2655            throw new EmptyStackException JavaDoc();
2656        
2657        } else {
2658        
2659            result = namedStack.peek(n);
2660        }
2661        return result;
2662    }
2663
2664    /**
2665     * <p>Is the stack with the given name empty?</p>
2666     * <p><strong>Note:</strong> a stack is considered empty
2667     * if no objects have been pushed onto it yet.</p>
2668     * @param stackName the name of the stack whose emptiness
2669     * should be evaluated
2670     * @return true if the given stack if empty
2671     *
2672     * @since 1.6
2673     */

2674    public boolean isEmpty(String JavaDoc stackName) {
2675        boolean result = true;
2676        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
2677        if (namedStack != null ) {
2678            result = namedStack.isEmpty();
2679        }
2680        return result;
2681    }
2682    
2683    /**
2684     * Returns the root element of the tree of objects created as a result
2685     * of applying the rule objects to the input XML.
2686     * <p>
2687     * If the digester stack was "primed" by explicitly pushing a root
2688     * object onto the stack before parsing started, then that root object
2689     * is returned here.
2690     * <p>
2691     * Alternatively, if a Rule which creates an object (eg ObjectCreateRule)
2692     * matched the root element of the xml, then the object created will be
2693     * returned here.
2694     * <p>
2695     * In other cases, the object most recently pushed onto an empty digester
2696     * stack is returned. This would be a most unusual use of digester, however;
2697     * one of the previous configurations is much more likely.
2698     * <p>
2699     * Note that when using one of the Digester.parse methods, the return
2700     * value from the parse method is exactly the same as the return value
2701     * from this method. However when the Digester is being used as a
2702     * SAXContentHandler, no such return value is available; in this case, this
2703     * method allows you to access the root object that has been created
2704     * after parsing has completed.
2705     *
2706     * @return the root object that has been created after parsing
2707     * or null if the digester has not parsed any XML yet.
2708     */

2709    public Object JavaDoc getRoot() {
2710        return root;
2711    }
2712    
2713    /**
2714     * This method allows the "root" variable to be reset to null.
2715     * <p>
2716     * It is not considered safe for a digester instance to be reused
2717     * to parse multiple xml documents. However if you are determined to
2718     * do so, then you should call both clear() and resetRoot() before
2719     * each parse.
2720     *
2721     * @since 1.7
2722     */

2723    public void resetRoot() {
2724        root = null;
2725    }
2726
2727    // ------------------------------------------------ Parameter Stack Methods
2728

2729
2730    // ------------------------------------------------------ Protected Methods
2731

2732
2733    /**
2734     * <p>
2735     * Provide a hook for lazy configuration of this <code>Digester</code>
2736     * instance. The default implementation does nothing, but subclasses
2737     * can override as needed.
2738     * </p>
2739     *
2740     * <p>
2741     * <strong>Note</strong> This method may be called more than once.
2742     * Once only initialization code should be placed in {@link #initialize}
2743     * or the code should take responsibility by checking and setting the
2744     * {@link #configured} flag.
2745     * </p>
2746     */

2747    protected void configure() {
2748
2749        // Do not configure more than once
2750
if (configured) {
2751            return;
2752        }
2753
2754        // Perform lazy configuration as needed
2755
initialize(); // call hook method for subclasses that want to be initialized once only
2756
// Nothing else required by default
2757

2758        // Set the configuration flag to avoid repeating
2759
configured = true;
2760
2761    }
2762    
2763    /**
2764     * <p>
2765     * Provides a hook for lazy initialization of this <code>Digester</code>
2766     * instance.
2767     * The default implementation does nothing, but subclasses
2768     * can override as needed.
2769     * Digester (by default) only calls this method once.
2770     * </p>
2771     *
2772     * <p>
2773     * <strong>Note</strong> This method will be called by {@link #configure}
2774     * only when the {@link #configured} flag is false.
2775     * Subclasses that override <code>configure</code> or who set <code>configured</code>
2776     * may find that this method may be called more than once.
2777     * </p>
2778     *
2779     * @since 1.6
2780     */

2781    protected void initialize() {
2782
2783        // Perform lazy initialization as needed
2784
; // Nothing required by default
2785

2786    }
2787
2788    // -------------------------------------------------------- Package Methods
2789

2790
2791    /**
2792     * Return the set of DTD URL registrations, keyed by public identifier.
2793     */

2794    Map JavaDoc getRegistrations() {
2795
2796        return (entityValidator);
2797
2798    }
2799
2800
2801    /**
2802     * Return the set of rules that apply to the specified match position.
2803     * The selected rules are those that match exactly, or those rules
2804     * that specify a suffix match and the tail of the rule matches the
2805     * current match position. Exact matches have precedence over
2806     * suffix matches, then (among suffix matches) the longest match
2807     * is preferred.
2808     *
2809     * @param match The current match position
2810     *
2811     * @deprecated Call <code>match()</code> on the <code>Rules</code>
2812     * implementation returned by <code>getRules()</code>
2813     */

2814    List JavaDoc getRules(String JavaDoc match) {
2815
2816        return (getRules().match(match));
2817
2818    }
2819
2820
2821    /**
2822     * <p>Return the top object on the parameters stack without removing it. If there are
2823     * no objects on the stack, return <code>null</code>.</p>
2824     *
2825     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2826     * See {@link #params}.</p>
2827     */

2828    public Object JavaDoc peekParams() {
2829
2830        try {
2831            return (params.peek());
2832        } catch (EmptyStackException JavaDoc e) {
2833            log.warn("Empty stack (returning null)");
2834            return (null);
2835        }
2836
2837    }
2838
2839
2840    /**
2841     * <p>Return the n'th object down the parameters stack, where 0 is the top element
2842     * and [getCount()-1] is the bottom element. If the specified index
2843     * is out of range, return <code>null</code>.</p>
2844     *
2845     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2846     * See {@link #params}.</p>
2847     *
2848     * @param n Index of the desired element, where 0 is the top of the stack,
2849     * 1 is the next element down, and so on.
2850     */

2851    public Object JavaDoc peekParams(int n) {
2852
2853        try {
2854            return (params.peek(n));
2855        } catch (EmptyStackException JavaDoc e) {
2856            log.warn("Empty stack (returning null)");
2857            return (null);
2858        }
2859
2860    }
2861
2862
2863    /**
2864     * <p>Pop the top object off of the parameters stack, and return it. If there are
2865     * no objects on the stack, return <code>null</code>.</p>
2866     *
2867     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2868     * See {@link #params}.</p>
2869     */

2870    public Object JavaDoc popParams() {
2871
2872        try {
2873            if (log.isTraceEnabled()) {
2874                log.trace("Popping params");
2875            }
2876            return (params.pop());
2877        } catch (EmptyStackException JavaDoc e) {
2878            log.warn("Empty stack (returning null)");
2879            return (null);
2880        }
2881
2882    }
2883
2884
2885    /**
2886     * <p>Push a new object onto the top of the parameters stack.</p>
2887     *
2888     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
2889     * See {@link #params}.</p>
2890     *
2891     * @param object The new object
2892     */

2893    public void pushParams(Object JavaDoc object) {
2894        if (log.isTraceEnabled()) {
2895            log.trace("Pushing params");
2896        }
2897        params.push(object);
2898
2899    }
2900
2901    /**
2902     * Create a SAX exception which also understands about the location in
2903     * the digester file where the exception occurs
2904     *
2905     * @return the new exception
2906     */

2907    public SAXException JavaDoc createSAXException(String JavaDoc message, Exception JavaDoc e) {
2908        if ((e != null) &&
2909            (e instanceof InvocationTargetException JavaDoc)) {
2910            Throwable JavaDoc t = ((InvocationTargetException JavaDoc) e).getTargetException();
2911            if ((t != null) && (t instanceof Exception JavaDoc)) {
2912                e = (Exception JavaDoc) t;
2913            }
2914        }
2915        if (locator != null) {
2916            String JavaDoc error = "Error at line " + locator.getLineNumber() + " char " +
2917                    locator.getColumnNumber() + ": " + message;
2918            if (e != null) {
2919                return new SAXParseException JavaDoc(error, locator, e);
2920            } else {
2921                return new SAXParseException JavaDoc(error, locator);
2922            }
2923        }
2924        log.error("No Locator!");
2925        if (e != null) {
2926            return new SAXException JavaDoc(message, e);
2927        } else {
2928            return new SAXException JavaDoc(message);
2929        }
2930    }
2931
2932    /**
2933     * Create a SAX exception which also understands about the location in
2934     * the digester file where the exception occurs
2935     *
2936     * @return the new exception
2937     */

2938    public SAXException JavaDoc createSAXException(Exception JavaDoc e) {
2939        if (e instanceof InvocationTargetException JavaDoc) {
2940            Throwable JavaDoc t = ((InvocationTargetException JavaDoc) e).getTargetException();
2941            if ((t != null) && (t instanceof Exception JavaDoc)) {
2942                e = (Exception JavaDoc) t;
2943            }
2944        }
2945        return createSAXException(e.getMessage(), e);
2946    }
2947
2948    /**
2949     * Create a SAX exception which also understands about the location in
2950     * the digester file where the exception occurs
2951     *
2952     * @return the new exception
2953     */

2954    public SAXException JavaDoc createSAXException(String JavaDoc message) {
2955        return createSAXException(message, null);
2956    }
2957    
2958}
2959
Popular Tags