001/*
002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
003 * 
004 * This software is open source. 
005 * See the bottom of this file for the licence.
006 * 
007 * $Id: SAXReader.java,v 1.39 2001/11/15 23:38:29 jstrachan Exp $
008 */
009
010package org.dom4j.io;
011
012import java.lang.reflect.InvocationTargetException;
013import java.lang.reflect.Method;
014import java.io.BufferedReader;
015import java.io.File;
016import java.io.FileReader;
017import java.io.FileNotFoundException;
018import java.io.InputStream;
019import java.io.IOException;
020import java.io.Reader;
021import java.io.Serializable;
022import java.net.MalformedURLException;
023import java.net.URL;
024import java.util.ArrayList;
025import java.util.StringTokenizer;
026
027import org.dom4j.Document;
028import org.dom4j.DocumentFactory;
029import org.dom4j.ElementHandler;
030import org.dom4j.DocumentException;
031
032import org.xml.sax.ContentHandler;
033import org.xml.sax.EntityResolver;
034import org.xml.sax.ErrorHandler;
035import org.xml.sax.InputSource;
036import org.xml.sax.SAXException;
037import org.xml.sax.SAXNotRecognizedException;
038import org.xml.sax.SAXNotSupportedException;
039import org.xml.sax.SAXParseException;
040import org.xml.sax.XMLFilter;
041import org.xml.sax.XMLReader;
042import org.xml.sax.ext.LexicalHandler;
043import org.xml.sax.helpers.DefaultHandler;
044import org.xml.sax.helpers.XMLReaderFactory;
045
046/** <p><code>SAXReader</code> creates a DOM4J tree from SAX parsing events.</p>
047  *
048  * <p>The actual SAX parser that is used by this class is configurable 
049  * so you can use your favourite SAX parser if you wish. DOM4J comes 
050  * configured with its own SAX parser so you do not need to worry about 
051  * configuring the SAX parser.</p>
052  *
053  * <p>To explicitly configure the SAX parser that is used via Java code you
054  * can use a constructor or use the 
055  * {@link #setXMLReader(XMLReader)} or
056  * {@link #setXMLReaderClassName(String)} methods.</p>
057  *
058  * <p>If the parser is not specified explicitly then the standard SAX 
059  * policy of using the <code>org.xml.sax.driver</code> system property is 
060  * used to determine the implementation class of {@link XMLReader}.</p>
061  *  
062  * <p>If the <code>org.xml.sax.driver</code> system property is not defined 
063  * then JAXP is used via reflection (so that DOM4J is not explicitly dependent 
064  * on the JAXP classes) to load the JAXP configured SAXParser. 
065  * If there is any error creating a JAXP SAXParser an informational message is 
066  * output and  then the default (Aelfred) SAX parser is used instead.</p>
067  *
068  * <p>If you are trying to use JAXP to explicitly set your SAX parser 
069  * and are experiencing problems, you can turn on verbose error reporting 
070  * by defining the system property <code>org.dom4j.verbose</code> to be "true"
071  * which will output a more detailed description of why JAXP could not find a 
072  * SAX parser</p>
073  *
074  * <p>
075  * For more information on JAXP please go to 
076  * <a href="http://java.sun.com/xml/">Sun's Java &amp; XML site</a></p>
077  *
078  * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
079  * @version $Revision: 1.39 $
080  */
081public class SAXReader {
082
083    /** <code>DocumentFactory</code> used to create new document objects */
084    private DocumentFactory factory;
085    
086    /** <code>XMLReader</code> used to parse the SAX events */
087    private XMLReader xmlReader;
088    
089    /** Whether validation should occur */
090    private boolean validating;
091    
092    /** DispatchHandler to call when each <code>Element</code> is encountered */
093    private DispatchHandler dispatchHandler;
094 
095    /** ErrorHandler class to use */
096    private ErrorHandler errorHandler;
097
098    /** The entity resolver */
099    private EntityResolver entityResolver;
100    
101    /** Should element & attribute names and namespace URIs be interned? */
102    private boolean stringInternEnabled = true;
103    
104    /** Should internal DTD declarations be expanded into a List in the DTD */
105    private boolean includeInternalDTDDeclarations = false;
106    
107    /** Should external DTD declarations be expanded into a List in the DTD */
108    private boolean includeExternalDTDDeclarations = false;
109    
110    /** Whether adjacent text nodes should be merged */
111    private boolean mergeAdjacentText = false;    
112    
113    /** Holds value of property stripWhitespaceText. */
114    private boolean stripWhitespaceText = false;
115    
116    
117    //private boolean includeExternalGeneralEntities = false;
118    //private boolean includeExternalParameterEntities = false;
119    
120    /** The SAX filter used to filter SAX events */
121    private XMLFilter xmlFilter;
122    
123    
124    public SAXReader() {
125    }
126
127    public SAXReader(boolean validating) {
128        this.validating = validating;
129    }
130
131    public SAXReader(DocumentFactory factory) {
132        this.factory = factory;
133    }
134
135    public SAXReader(DocumentFactory factory, boolean validating) {
136        this.factory = factory;
137        this.validating = validating;
138    }
139
140    public SAXReader(XMLReader xmlReader) {
141        this.xmlReader = xmlReader;
142    }
143
144    public SAXReader(XMLReader xmlReader, boolean validating) {
145        this.xmlReader = xmlReader;
146        this.validating = validating;
147    }
148
149    public SAXReader(String xmlReaderClassName) throws SAXException {
150        if (xmlReaderClassName != null) {
151            this.xmlReader = XMLReaderFactory.createXMLReader(xmlReaderClassName);
152        }
153    }
154    
155    public SAXReader(String xmlReaderClassName, boolean validating) throws SAXException {
156        if (xmlReaderClassName != null) {
157            this.xmlReader = XMLReaderFactory.createXMLReader(xmlReaderClassName);
158        }
159        this.validating = validating;
160    }
161
162
163    
164    /** Allows a SAX property to be set on the underlying SAX parser.
165      * This can be useful to set parser-specific properties
166      * such as the location of schema or DTD resources. 
167      * Though use this method with caution as it has the possibility
168      * of breaking the standard behaviour.
169      * An alternative to calling this method is to correctly configure an 
170      * XMLReader object instance and call the {@link #setXMLReader(XMLReader)} method
171      *
172      * @name is the SAX property name
173      * @value is the value of the SAX property
174      * @throws SAXException if the XMLReader could not be created or
175      * the property could not be changed. 
176      */
177    public void setProperty(String name, Object value) throws SAXException {
178        getXMLReader().setProperty(name, value);
179    }
180    
181    
182    /** Allows a SAX featuer on the underlying SAX parser.
183      * This can be useful to set parser-specific features. 
184      * Though use this method with caution as it has the possibility
185      * of breaking the standard behaviour.
186      * An alternative to calling this method is to correctly configure an 
187      * XMLReader object instance and call the {@link #setXMLReader(XMLReader)} method
188      *
189      * @name is the SAX feature name
190      * @value is the value of the SAX feature
191      * @throws SAXException if the XMLReader could not be created or
192      * the feature could not be changed. 
193      */
194    public void setFeature(String name, boolean value) throws SAXException {
195        getXMLReader().setFeature(name, value);
196    }
197    
198        
199    /** <p>Reads a Document from the given <code>File</code></p>
200      *
201      * @param file is the <code>File</code> to read from.
202      * @return the newly created Document instance
203      * @throws DocumentException if an error occurs during parsing.
204      * @throws MalformedURLException if a URL could not be made for the given File
205      */
206    public Document read(File file) throws DocumentException, MalformedURLException {
207        return read( file.toURL() );
208    }
209    
210    /** <p>Reads a Document from the given <code>URL</code> using SAX</p>
211      *
212      * @param url <code>URL</code> to read from.
213      * @return the newly created Document instance
214      * @throws DocumentException if an error occurs during parsing.
215      */
216    public Document read(URL url) throws DocumentException {
217        String systemID = url.toExternalForm();
218        return read(new InputSource(systemID));
219    }
220    
221    /** <p>Reads a Document from the given URL or filename using SAX.</p>
222      *
223      * <p>
224      * If the systemId contains a <code>':'</code> character then it is
225      * assumed to be a URL otherwise its assumed to be a file name.
226      * If you want finer grained control over this mechansim then please
227      * explicitly pass in either a {@link URL} or a {@link File} instance
228      * instead of a {@link String} to denote the source of the document.
229      * </p>
230      *
231      * @param systemId is a URL for a document or a file name.
232      * @return the newly created Document instance
233      * @throws DocumentException if an error occurs during parsing.
234      */
235    public Document read(String systemId) throws DocumentException {
236        return read(new InputSource(systemId));        
237    }
238
239    /** <p>Reads a Document from the given stream using SAX</p>
240      *
241      * @param in <code>InputStream</code> to read from.
242      * @return the newly created Document instance
243      * @throws DocumentException if an error occurs during parsing.
244      */
245    public Document read(InputStream in) throws DocumentException {
246        return read(new InputSource(in));
247    }
248
249    /** <p>Reads a Document from the given <code>Reader</code> using SAX</p>
250      *
251      * @param reader is the reader for the input
252      * @return the newly created Document instance
253      * @throws DocumentException if an error occurs during parsing.
254      */
255    public Document read(Reader reader) throws DocumentException {
256        return read(new InputSource(reader));
257    }
258
259    /** <p>Reads a Document from the given stream using SAX</p>
260      *
261      * @param in <code>InputStream</code> to read from.
262      * @param systemId is the URI for the input
263      * @return the newly created Document instance
264      * @throws DocumentException if an error occurs during parsing.
265      */
266    public Document read(InputStream in, String systemId) throws DocumentException {
267        InputSource source = new InputSource(in);
268        source.setSystemId(systemId);
269        return read(source);
270    }
271
272    /** <p>Reads a Document from the given <code>Reader</code> using SAX</p>
273      *
274      * @param reader is the reader for the input
275      * @param systemId is the URI for the input
276      * @return the newly created Document instance
277      * @throws DocumentException if an error occurs during parsing.
278      */
279    public Document read(Reader reader, String SystemId) throws DocumentException {
280        InputSource source = new InputSource(reader);
281        source.setSystemId(SystemId);
282        return read(source);
283    }
284    
285    /** <p>Reads a Document from the given <code>InputSource</code> using SAX</p>
286      *
287      * @param in <code>InputSource</code> to read from.
288      * @param systemId is the URI for the input
289      * @return the newly created Document instance
290      * @throws DocumentException if an error occurs during parsing.
291      */
292    public Document read(InputSource in) throws DocumentException {
293        try {
294            XMLReader xmlReader = getXMLReader();
295            
296            xmlReader = installXMLFilter(xmlReader);
297            
298            EntityResolver entityResolver = xmlReader.getEntityResolver();
299            if ( entityResolver == null ) {
300                entityResolver = this.entityResolver;
301                if ( entityResolver == null ) {
302                    entityResolver = createDefaultEntityResolver( in.getSystemId() );
303                }
304                xmlReader.setEntityResolver( entityResolver );
305            }
306            else {
307                if ( this.entityResolver != null ) {
308                    xmlReader.setEntityResolver( this.entityResolver );
309                }
310            }
311            
312            SAXContentHandler contentHandler = createContentHandler(xmlReader);
313            contentHandler.setEntityResolver( entityResolver );
314            contentHandler.setInputSource( in );
315            contentHandler.setIncludeInternalDTDDeclarations( isIncludeInternalDTDDeclarations() );
316            contentHandler.setIncludeExternalDTDDeclarations( isIncludeExternalDTDDeclarations() );
317            contentHandler.setMergeAdjacentText( isMergeAdjacentText() );
318            contentHandler.setStripWhitespaceText( isStripWhitespaceText() );
319            xmlReader.setContentHandler(contentHandler);
320
321            configureReader(xmlReader, contentHandler);
322        
323            xmlReader.parse(in);
324            return contentHandler.getDocument();
325        } 
326        catch (Exception e) {
327            if (e instanceof SAXParseException) {
328                //e.printStackTrace();
329                SAXParseException parseException = (SAXParseException) e;
330                String systemId = parseException.getSystemId();
331                if ( systemId == null ) {
332                    systemId = "";
333                }
334                String message = "Error on line " 
335                    + parseException.getLineNumber()
336                    + " of document "  + systemId
337                    + " : " + parseException.getMessage();
338                
339                throw new DocumentException(message, e);
340            }
341            else {
342                throw new DocumentException(e.getMessage(), e);
343            }
344        }
345    }
346    
347
348    
349    // Properties
350    //-------------------------------------------------------------------------                
351    
352    /** @return the validation mode, true if validating will be done 
353      * otherwise false.
354      */
355    public boolean isValidating() {
356        return validating;
357    }
358    
359    /** Sets the validation mode.
360      *
361      * @param validating indicates whether or not validation should occur.
362      */
363    public void setValidation(boolean validating) {
364        this.validating = validating;
365    }
366    
367    /** @return whether internal DTD declarations should be expanded into the DocumentType
368      * object or not. 
369      */
370    public boolean isIncludeInternalDTDDeclarations() {
371        return includeInternalDTDDeclarations;
372    }
373    
374    /** Sets whether internal DTD declarations should be expanded into the DocumentType
375      * object or not.
376      *
377      * @param includeInternalDTDDeclarations whether or not DTD declarations should be expanded
378      * and included into the DocumentType object.
379      */
380    public void setIncludeInternalDTDDeclarations(boolean includeInternalDTDDeclarations) {
381        this.includeInternalDTDDeclarations = includeInternalDTDDeclarations;
382    }
383    
384    /** @return whether external DTD declarations should be expanded into the DocumentType
385      * object or not. 
386      */
387    public boolean isIncludeExternalDTDDeclarations() {
388        return includeExternalDTDDeclarations;
389    }
390    
391    /** Sets whether DTD external declarations should be expanded into the DocumentType
392      * object or not.
393      *
394      * @param includeInternalDTDDeclarations whether or not DTD declarations should be expanded
395      * and included into the DocumentType object.
396      */
397    public void setIncludeExternalDTDDeclarations(boolean includeExternalDTDDeclarations) {
398        this.includeExternalDTDDeclarations = includeExternalDTDDeclarations;
399    }
400    
401    /** Sets whether String interning
402      * is enabled or disabled for element & attribute names and namespace URIs.
403      * This proprety is enabled by default.
404      */
405    public boolean isStringInternEnabled() {
406        return stringInternEnabled;
407    }
408    
409    /** Sets whether String interning 
410      * is enabled or disabled for element & attribute names and namespace URIs
411      */
412    public void setStringInternEnabled(boolean stringInternEnabled) {
413        this.stringInternEnabled = stringInternEnabled;
414    }
415    
416    /** Returns whether adjacent text nodes should be merged together.
417      * @return Value of property mergeAdjacentText.
418      */
419    public boolean isMergeAdjacentText() {
420        return mergeAdjacentText;
421    }
422    
423    /** Sets whether or not adjacent text nodes should be merged
424      * together when parsing.
425      * @param mergeAdjacentText New value of property mergeAdjacentText.
426      */
427    public void setMergeAdjacentText(boolean mergeAdjacentText) {
428        this.mergeAdjacentText = mergeAdjacentText;
429    }
430           
431    /** Sets whether whitespace between element start and end tags should be ignored
432      * 
433      * @return Value of property stripWhitespaceText.
434      */
435    public boolean isStripWhitespaceText() {
436        return stripWhitespaceText;
437    }
438    
439    /** Sets whether whitespace between element start and end tags should be ignored.
440      *
441      * @param stripWhitespaceText New value of property stripWhitespaceText.
442      */
443    public void setStripWhitespaceText(boolean stripWhitespaceText) {
444        this.stripWhitespaceText = stripWhitespaceText;
445    }
446    
447    
448    /** @return the <code>DocumentFactory</code> used to create document objects
449      */
450    public DocumentFactory getDocumentFactory() {
451        if (factory == null) {
452            factory = DocumentFactory.getInstance();
453        }
454        return factory;
455    }
456
457    /** <p>This sets the <code>DocumentFactory</code> used to create new documents.
458      * This method allows the building of custom DOM4J tree objects to be implemented
459      * easily using a custom derivation of {@link DocumentFactory}</p>
460      *
461      * @param factory <code>DocumentFactory</code> used to create DOM4J objects
462      */
463    public void setDocumentFactory(DocumentFactory factory) {
464        this.factory = factory;
465    }
466
467    /** @return the <code>ErrorHandler</code> used by SAX
468      */
469    public ErrorHandler getErrorHandler() {
470        return errorHandler;
471    }
472
473    /** Sets the <code>ErrorHandler</code> used by the SAX 
474      * <code>XMLReader</code>.
475      *
476      * @param errorHandler is the <code>ErrorHandler</code> used by SAX
477      */
478    public void setErrorHandler(ErrorHandler errorHandler) {
479        this.errorHandler = errorHandler;
480    }
481
482    /** Returns the current entity resolver used to resolve entities
483     */
484    public EntityResolver getEntityResolver() {
485        return entityResolver;
486    }
487    
488    /** Sets the entity resolver used to resolve entities. 
489     */
490    public void setEntityResolver(EntityResolver entityResolver) {
491        this.entityResolver = entityResolver;
492    }
493
494        /** @return the <code>XMLReader</code> used to parse SAX events
495      */
496    public XMLReader getXMLReader() throws SAXException {
497        if (xmlReader == null) {
498            xmlReader = createXMLReader();
499        }
500        return xmlReader;
501    }
502
503    /** Sets the <code>XMLReader</code> used to parse SAX events
504      *
505      * @param xmlReader is the <code>XMLReader</code> to parse SAX events
506      */
507    public void setXMLReader(XMLReader xmlReader) {
508        this.xmlReader = xmlReader;
509    }
510
511    /** Sets the class name of the <code>XMLReader</code> to be used 
512      * to parse SAX events.
513      *
514      * @param xmlReaderClassName is the class name of the <code>XMLReader</code> 
515      * to parse SAX events
516      */
517    public void setXMLReaderClassName(String xmlReaderClassName) throws SAXException {
518        setXMLReader( XMLReaderFactory.createXMLReader(xmlReaderClassName) );
519    }
520
521    
522    /** Adds the <code>ElementHandler</code> to be called when the 
523      * specified path is encounted.
524      *
525      * @param path is the path to be handled
526      * @param handler is the <code>ElementHandler</code> to be called
527      * by the event based processor.
528      */
529    public void addHandler(String path, ElementHandler handler) {
530        getDispatchHandler().addHandler(path, handler);   
531    }
532    
533    /** Removes the <code>ElementHandler</code> from the event based
534      * processor, for the specified path.
535      *
536      * @param path is the path to remove the <code>ElementHandler</code> for.
537      */
538    public void removeHandler(String path) {
539        getDispatchHandler().removeHandler(path);   
540    }
541    
542    /** When multiple <code>ElementHandler</code> instances have been 
543      * registered, this will set a default <code>ElementHandler</code>
544      * to be called for any path which does <b>NOT</b> have a handler
545      * registered.
546      * @param handler is the <code>ElementHandler</code> to be called
547      * by the event based processor.
548      */
549    public void setDefaultHandler(ElementHandler handler) {
550        getDispatchHandler().setDefaultHandler(handler);   
551    }
552    
553    
554    /** Returns the SAX filter being used to filter SAX events.
555      *
556      * @return the SAX filter being used or null if no SAX filter is installed
557      */
558    public XMLFilter getXMLFilter() {
559        return xmlFilter;
560    }
561    
562    /** Sets the SAX filter to be used when filtering SAX events
563      *
564      * @param xmlFilter is the SAX filter to use or null to disable filtering
565      */
566    public void setXMLFilter(XMLFilter xmlFilter) {
567        this.xmlFilter = xmlFilter;
568    }
569    
570    // Implementation methods    
571    //-------------------------------------------------------------------------    
572    
573    /** Installs any XMLFilter objects required to allow the SAX event stream
574      * to be filtered and preprocessed before it gets to dom4j.
575      *
576      * @return the new XMLFilter if applicable or the original XMLReader if no
577      * filter is being used.
578      */
579    protected XMLReader installXMLFilter(XMLReader xmlReader) {
580        XMLFilter xmlFilter = getXMLFilter();
581        if ( xmlFilter != null ) {
582            // find the root XMLFilter
583            XMLFilter root = xmlFilter;
584            while (true) {
585                XMLReader parent = root.getParent();
586                if ( parent instanceof XMLFilter ) {
587                    root = (XMLFilter) parent;
588                }
589                else {
590                    break;
591                }
592            }
593            root.setParent(xmlReader);
594            return xmlFilter;
595        }
596        return xmlReader;
597    }
598
599    
600    protected DispatchHandler getDispatchHandler() {
601        if (dispatchHandler == null) {
602            dispatchHandler = new DispatchHandler();
603        }
604        return dispatchHandler;   
605    }
606    
607    protected void setDispatchHandler(DispatchHandler dispatchHandler) {
608        this.dispatchHandler = dispatchHandler;
609    }
610    
611    /** Factory Method to allow alternate methods of 
612      * creating and configuring XMLReader objects
613      */
614    protected XMLReader createXMLReader() throws SAXException {
615        return SAXHelper.createXMLReader( isValidating() );
616    }
617    
618    /** Configures the XMLReader before use */
619    protected void configureReader(XMLReader reader, DefaultHandler contentHandler) throws DocumentException {                
620        // configure lexical handling
621        SAXHelper.setParserProperty(
622            reader,
623            "http://xml.org/sax/handlers/LexicalHandler", 
624            contentHandler
625        );
626        
627        // try alternate property just in case
628        SAXHelper.setParserProperty(
629            reader,
630            "http://xml.org/sax/properties/lexical-handler", 
631            contentHandler
632        );
633
634        // register the DeclHandler 
635        if ( includeInternalDTDDeclarations ) {
636            SAXHelper.setParserProperty(
637                reader,
638                "http://xml.org/sax/properties/declaration-handler", 
639                contentHandler
640            );
641        }
642    
643        // configure namespace support
644        SAXHelper.setParserFeature(
645            reader,
646            "http://xml.org/sax/features/namespaces", 
647            true
648        );
649
650        SAXHelper.setParserFeature(
651            reader,
652            "http://xml.org/sax/features/namespace-prefixes", 
653            false
654        );
655
656        // string interning
657        SAXHelper.setParserFeature(
658            reader,
659            "http://xml.org/sax/features/string-interning", 
660            isStringInternEnabled()
661        );
662        
663        // external entites
664/*        
665        SAXHelper.setParserFeature(
666            reader,
667            "http://xml.org/sax/properties/external-general-entities",
668            includeExternalGeneralEntities
669        );
670        SAXHelper.setParserFeature(
671            reader,
672            "http://xml.org/sax/properties/external-parameter-entities",
673            includeExternalParameterEntities
674        );
675*/        
676        try {
677            // configure validation support
678            reader.setFeature(
679                "http://xml.org/sax/features/validation", 
680                isValidating()
681            );
682            if (errorHandler != null) {
683                 reader.setErrorHandler(errorHandler);
684            }
685            else {
686                 reader.setErrorHandler(contentHandler);
687            }
688        } 
689        catch (Exception e) {
690            if (isValidating()) {
691                throw new DocumentException( 
692                    "Validation not supported for XMLReader: " + reader, 
693                    e
694                );
695            }
696
697        }
698    }
699        
700    /** Factory Method to allow user derived SAXContentHandler objects to be used
701      */
702    protected SAXContentHandler createContentHandler(XMLReader reader) {
703        return new SAXContentHandler( 
704            getDocumentFactory(), dispatchHandler 
705        );
706    }
707
708    protected EntityResolver createDefaultEntityResolver( String documentSystemId ) {
709        String prefix = null;
710        if ( documentSystemId != null && documentSystemId.length() > 0 ) {
711            int idx = documentSystemId.lastIndexOf( '/' );
712            if ( idx > 0 ) {
713                prefix = documentSystemId.substring(0, idx+1);
714                
715            }
716        }
717        return new SAXEntityResolver(prefix);
718    }
719    
720    protected static class SAXEntityResolver implements EntityResolver, Serializable {
721        String uriPrefix;
722        
723        public SAXEntityResolver(String uriPrefix) {
724            this.uriPrefix = uriPrefix;
725        }
726        
727        public InputSource resolveEntity(String publicId, String systemId) {            
728            // try create a relative URI reader...
729            if ( systemId != null && systemId.length() > 0 ) {
730                if ( uriPrefix != null && systemId.indexOf( ':' ) <= 0 ) {
731                    systemId = uriPrefix + systemId;
732                }                    
733            }
734            return new InputSource(systemId);
735        }
736    }
737
738    
739    
740}
741
742
743
744
745/*
746 * Redistribution and use of this software and associated documentation
747 * ("Software"), with or without modification, are permitted provided
748 * that the following conditions are met:
749 *
750 * 1. Redistributions of source code must retain copyright
751 *    statements and notices.  Redistributions must also contain a
752 *    copy of this document.
753 *
754 * 2. Redistributions in binary form must reproduce the
755 *    above copyright notice, this list of conditions and the
756 *    following disclaimer in the documentation and/or other
757 *    materials provided with the distribution.
758 *
759 * 3. The name "DOM4J" must not be used to endorse or promote
760 *    products derived from this Software without prior written
761 *    permission of MetaStuff, Ltd.  For written permission,
762 *    please contact dom4j-info@metastuff.com.
763 *
764 * 4. Products derived from this Software may not be called "DOM4J"
765 *    nor may "DOM4J" appear in their names without prior written
766 *    permission of MetaStuff, Ltd. DOM4J is a registered
767 *    trademark of MetaStuff, Ltd.
768 *
769 * 5. Due credit should be given to the DOM4J Project
770 *    (http://dom4j.org/).
771 *
772 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
773 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
774 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
775 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
776 * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
777 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
778 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
779 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
780 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
781 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
782 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
783 * OF THE POSSIBILITY OF SUCH DAMAGE.
784 *
785 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
786 *
787 * $Id: SAXReader.java,v 1.39 2001/11/15 23:38:29 jstrachan Exp $
788 */