1   
2   
3   
4   
5   
6   
7   
8   package org.dom4j.io;
9   
10  import java.io.File;
11  import java.io.FileInputStream;
12  import java.io.FileNotFoundException;
13  import java.io.InputStream;
14  import java.io.Reader;
15  import java.io.Serializable;
16  import java.net.URL;
17  
18  import org.dom4j.Document;
19  import org.dom4j.DocumentException;
20  import org.dom4j.DocumentFactory;
21  import org.dom4j.ElementHandler;
22  
23  import org.xml.sax.EntityResolver;
24  import org.xml.sax.ErrorHandler;
25  import org.xml.sax.InputSource;
26  import org.xml.sax.SAXException;
27  import org.xml.sax.SAXParseException;
28  import org.xml.sax.XMLFilter;
29  import org.xml.sax.XMLReader;
30  import org.xml.sax.helpers.DefaultHandler;
31  import org.xml.sax.helpers.XMLReaderFactory;
32  
33  /***
34   * <p>
35   * <code>SAXReader</code> creates a DOM4J tree from SAX parsing events.
36   * </p>
37   * 
38   * <p>
39   * The actual SAX parser that is used by this class is configurable so you can
40   * use your favourite SAX parser if you wish. DOM4J comes configured with its
41   * own SAX parser so you do not need to worry about configuring the SAX parser.
42   * </p>
43   * 
44   * <p>
45   * To explicitly configure the SAX parser that is used via Java code you can use
46   * a constructor or use the {@link #setXMLReader(XMLReader)}or {@link
47   * #setXMLReaderClassName(String)} methods.
48   * </p>
49   * 
50   * <p>
51   * If the parser is not specified explicitly then the standard SAX policy of
52   * using the <code>org.xml.sax.driver</code> system property is used to
53   * determine the implementation class of {@link XMLReader}.
54   * </p>
55   * 
56   * <p>
57   * If the <code>org.xml.sax.driver</code> system property is not defined then
58   * JAXP is used via reflection (so that DOM4J is not explicitly dependent on the
59   * JAXP classes) to load the JAXP configured SAXParser. If there is any error
60   * creating a JAXP SAXParser an informational message is output and then the
61   * default (Aelfred) SAX parser is used instead.
62   * </p>
63   * 
64   * <p>
65   * If you are trying to use JAXP to explicitly set your SAX parser and are
66   * experiencing problems, you can turn on verbose error reporting by defining
67   * the system property <code>org.dom4j.verbose</code> to be "true" which will
68   * output a more detailed description of why JAXP could not find a SAX parser
69   * </p>
70   * 
71   * <p>
72   * For more information on JAXP please go to <a
73   * href="http://java.sun.com/xml/">Sun's Java & XML site </a>
74   * </p>
75   * 
76   * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
77   * @version $Revision: 1.58 $
78   */
79  public class SAXReader {
80      private static final String SAX_STRING_INTERNING = 
81              "http://xml.org/sax/features/string-interning";
82      private static final String SAX_NAMESPACE_PREFIXES = 
83              "http://xml.org/sax/features/namespace-prefixes";
84      private static final String SAX_NAMESPACES = 
85              "http://xml.org/sax/features/namespaces";
86      private static final String SAX_DECL_HANDLER = 
87              "http://xml.org/sax/properties/declaration-handler";
88      private static final String SAX_LEXICAL_HANDLER = 
89              "http://xml.org/sax/properties/lexical-handler";
90      private static final String SAX_LEXICALHANDLER = 
91              "http://xml.org/sax/handlers/LexicalHandler";
92  
93      /*** <code>DocumentFactory</code> used to create new document objects */
94      private DocumentFactory factory;
95  
96      /*** <code>XMLReader</code> used to parse the SAX events */
97      private XMLReader xmlReader;
98  
99      /*** Whether validation should occur */
100     private boolean validating;
101 
102     /*** DispatchHandler to call when each <code>Element</code> is encountered */
103     private DispatchHandler dispatchHandler;
104 
105     /*** ErrorHandler class to use */
106     private ErrorHandler errorHandler;
107 
108     /*** The entity resolver */
109     private EntityResolver entityResolver;
110 
111     /*** Should element & attribute names and namespace URIs be interned? */
112     private boolean stringInternEnabled = true;
113 
114     /*** Should internal DTD declarations be expanded into a List in the DTD */
115     private boolean includeInternalDTDDeclarations = false;
116 
117     /*** Should external DTD declarations be expanded into a List in the DTD */
118     private boolean includeExternalDTDDeclarations = false;
119 
120     /*** Whether adjacent text nodes should be merged */
121     private boolean mergeAdjacentText = false;
122 
123     /*** Holds value of property stripWhitespaceText. */
124     private boolean stripWhitespaceText = false;
125 
126     /*** Should we ignore comments */
127     private boolean ignoreComments = false;
128 
129     /*** Encoding of InputSource - null means system default encoding */
130     private String encoding = null;
131 
132     
133     
134 
135     /*** The SAX filter used to filter SAX events */
136     private XMLFilter xmlFilter;
137 
138     public SAXReader() {
139     }
140 
141     public SAXReader(boolean validating) {
142         this.validating = validating;
143     }
144 
145     public SAXReader(DocumentFactory factory) {
146         this.factory = factory;
147     }
148 
149     public SAXReader(DocumentFactory factory, boolean validating) {
150         this.factory = factory;
151         this.validating = validating;
152     }
153 
154     public SAXReader(XMLReader xmlReader) {
155         this.xmlReader = xmlReader;
156     }
157 
158     public SAXReader(XMLReader xmlReader, boolean validating) {
159         this.xmlReader = xmlReader;
160         this.validating = validating;
161     }
162 
163     public SAXReader(String xmlReaderClassName) throws SAXException {
164         if (xmlReaderClassName != null) {
165             this.xmlReader = XMLReaderFactory
166                     .createXMLReader(xmlReaderClassName);
167         }
168     }
169 
170     public SAXReader(String xmlReaderClassName, boolean validating)
171             throws SAXException {
172         if (xmlReaderClassName != null) {
173             this.xmlReader = XMLReaderFactory
174                     .createXMLReader(xmlReaderClassName);
175         }
176 
177         this.validating = validating;
178     }
179 
180     /***
181      * Allows a SAX property to be set on the underlying SAX parser. This can be
182      * useful to set parser-specific properties such as the location of schema
183      * or DTD resources. Though use this method with caution as it has the
184      * possibility of breaking the standard behaviour. An alternative to calling
185      * this method is to correctly configure an XMLReader object instance and
186      * call the {@link #setXMLReader(XMLReader)}method
187      * 
188      * @param name
189      *            is the SAX property name
190      * @param value
191      *            is the value of the SAX property
192      * 
193      * @throws SAXException
194      *             if the XMLReader could not be created or the property could
195      *             not be changed.
196      */
197     public void setProperty(String name, Object value) throws SAXException {
198         getXMLReader().setProperty(name, value);
199     }
200 
201     /***
202      * Sets a SAX feature on the underlying SAX parser. This can be useful to
203      * set parser-specific features. Though use this method with caution as it
204      * has the possibility of breaking the standard behaviour. An alternative to
205      * calling this method is to correctly configure an XMLReader object
206      * instance and call the {@link #setXMLReader(XMLReader)}method
207      * 
208      * @param name
209      *            is the SAX feature name
210      * @param value
211      *            is the value of the SAX feature
212      * 
213      * @throws SAXException
214      *             if the XMLReader could not be created or the feature could
215      *             not be changed.
216      */
217     public void setFeature(String name, boolean value) throws SAXException {
218         getXMLReader().setFeature(name, value);
219     }
220 
221     /***
222      * <p>
223      * Reads a Document from the given <code>File</code>
224      * </p>
225      * 
226      * @param file
227      *            is the <code>File</code> to read from.
228      * 
229      * @return the newly created Document instance
230      * 
231      * @throws DocumentException
232      *             if an error occurs during parsing.
233      */
234     public Document read(File file) throws DocumentException {
235         try {
236             
237 
238 
239 
240 
241 
242 
243             InputSource source = new InputSource(new FileInputStream(file));
244             if (this.encoding != null) {
245                 source.setEncoding(this.encoding);
246             }
247             String path = file.getAbsolutePath();
248 
249             if (path != null) {
250                 
251                 StringBuffer sb = new StringBuffer("file://");
252 
253                 
254                 if (!path.startsWith(File.separator)) {
255                     sb.append("/");
256                 }
257 
258                 path = path.replace('//', '/');
259                 sb.append(path);
260 
261                 source.setSystemId(sb.toString());
262             }
263 
264             return read(source);
265         } catch (FileNotFoundException e) {
266             throw new DocumentException(e.getMessage(), e);
267         }
268     }
269 
270     /***
271      * <p>
272      * Reads a Document from the given <code>URL</code> using SAX
273      * </p>
274      * 
275      * @param url
276      *            <code>URL</code> to read from.
277      * 
278      * @return the newly created Document instance
279      * 
280      * @throws DocumentException
281      *             if an error occurs during parsing.
282      */
283     public Document read(URL url) throws DocumentException {
284         String systemID = url.toExternalForm();
285 
286         InputSource source = new InputSource(systemID);
287         if (this.encoding != null) {
288             source.setEncoding(this.encoding);
289         }
290 
291         return read(source);
292     }
293 
294     /***
295      * <p>
296      * Reads a Document from the given URL or filename using SAX.
297      * </p>
298      * 
299      * <p>
300      * If the systemId contains a <code>':'</code> character then it is
301      * assumed to be a URL otherwise its assumed to be a file name. If you want
302      * finer grained control over this mechansim then please explicitly pass in
303      * either a {@link URL}or a {@link File}instance instead of a {@link
304      * String} to denote the source of the document.
305      * </p>
306      * 
307      * @param systemId
308      *            is a URL for a document or a file name.
309      * 
310      * @return the newly created Document instance
311      * 
312      * @throws DocumentException
313      *             if an error occurs during parsing.
314      */
315     public Document read(String systemId) throws DocumentException {
316         InputSource source = new InputSource(systemId);
317         if (this.encoding != null) {
318             source.setEncoding(this.encoding);
319         }
320 
321         return read(source);
322     }
323 
324     /***
325      * <p>
326      * Reads a Document from the given stream using SAX
327      * </p>
328      * 
329      * @param in
330      *            <code>InputStream</code> to read from.
331      * 
332      * @return the newly created Document instance
333      * 
334      * @throws DocumentException
335      *             if an error occurs during parsing.
336      */
337     public Document read(InputStream in) throws DocumentException {
338         InputSource source = new InputSource(in);
339         if (this.encoding != null) {
340             source.setEncoding(this.encoding);
341         }
342 
343         return read(source);
344     }
345 
346     /***
347      * <p>
348      * Reads a Document from the given <code>Reader</code> using SAX
349      * </p>
350      * 
351      * @param reader
352      *            is the reader for the input
353      * 
354      * @return the newly created Document instance
355      * 
356      * @throws DocumentException
357      *             if an error occurs during parsing.
358      */
359     public Document read(Reader reader) throws DocumentException {
360         InputSource source = new InputSource(reader);
361         if (this.encoding != null) {
362             source.setEncoding(this.encoding);
363         }
364 
365         return read(source);
366     }
367 
368     /***
369      * <p>
370      * Reads a Document from the given stream using SAX
371      * </p>
372      * 
373      * @param in
374      *            <code>InputStream</code> to read from.
375      * @param systemId
376      *            is the URI for the input
377      * 
378      * @return the newly created Document instance
379      * 
380      * @throws DocumentException
381      *             if an error occurs during parsing.
382      */
383     public Document read(InputStream in, String systemId)
384             throws DocumentException {
385         InputSource source = new InputSource(in);
386         source.setSystemId(systemId);
387         if (this.encoding != null) {
388             source.setEncoding(this.encoding);
389         }
390 
391         return read(source);
392     }
393 
394     /***
395      * <p>
396      * Reads a Document from the given <code>Reader</code> using SAX
397      * </p>
398      * 
399      * @param reader
400      *            is the reader for the input
401      * @param systemId
402      *            is the URI for the input
403      * 
404      * @return the newly created Document instance
405      * 
406      * @throws DocumentException
407      *             if an error occurs during parsing.
408      */
409     public Document read(Reader reader, String systemId)
410             throws DocumentException {
411         InputSource source = new InputSource(reader);
412         source.setSystemId(systemId);
413         if (this.encoding != null) {
414             source.setEncoding(this.encoding);
415         }
416 
417         return read(source);
418     }
419 
420     /***
421      * <p>
422      * Reads a Document from the given <code>InputSource</code> using SAX
423      * </p>
424      * 
425      * @param in
426      *            <code>InputSource</code> to read from.
427      * 
428      * @return the newly created Document instance
429      * 
430      * @throws DocumentException
431      *             if an error occurs during parsing.
432      */
433     public Document read(InputSource in) throws DocumentException {
434         try {
435             XMLReader reader = getXMLReader();
436 
437             reader = installXMLFilter(reader);
438 
439             EntityResolver thatEntityResolver = this.entityResolver;
440 
441             if (thatEntityResolver == null) {
442                 thatEntityResolver = createDefaultEntityResolver(in
443                         .getSystemId());
444                 this.entityResolver = thatEntityResolver;
445             }
446 
447             reader.setEntityResolver(thatEntityResolver);
448 
449             SAXContentHandler contentHandler = createContentHandler(reader);
450             contentHandler.setEntityResolver(thatEntityResolver);
451             contentHandler.setInputSource(in);
452 
453             boolean internal = isIncludeInternalDTDDeclarations();
454             boolean external = isIncludeExternalDTDDeclarations();
455 
456             contentHandler.setIncludeInternalDTDDeclarations(internal);
457             contentHandler.setIncludeExternalDTDDeclarations(external);
458             contentHandler.setMergeAdjacentText(isMergeAdjacentText());
459             contentHandler.setStripWhitespaceText(isStripWhitespaceText());
460             contentHandler.setIgnoreComments(isIgnoreComments());
461             reader.setContentHandler(contentHandler);
462 
463             configureReader(reader, contentHandler);
464 
465             reader.parse(in);
466 
467             return contentHandler.getDocument();
468         } catch (Exception e) {
469             if (e instanceof SAXParseException) {
470                 
471                 SAXParseException parseException = (SAXParseException) e;
472                 String systemId = parseException.getSystemId();
473 
474                 if (systemId == null) {
475                     systemId = "";
476                 }
477 
478                 String message = "Error on line "
479                         + parseException.getLineNumber() + " of document "
480                         + systemId + " : " + parseException.getMessage();
481 
482                 throw new DocumentException(message, e);
483             } else {
484                 throw new DocumentException(e.getMessage(), e);
485             }
486         }
487     }
488 
489     
490     
491 
492     /***
493      * DOCUMENT ME!
494      * 
495      * @return the validation mode, true if validating will be done otherwise
496      *         false.
497      */
498     public boolean isValidating() {
499         return validating;
500     }
501 
502     /***
503      * Sets the validation mode.
504      * 
505      * @param validation
506      *            indicates whether or not validation should occur.
507      */
508     public void setValidation(boolean validation) {
509         this.validating = validation;
510     }
511 
512     /***
513      * DOCUMENT ME!
514      * 
515      * @return whether internal DTD declarations should be expanded into the
516      *         DocumentType object or not.
517      */
518     public boolean isIncludeInternalDTDDeclarations() {
519         return includeInternalDTDDeclarations;
520     }
521 
522     /***
523      * Sets whether internal DTD declarations should be expanded into the
524      * DocumentType object or not.
525      * 
526      * @param include
527      *            whether or not DTD declarations should be expanded and
528      *            included into the DocumentType object.
529      */
530     public void setIncludeInternalDTDDeclarations(boolean include) {
531         this.includeInternalDTDDeclarations = include;
532     }
533 
534     /***
535      * DOCUMENT ME!
536      * 
537      * @return whether external DTD declarations should be expanded into the
538      *         DocumentType object or not.
539      */
540     public boolean isIncludeExternalDTDDeclarations() {
541         return includeExternalDTDDeclarations;
542     }
543 
544     /***
545      * Sets whether DTD external declarations should be expanded into the
546      * DocumentType object or not.
547      * 
548      * @param include
549      *            whether or not DTD declarations should be expanded and
550      *            included into the DocumentType object.
551      */
552     public void setIncludeExternalDTDDeclarations(boolean include) {
553         this.includeExternalDTDDeclarations = include;
554     }
555 
556     /***
557      * Sets whether String interning is enabled or disabled for element &
558      * attribute names and namespace URIs. This proprety is enabled by default.
559      * 
560      * @return DOCUMENT ME!
561      */
562     public boolean isStringInternEnabled() {
563         return stringInternEnabled;
564     }
565 
566     /***
567      * Sets whether String interning is enabled or disabled for element &
568      * attribute names and namespace URIs
569      * 
570      * @param stringInternEnabled
571      *            DOCUMENT ME!
572      */
573     public void setStringInternEnabled(boolean stringInternEnabled) {
574         this.stringInternEnabled = stringInternEnabled;
575     }
576 
577     /***
578      * Returns whether adjacent text nodes should be merged together.
579      * 
580      * @return Value of property mergeAdjacentText.
581      */
582     public boolean isMergeAdjacentText() {
583         return mergeAdjacentText;
584     }
585 
586     /***
587      * Sets whether or not adjacent text nodes should be merged together when
588      * parsing.
589      * 
590      * @param mergeAdjacentText
591      *            New value of property mergeAdjacentText.
592      */
593     public void setMergeAdjacentText(boolean mergeAdjacentText) {
594         this.mergeAdjacentText = mergeAdjacentText;
595     }
596 
597     /***
598      * Sets whether whitespace between element start and end tags should be
599      * ignored
600      * 
601      * @return Value of property stripWhitespaceText.
602      */
603     public boolean isStripWhitespaceText() {
604         return stripWhitespaceText;
605     }
606 
607     /***
608      * Sets whether whitespace between element start and end tags should be
609      * ignored.
610      * 
611      * @param stripWhitespaceText
612      *            New value of property stripWhitespaceText.
613      */
614     public void setStripWhitespaceText(boolean stripWhitespaceText) {
615         this.stripWhitespaceText = stripWhitespaceText;
616     }
617 
618     /***
619      * Returns whether we should ignore comments or not.
620      * 
621      * @return boolean
622      */
623     public boolean isIgnoreComments() {
624         return ignoreComments;
625     }
626 
627     /***
628      * Sets whether we should ignore comments or not.
629      * 
630      * @param ignoreComments
631      *            whether we should ignore comments or not.
632      */
633     public void setIgnoreComments(boolean ignoreComments) {
634         this.ignoreComments = ignoreComments;
635     }
636 
637     /***
638      * DOCUMENT ME!
639      * 
640      * @return the <code>DocumentFactory</code> used to create document
641      *         objects
642      */
643     public DocumentFactory getDocumentFactory() {
644         if (factory == null) {
645             factory = DocumentFactory.getInstance();
646         }
647 
648         return factory;
649     }
650 
651     /***
652      * <p>
653      * This sets the <code>DocumentFactory</code> used to create new
654      * documents. This method allows the building of custom DOM4J tree objects
655      * to be implemented easily using a custom derivation of
656      * {@link DocumentFactory}
657      * </p>
658      * 
659      * @param documentFactory
660      *            <code>DocumentFactory</code> used to create DOM4J objects
661      */
662     public void setDocumentFactory(DocumentFactory documentFactory) {
663         this.factory = documentFactory;
664     }
665 
666     /***
667      * DOCUMENT ME!
668      * 
669      * @return the <code>ErrorHandler</code> used by SAX
670      */
671     public ErrorHandler getErrorHandler() {
672         return errorHandler;
673     }
674 
675     /***
676      * Sets the <code>ErrorHandler</code> used by the SAX
677      * <code>XMLReader</code>.
678      * 
679      * @param errorHandler
680      *            is the <code>ErrorHandler</code> used by SAX
681      */
682     public void setErrorHandler(ErrorHandler errorHandler) {
683         this.errorHandler = errorHandler;
684     }
685 
686     /***
687      * Returns the current entity resolver used to resolve entities
688      * 
689      * @return DOCUMENT ME!
690      */
691     public EntityResolver getEntityResolver() {
692         return entityResolver;
693     }
694 
695     /***
696      * Sets the entity resolver used to resolve entities.
697      * 
698      * @param entityResolver
699      *            DOCUMENT ME!
700      */
701     public void setEntityResolver(EntityResolver entityResolver) {
702         this.entityResolver = entityResolver;
703     }
704 
705     /***
706      * DOCUMENT ME!
707      * 
708      * @return the <code>XMLReader</code> used to parse SAX events
709      * 
710      * @throws SAXException
711      *             DOCUMENT ME!
712      */
713     public XMLReader getXMLReader() throws SAXException {
714         if (xmlReader == null) {
715             xmlReader = createXMLReader();
716         }
717 
718         return xmlReader;
719     }
720 
721     /***
722      * Sets the <code>XMLReader</code> used to parse SAX events
723      * 
724      * @param reader
725      *            is the <code>XMLReader</code> to parse SAX events
726      */
727     public void setXMLReader(XMLReader reader) {
728         this.xmlReader = reader;
729     }
730 
731     /***
732      * Returns encoding used for InputSource (null means system default
733      * encoding)
734      * 
735      * @return encoding used for InputSource
736      * 
737      */
738     public String getEncoding() {
739         return encoding;
740     }
741 
742     /***
743      * Sets encoding used for InputSource (null means system default encoding)
744      * 
745      * @param encoding
746      *            is encoding used for InputSource
747      */
748     public void setEncoding(String encoding) {
749         this.encoding = encoding;
750     }
751 
752     /***
753      * Sets the class name of the <code>XMLReader</code> to be used to parse
754      * SAX events.
755      * 
756      * @param xmlReaderClassName
757      *            is the class name of the <code>XMLReader</code> to parse SAX
758      *            events
759      * 
760      * @throws SAXException
761      *             DOCUMENT ME!
762      */
763     public void setXMLReaderClassName(String xmlReaderClassName)
764             throws SAXException {
765         setXMLReader(XMLReaderFactory.createXMLReader(xmlReaderClassName));
766     }
767 
768     /***
769      * Adds the <code>ElementHandler</code> to be called when the specified
770      * path is encounted.
771      * 
772      * @param path
773      *            is the path to be handled
774      * @param handler
775      *            is the <code>ElementHandler</code> to be called by the event
776      *            based processor.
777      */
778     public void addHandler(String path, ElementHandler handler) {
779         getDispatchHandler().addHandler(path, handler);
780     }
781 
782     /***
783      * Removes the <code>ElementHandler</code> from the event based processor,
784      * for the specified path.
785      * 
786      * @param path
787      *            is the path to remove the <code>ElementHandler</code> for.
788      */
789     public void removeHandler(String path) {
790         getDispatchHandler().removeHandler(path);
791     }
792 
793     /***
794      * When multiple <code>ElementHandler</code> instances have been
795      * registered, this will set a default <code>ElementHandler</code> to be
796      * called for any path which does <b>NOT </b> have a handler registered.
797      * 
798      * @param handler
799      *            is the <code>ElementHandler</code> to be called by the event
800      *            based processor.
801      */
802     public void setDefaultHandler(ElementHandler handler) {
803         getDispatchHandler().setDefaultHandler(handler);
804     }
805 
806     /***
807      * This method clears out all the existing handlers and default handler
808      * setting things back as if no handler existed. Useful when reusing an
809      * object instance.
810      */
811     public void resetHandlers() {
812         getDispatchHandler().resetHandlers();
813     }
814 
815     /***
816      * Returns the SAX filter being used to filter SAX events.
817      * 
818      * @return the SAX filter being used or null if no SAX filter is installed
819      */
820     public XMLFilter getXMLFilter() {
821         return xmlFilter;
822     }
823 
824     /***
825      * Sets the SAX filter to be used when filtering SAX events
826      * 
827      * @param filter
828      *            is the SAX filter to use or null to disable filtering
829      */
830     public void setXMLFilter(XMLFilter filter) {
831         this.xmlFilter = filter;
832     }
833 
834     
835     
836 
837     /***
838      * Installs any XMLFilter objects required to allow the SAX event stream to
839      * be filtered and preprocessed before it gets to dom4j.
840      * 
841      * @param reader
842      *            DOCUMENT ME!
843      * 
844      * @return the new XMLFilter if applicable or the original XMLReader if no
845      *         filter is being used.
846      */
847     protected XMLReader installXMLFilter(XMLReader reader) {
848         XMLFilter filter = getXMLFilter();
849 
850         if (filter != null) {
851             
852             XMLFilter root = filter;
853 
854             while (true) {
855                 XMLReader parent = root.getParent();
856 
857                 if (parent instanceof XMLFilter) {
858                     root = (XMLFilter) parent;
859                 } else {
860                     break;
861                 }
862             }
863 
864             root.setParent(reader);
865 
866             return filter;
867         }
868 
869         return reader;
870     }
871 
872     protected DispatchHandler getDispatchHandler() {
873         if (dispatchHandler == null) {
874             dispatchHandler = new DispatchHandler();
875         }
876 
877         return dispatchHandler;
878     }
879 
880     protected void setDispatchHandler(DispatchHandler dispatchHandler) {
881         this.dispatchHandler = dispatchHandler;
882     }
883 
884     /***
885      * Factory Method to allow alternate methods of creating and configuring
886      * XMLReader objects
887      * 
888      * @return DOCUMENT ME!
889      * 
890      * @throws SAXException
891      *             DOCUMENT ME!
892      */
893     protected XMLReader createXMLReader() throws SAXException {
894         return SAXHelper.createXMLReader(isValidating());
895     }
896 
897     /***
898      * Configures the XMLReader before use
899      * 
900      * @param reader
901      *            DOCUMENT ME!
902      * @param handler
903      *            DOCUMENT ME!
904      * 
905      * @throws DocumentException
906      *             DOCUMENT ME!
907      */
908     protected void configureReader(XMLReader reader, DefaultHandler handler)
909             throws DocumentException {
910         
911         SAXHelper.setParserProperty(reader, SAX_LEXICALHANDLER, handler);
912 
913         
914         SAXHelper.setParserProperty(reader, SAX_LEXICAL_HANDLER, handler);
915 
916         
917         if (includeInternalDTDDeclarations || includeExternalDTDDeclarations) {
918             SAXHelper.setParserProperty(reader, SAX_DECL_HANDLER, handler);
919         }
920 
921         
922         SAXHelper.setParserFeature(reader, SAX_NAMESPACES, true);
923 
924         SAXHelper.setParserFeature(reader, SAX_NAMESPACE_PREFIXES, false);
925 
926         
927         SAXHelper.setParserFeature(reader, SAX_STRING_INTERNING,
928                 isStringInternEnabled());
929 
930         
931         
932 
933 
934 
935 
936 
937 
938         
939         SAXHelper.setParserFeature(reader,
940                 "http://xml.org/sax/features/use-locator2", true);
941 
942         try {
943             
944             reader.setFeature("http://xml.org/sax/features/validation",
945                     isValidating());
946 
947             if (errorHandler != null) {
948                 reader.setErrorHandler(errorHandler);
949             } else {
950                 reader.setErrorHandler(handler);
951             }
952         } catch (Exception e) {
953             if (isValidating()) {
954                 throw new DocumentException("Validation not supported for"
955                         + " XMLReader: " + reader, e);
956             }
957         }
958     }
959 
960     /***
961      * Factory Method to allow user derived SAXContentHandler objects to be used
962      * 
963      * @param reader
964      *            DOCUMENT ME!
965      * 
966      * @return DOCUMENT ME!
967      */
968     protected SAXContentHandler createContentHandler(XMLReader reader) {
969         return new SAXContentHandler(getDocumentFactory(), dispatchHandler);
970     }
971 
972     protected EntityResolver createDefaultEntityResolver(String systemId) {
973         String prefix = null;
974 
975         if ((systemId != null) && (systemId.length() > 0)) {
976             int idx = systemId.lastIndexOf('/');
977 
978             if (idx > 0) {
979                 prefix = systemId.substring(0, idx + 1);
980             }
981         }
982 
983         return new SAXEntityResolver(prefix);
984     }
985 
986     protected static class SAXEntityResolver implements EntityResolver,
987             Serializable {
988         protected String uriPrefix;
989 
990         public SAXEntityResolver(String uriPrefix) {
991             this.uriPrefix = uriPrefix;
992         }
993 
994         public InputSource resolveEntity(String publicId, String systemId) {
995             
996             if ((systemId != null) && (systemId.length() > 0)) {
997                 if ((uriPrefix != null) && (systemId.indexOf(':') <= 0)) {
998                     systemId = uriPrefix + systemId;
999                 }
1000             }
1001 
1002             return new InputSource(systemId);
1003         }
1004     }
1005 }
1006 
1007 
1008 
1009 
1010 
1011 
1012 
1013 
1014 
1015 
1016 
1017 
1018 
1019 
1020 
1021 
1022 
1023 
1024 
1025 
1026 
1027 
1028 
1029 
1030 
1031 
1032 
1033 
1034 
1035 
1036 
1037 
1038 
1039 
1040 
1041 
1042