View Javadoc

1   /*
2    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
3    * 
4    * This software is open source. 
5    * See the bottom of this file for the licence.
6    * 
7    * $Id: SAXWriter.java,v 1.22 2004/06/25 12:34:47 maartenc Exp $
8    */
9   
10  package org.dom4j.io;
11  
12  import java.io.IOException;
13  import java.util.HashMap;
14  import java.util.Iterator;
15  import java.util.List;
16  import java.util.Map;
17  
18  import org.dom4j.Attribute;
19  import org.dom4j.Branch;
20  import org.dom4j.CDATA;
21  import org.dom4j.CharacterData;
22  import org.dom4j.Comment;
23  import org.dom4j.Document;
24  import org.dom4j.DocumentType;
25  import org.dom4j.Element;
26  import org.dom4j.Entity;
27  import org.dom4j.Namespace;
28  import org.dom4j.Node;
29  import org.dom4j.ProcessingInstruction;
30  import org.dom4j.Text;
31  import org.dom4j.tree.NamespaceStack;
32  import org.xml.sax.Attributes;
33  import org.xml.sax.ContentHandler;
34  import org.xml.sax.DTDHandler;
35  import org.xml.sax.EntityResolver;
36  import org.xml.sax.ErrorHandler;
37  import org.xml.sax.InputSource;
38  import org.xml.sax.SAXException;
39  import org.xml.sax.SAXNotRecognizedException;
40  import org.xml.sax.SAXNotSupportedException;
41  import org.xml.sax.XMLReader;
42  import org.xml.sax.ext.LexicalHandler;
43  import org.xml.sax.helpers.AttributesImpl;
44  import org.xml.sax.helpers.LocatorImpl;
45  
46  /*** <p><code>SAXWriter</code> writes a DOM4J tree to a SAX ContentHandler.</p>
47    *
48    * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
49    * @version $Revision: 1.22 $
50    */
51  public class SAXWriter implements XMLReader {
52  
53      protected static final String[] LEXICAL_HANDLER_NAMES = {
54          "http://xml.org/sax/properties/lexical-handler",
55          "http://xml.org/sax/handlers/LexicalHandler"
56      };
57      
58      protected static String FEATURE_NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";
59      protected static String FEATURE_NAMESPACES = "http://xml.org/sax/features/namespaces";
60  
61      /*** <code>ContentHandler</code> to which SAX events are raised */
62      private ContentHandler contentHandler;
63      
64      /*** code>DTDHandler</code> fired when a document has a DTD */
65      private DTDHandler dtdHandler;
66      
67      /*** code>EntityResolver</code> fired when a document has a DTD */
68      private EntityResolver entityResolver;
69  
70      private ErrorHandler errorHandler;
71      
72      /*** code>LexicalHandler</code> fired on Entity and CDATA sections */
73      private LexicalHandler lexicalHandler;
74  
75      /*** code>AttributesImpl</code> used when generating the Attributes */
76      private AttributesImpl attributes = new AttributesImpl();
77      
78      /*** Stores the features */
79      private Map features = new HashMap();
80      
81      /*** Stores the properties */
82      private Map properties = new HashMap();
83  
84      /*** Whether namespace declarations are exported as attributes or not */
85      private boolean declareNamespaceAttributes;
86      
87      
88      public SAXWriter() {
89          properties.put( FEATURE_NAMESPACE_PREFIXES, Boolean.FALSE );
90          properties.put( FEATURE_NAMESPACE_PREFIXES, Boolean.TRUE );
91      }
92  
93      public SAXWriter(ContentHandler contentHandler) {
94          this();
95          this.contentHandler = contentHandler;
96      }
97  
98      public SAXWriter(
99          ContentHandler contentHandler, 
100         LexicalHandler lexicalHandler
101     ) {
102         this();
103         this.contentHandler = contentHandler;
104         this.lexicalHandler = lexicalHandler;
105     }
106 
107     public SAXWriter(
108         ContentHandler contentHandler, 
109         LexicalHandler lexicalHandler,
110         EntityResolver entityResolver
111     ) {
112         this();
113         this.contentHandler = contentHandler;
114         this.lexicalHandler = lexicalHandler;
115         this.entityResolver = entityResolver;
116     }
117 
118     
119     /***
120      * A polymorphic method to write any Node to this SAX stream
121      */
122     public void write(Node node) throws SAXException {
123         int nodeType = node.getNodeType();
124         switch (nodeType) {
125             case Node.ELEMENT_NODE:
126                 write((Element) node);
127                 break;
128             case Node.ATTRIBUTE_NODE:
129                 write((Attribute) node);
130                 break;
131             case Node.TEXT_NODE:
132                 write(node.getText());
133                 break;
134             case Node.CDATA_SECTION_NODE:
135                 write((CDATA) node);
136                 break;
137             case Node.ENTITY_REFERENCE_NODE:
138                 write((Entity) node);
139                 break;
140             case Node.PROCESSING_INSTRUCTION_NODE:
141                 write((ProcessingInstruction) node);
142                 break;
143             case Node.COMMENT_NODE:
144                 write((Comment) node);
145                 break;
146             case Node.DOCUMENT_NODE:
147                 write((Document) node);
148                 break;
149             case Node.DOCUMENT_TYPE_NODE:
150                 write((DocumentType) node);
151                 break;
152             case Node.NAMESPACE_NODE:
153                 // Will be output with attributes
154                 //write((Namespace) node);
155                 break;
156             default:
157                 throw new SAXException( "Invalid node type: " + node );
158         }
159     }
160 
161 
162     /*** Generates SAX events for the given Document and all its content
163       *
164       * @param document is the Document to parse
165       * @throws SAXException if there is a SAX error processing the events
166       */
167     public void write(Document document) throws SAXException {
168         if (document != null) {       
169             checkForNullHandlers();
170             
171             documentLocator(document);
172             startDocument();            
173             entityResolver(document);
174             dtdHandler(document);
175             
176             writeContent( document, new NamespaceStack() );
177             endDocument();
178         }
179     }
180     
181     
182     
183     /*** Generates SAX events for the given Element and all its content
184       *
185       * @param element is the Element to parse
186       * @throws SAXException if there is a SAX error processing the events
187       */
188     public void write( Element element ) throws SAXException {
189         write( element, new NamespaceStack() );
190     }
191     
192 
193     /*** <p>Writes the opening tag of an {@link Element},
194       * including its {@link Attribute}s
195       * but without its content.</p>
196       *
197       * @param element <code>Element</code> to output.
198       */
199     public void writeOpen(Element element) throws SAXException {
200         startElement(element, null);
201     }
202 
203     /*** <p>Writes the closing tag of an {@link Element}</p>
204       *
205       * @param element <code>Element</code> to output.
206       */
207     public void writeClose(Element element) throws SAXException {
208         endElement(element);
209     }
210     
211     /*** Generates SAX events for the given text
212       *
213       * @param text is the text to send to the SAX ContentHandler
214       * @throws SAXException if there is a SAX error processing the events
215       */
216     public void write( String text ) throws SAXException {
217         if ( text != null ) {
218             char[] chars = text.toCharArray();
219             contentHandler.characters( chars, 0, chars.length );
220         }
221     }
222     
223     /*** Generates SAX events for the given CDATA
224       *
225       * @param cdata is the CDATA to parse
226       * @throws SAXException if there is a SAX error processing the events
227       */
228     public void write( CDATA cdata ) throws SAXException {
229         String text = cdata.getText();
230         if ( lexicalHandler != null ) {
231             lexicalHandler.startCDATA();
232             write( text );
233             lexicalHandler.endCDATA();
234         }
235         else {
236             write( text );
237         }
238     }
239     
240     /*** Generates SAX events for the given Comment
241       *
242       * @param comment is the Comment to parse
243       * @throws SAXException if there is a SAX error processing the events
244       */
245     public void write( Comment comment ) throws SAXException {
246         if ( lexicalHandler != null ) {
247             String text = comment.getText();
248             char[] chars = text.toCharArray();
249             lexicalHandler.comment( chars, 0, chars.length );
250         }
251     }
252     
253     /*** Generates SAX events for the given Entity
254       *
255       * @param entity is the Entity to parse
256       * @throws SAXException if there is a SAX error processing the events
257       */
258     public void write( Entity entity ) throws SAXException {
259         String text = entity.getText();
260         if ( lexicalHandler != null ) {
261             String name = entity.getName();
262             lexicalHandler.startEntity(name);
263             write( text );
264             lexicalHandler.endEntity(name);
265         }
266         else {
267             write( text );
268         }
269     }
270     
271     /*** Generates SAX events for the given ProcessingInstruction
272       *
273       * @param pi is the ProcessingInstruction to parse
274       * @throws SAXException if there is a SAX error processing the events
275       */
276     public void write( ProcessingInstruction pi ) throws SAXException {        
277         String target = pi.getTarget();
278         String text = pi.getText();
279         contentHandler.processingInstruction(target, text);
280     }
281     
282 
283     
284     /*** Should namespace declarations be converted to "xmlns" attributes. This property
285       * defaults to <code>false</code> as per the SAX specification. 
286       * This property is set via the SAX feature "http://xml.org/sax/features/namespace-prefixes"
287       */ 
288     public boolean isDeclareNamespaceAttributes() {
289         return declareNamespaceAttributes;
290     }
291     
292     /*** Sets whether namespace declarations should be exported as "xmlns" attributes or not.
293       * This property is set from the SAX feature "http://xml.org/sax/features/namespace-prefixes"
294       */ 
295     public void setDeclareNamespaceAttributes(boolean declareNamespaceAttributes) {
296         this.declareNamespaceAttributes = declareNamespaceAttributes;
297     }
298     
299 
300     
301     // XMLReader methods
302     //-------------------------------------------------------------------------                
303 
304     /*** @return the <code>ContentHandler</code> called when SAX events 
305       * are raised
306       */
307     public ContentHandler getContentHandler() {
308         return contentHandler;
309     }
310 
311     /*** Sets the <code>ContentHandler</code> called when SAX events 
312       * are raised
313       *
314       * @param contentHandler is the <code>ContentHandler</code> called when SAX events 
315       * are raised
316       */
317     public void setContentHandler(ContentHandler contentHandler) {
318         this.contentHandler = contentHandler;
319     }
320 
321 
322     /*** @return the <code>DTDHandler</code> 
323       */
324     public DTDHandler getDTDHandler() {
325         return dtdHandler;
326     }
327 
328     /*** Sets the <code>DTDHandler</code>.
329       */
330     public void setDTDHandler(DTDHandler dtdHandler) {
331         this.dtdHandler = dtdHandler;
332     }
333 
334     /*** @return the <code>ErrorHandler</code> 
335       */
336     public ErrorHandler getErrorHandler() {
337         return errorHandler;
338     }
339 
340     /*** Sets the <code>ErrorHandler</code>.
341       */
342     public void setErrorHandler(ErrorHandler errorHandler) {
343         this.errorHandler = errorHandler;
344     }
345 
346     /*** @return the <code>EntityResolver</code> used when a Document contains 
347       * a DTD
348       */
349     public EntityResolver getEntityResolver() {
350         return entityResolver;
351     }
352 
353     /*** Sets the <code>EntityResolver</code> .
354       *
355       * @param entityResolver is the <code>EntityResolver</code> 
356       */
357     public void setEntityResolver(EntityResolver entityResolver) {
358         this.entityResolver = entityResolver;
359     }
360 
361     /*** @return the <code>LexicalHandler</code> used when a Document contains 
362       * a DTD
363       */
364     public LexicalHandler getLexicalHandler() {
365         return lexicalHandler;
366     }
367 
368     /*** Sets the <code>LexicalHandler</code> .
369       *
370       * @param lexicalHandler is the <code>LexicalHandler</code> 
371       */
372     public void setLexicalHandler(LexicalHandler lexicalHandler) {
373         this.lexicalHandler = lexicalHandler;
374     }
375 
376     
377     /*** Sets the <code>XMLReader</code> used to write SAX events to
378       * 
379       * @param xmlReader is the <code>XMLReader</code> 
380       */
381     public void setXMLReader(XMLReader xmlReader) {
382         setContentHandler( xmlReader.getContentHandler() );
383         setDTDHandler( xmlReader.getDTDHandler() );
384         setEntityResolver( xmlReader.getEntityResolver() );
385         setErrorHandler( xmlReader.getErrorHandler() );
386     }
387     
388     /*** Looks up the value of a feature.
389       */
390     public boolean getFeature(String name) 
391             throws SAXNotRecognizedException, SAXNotSupportedException {
392         Boolean answer = (Boolean) features.get(name);
393         return answer != null && answer.booleanValue();
394     }
395 
396     /*** This implementation does actually use any features but just
397       * stores them for later retrieval
398       */
399     public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
400         if ( FEATURE_NAMESPACE_PREFIXES.equals( name ) ) {
401             setDeclareNamespaceAttributes( value );
402         }
403         else if ( FEATURE_NAMESPACE_PREFIXES.equals( name ) ) {
404             if ( ! value ) {
405                 throw new SAXNotSupportedException(name + ". namespace feature is always supported in dom4j." );
406             }
407         }
408         features.put(name, (value) ? Boolean.TRUE : Boolean.FALSE );        
409     }
410 
411     /*** Sets the given SAX property
412       */    
413     public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
414         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
415             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
416                 setLexicalHandler((LexicalHandler) value);
417                 return;
418             }
419         }
420         properties.put(name, value);
421     }
422 
423     /*** Gets the given SAX property
424       */    
425     public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
426         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
427             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
428                 return getLexicalHandler();
429             }
430         }
431         return properties.get(name);
432     }
433 
434     
435 
436     
437     /*** This method is not supported. 
438       */
439     public void parse(String systemId) throws SAXNotSupportedException {
440         throw new SAXNotSupportedException(
441             "This XMLReader can only accept <dom4j> InputSource objects"
442         );
443     }
444 
445     
446     /*** Parses an XML document. 
447       * This method can only accept DocumentInputSource inputs
448       * otherwise a {@link SAXNotSupportedException} exception is thrown.
449       *
450       * @throws SAXNotSupportedException 
451       *      if the input source is not wrapping a dom4j document
452       */
453     public void parse(InputSource input) throws SAXException {
454         if (input instanceof DocumentInputSource) {
455             DocumentInputSource documentInput = (DocumentInputSource) input;
456             Document document = documentInput.getDocument();
457             write( document );
458         }
459         else {
460             throw new SAXNotSupportedException(
461                 "This XMLReader can only accept <dom4j> InputSource objects"
462             );
463         }
464     }
465 
466     
467     
468     // Implementation methods    
469     //-------------------------------------------------------------------------                
470     
471     protected void writeContent( Branch branch, NamespaceStack namespaceStack ) throws SAXException {
472         for ( Iterator iter = branch.nodeIterator(); iter.hasNext(); ) {
473             Object object = iter.next();
474             if ( object instanceof Element ) {
475                 write( (Element) object, namespaceStack );
476             }
477             else if ( object instanceof CharacterData ) { 
478                 if ( object instanceof Text ) {
479                     Text text = (Text) object;
480                     write( text.getText() );
481                 }
482                 else if ( object instanceof CDATA ) {
483                     write( (CDATA) object );
484                 }
485                 else if ( object instanceof Comment ) {
486                     write( (Comment) object );
487                 }
488                 else {
489                     throw new SAXException( "Invalid Node in DOM4J content: " + object + " of type: " + object.getClass() );
490                 }
491             }
492             else if ( object instanceof String ) { 
493                 write( (String) object );
494             }
495             else if ( object instanceof Entity ) { 
496                 write( (Entity) object );
497             }
498             else if ( object instanceof ProcessingInstruction ) { 
499                 write( (ProcessingInstruction) object );
500             }
501             else if ( object instanceof Namespace ) { 
502                 // namespaceStack are written via write of element
503             }
504             else {
505                 throw new SAXException( "Invalid Node in DOM4J content: " + object );
506             }
507         }
508     }
509     
510     /*** The {@link org.xml.sax.Locator} is only really useful when parsing a textual
511       * document as its main purpose is to identify the line and column number.
512       * Since we are processing an in memory tree which will probably have
513       * its line number information removed, we'll just use -1 for the line
514       * and column numbers.
515       */
516     protected void documentLocator(Document document) throws SAXException {
517         LocatorImpl locator = new LocatorImpl();
518         
519         String publicID = null;
520         String systemID = null;
521         DocumentType docType = document.getDocType();
522         if (docType != null) {
523             publicID = docType.getPublicID();
524             systemID = docType.getSystemID();
525         }
526         if ( publicID != null ) {
527             locator.setPublicId(publicID);
528         }
529         if ( systemID != null ) {
530             locator.setSystemId(systemID);
531         }
532             
533         locator.setLineNumber(-1);
534         locator.setColumnNumber(-1);
535         
536         contentHandler.setDocumentLocator( locator );
537     }
538 
539     protected void entityResolver(Document document) throws SAXException {
540         if (entityResolver != null) {
541             DocumentType docType = document.getDocType();
542             if (docType != null) {
543                 String publicID = docType.getPublicID();
544                 String systemID = docType.getSystemID();
545                 
546                 if ( publicID != null || systemID != null ) {
547                     try {
548                         entityResolver.resolveEntity( publicID, systemID );
549                     }
550                     catch (IOException e) {
551                         throw new SAXException( 
552                             "Could not resolve entity publicID: " 
553                             + publicID + " systemID: " + systemID, e 
554                         );
555                     }
556                 }
557             }
558         }
559     }
560 
561     
562     /*** We do not yet support DTD or XML Schemas so this method does nothing
563       * right now.
564       */
565     protected void dtdHandler(Document document) throws SAXException {
566     }
567 
568     protected void startDocument() throws SAXException {
569         contentHandler.startDocument();
570     }
571 
572     protected void endDocument() throws SAXException {
573         contentHandler.endDocument();
574     }
575     
576     protected void write( Element element, NamespaceStack namespaceStack ) throws SAXException {
577         int stackSize = namespaceStack.size();
578         AttributesImpl namespaceAttributes = startPrefixMapping(element, namespaceStack);
579         startElement(element, namespaceAttributes);
580         writeContent(element, namespaceStack);
581         endElement(element);
582         endPrefixMapping(namespaceStack, stackSize);
583     }
584     
585     /*** Fires a SAX startPrefixMapping event for all the namespaceStack
586       * which have just come into scope
587       */
588     protected AttributesImpl startPrefixMapping( Element element, NamespaceStack namespaceStack ) throws SAXException {
589         AttributesImpl namespaceAttributes = null;
590         
591         // start with the namespace of the element
592         Namespace elementNamespace = element.getNamespace();
593         if ((elementNamespace != null) && !isIgnoreableNamespace(elementNamespace, namespaceStack)) {
594             namespaceStack.push(elementNamespace);
595             contentHandler.startPrefixMapping(
596                     elementNamespace.getPrefix(), elementNamespace.getURI());
597             namespaceAttributes = addNamespaceAttribute(namespaceAttributes, elementNamespace);
598         }
599         
600         List declaredNamespaces = element.declaredNamespaces();
601         for ( int i = 0, size = declaredNamespaces.size(); i < size ; i++ ) {
602             Namespace namespace = (Namespace) declaredNamespaces.get(i);
603             if ( ! isIgnoreableNamespace( namespace, namespaceStack ) ) {
604                 namespaceStack.push( namespace );
605                 contentHandler.startPrefixMapping(
606                     namespace.getPrefix(), namespace.getURI()
607                 );
608                 namespaceAttributes = addNamespaceAttribute( namespaceAttributes, namespace );
609             }
610         }
611         return namespaceAttributes;
612     }
613     
614     /*** Fires a SAX endPrefixMapping event for all the namespaceStack which 
615       * have gone out of scope
616       */
617     protected void endPrefixMapping( NamespaceStack namespaceStack, int stackSize ) throws SAXException {                       
618         while ( namespaceStack.size() > stackSize ) {
619             Namespace namespace = namespaceStack.pop();
620             if ( namespace != null ) {
621                 contentHandler.endPrefixMapping( namespace.getPrefix() );            
622             }
623         }
624     }
625     
626     
627     protected void startElement( Element element, AttributesImpl namespaceAttributes ) throws SAXException {                       
628         contentHandler.startElement( 
629             element.getNamespaceURI(), 
630             element.getName(), 
631             element.getQualifiedName(), 
632             createAttributes( element, namespaceAttributes )
633         );
634     }
635 
636     protected void endElement( Element element ) throws SAXException {        
637         contentHandler.endElement( 
638             element.getNamespaceURI(), 
639             element.getName(), 
640             element.getQualifiedName()
641         );
642     }
643 
644     protected Attributes createAttributes( Element element, Attributes namespaceAttributes ) throws SAXException {
645         attributes.clear();
646         if ( namespaceAttributes != null ) {
647             attributes.setAttributes( namespaceAttributes );
648         }
649         
650         for ( Iterator iter = element.attributeIterator(); iter.hasNext(); ) {
651             Attribute attribute = (Attribute) iter.next();
652             attributes.addAttribute( 
653                 attribute.getNamespaceURI(), 
654                 attribute.getName(), 
655                 attribute.getQualifiedName(), 
656                 "CDATA",
657                 attribute.getValue()
658             );
659         }
660         return attributes;
661     }
662     
663     /*** If isDelcareNamespaceAttributes() is enabled then this method will add the
664       * given namespace declaration to the supplied attributes object, creating one if
665       * it does not exist.
666       */
667     protected AttributesImpl addNamespaceAttribute( AttributesImpl namespaceAttributes, Namespace namespace ) {
668         if ( declareNamespaceAttributes ) {
669             if ( namespaceAttributes == null ) {
670                 namespaceAttributes = new AttributesImpl();
671             }
672             String prefix = namespace.getPrefix();
673             String qualifiedName = "xmlns";
674             if ( prefix != null && prefix.length() > 0 ) {
675                 qualifiedName = "xmlns:" + prefix;
676             }
677             String uri = "";
678             String localName = prefix;
679             String type = "CDATA";
680             String value = namespace.getURI();
681             
682             namespaceAttributes.addAttribute( uri, localName, qualifiedName, type, value );
683         }
684         return namespaceAttributes;
685     }
686 
687     
688     /*** @return true if the given namespace is an ignorable namespace 
689       * (such as Namespace.NO_NAMESPACE or Namespace.XML_NAMESPACE) or if the
690       * namespace has already been declared in the current scope
691       */
692     protected boolean isIgnoreableNamespace( Namespace namespace, NamespaceStack namespaceStack ) {
693         if ( namespace.equals( Namespace.NO_NAMESPACE ) || namespace.equals( Namespace.XML_NAMESPACE ) ) {
694             return true;
695         }
696         String uri = namespace.getURI();
697         if ( uri == null || uri.length() <= 0 ) {
698             return true;
699         }
700         return namespaceStack.contains( namespace );
701     }
702 
703     /*** Ensures non-null content handlers?
704       */
705     protected void checkForNullHandlers() {
706     }
707 
708 }
709 
710 
711 
712 
713 /*
714  * Redistribution and use of this software and associated documentation
715  * ("Software"), with or without modification, are permitted provided
716  * that the following conditions are met:
717  *
718  * 1. Redistributions of source code must retain copyright
719  *    statements and notices.  Redistributions must also contain a
720  *    copy of this document.
721  *
722  * 2. Redistributions in binary form must reproduce the
723  *    above copyright notice, this list of conditions and the
724  *    following disclaimer in the documentation and/or other
725  *    materials provided with the distribution.
726  *
727  * 3. The name "DOM4J" must not be used to endorse or promote
728  *    products derived from this Software without prior written
729  *    permission of MetaStuff, Ltd.  For written permission,
730  *    please contact dom4j-info@metastuff.com.
731  *
732  * 4. Products derived from this Software may not be called "DOM4J"
733  *    nor may "DOM4J" appear in their names without prior written
734  *    permission of MetaStuff, Ltd. DOM4J is a registered
735  *    trademark of MetaStuff, Ltd.
736  *
737  * 5. Due credit should be given to the DOM4J Project - 
738  *    http://www.dom4j.org
739  *
740  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
741  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
742  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
743  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
744  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
745  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
746  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
747  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
748  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
749  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
750  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
751  * OF THE POSSIBILITY OF SUCH DAMAGE.
752  *
753  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
754  *
755  * $Id: SAXWriter.java,v 1.22 2004/06/25 12:34:47 maartenc Exp $
756  */