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: STAXEventWriter.java,v 1.6 2004/07/14 19:32:23 maartenc Exp $
8    */
9   
10  package org.dom4j.io;
11  
12  import java.io.File;
13  import java.io.FileWriter;
14  import java.io.IOException;
15  import java.io.OutputStream;
16  import java.io.StringWriter;
17  import java.io.Writer;
18  import java.util.Iterator;
19  
20  import javax.xml.namespace.QName;
21  import javax.xml.stream.XMLEventFactory;
22  import javax.xml.stream.XMLOutputFactory;
23  import javax.xml.stream.XMLStreamException;
24  import javax.xml.stream.events.Characters;
25  import javax.xml.stream.events.DTD;
26  import javax.xml.stream.events.EndDocument;
27  import javax.xml.stream.events.EndElement;
28  import javax.xml.stream.events.EntityReference;
29  import javax.xml.stream.events.StartDocument;
30  import javax.xml.stream.events.StartElement;
31  import javax.xml.stream.util.XMLEventConsumer;
32  
33  import org.dom4j.Attribute;
34  import org.dom4j.Branch;
35  import org.dom4j.CDATA;
36  import org.dom4j.Comment;
37  import org.dom4j.Document;
38  import org.dom4j.DocumentType;
39  import org.dom4j.Element;
40  import org.dom4j.Entity;
41  import org.dom4j.Namespace;
42  import org.dom4j.Node;
43  import org.dom4j.ProcessingInstruction;
44  import org.dom4j.Text;
45  
46  /***
47   * Writes DOM4J {@link Node}s to a StAX event stream. In addition the
48   * <code>createXXX</code> methods are provided to directly create STAX events
49   * from DOM4J nodes.
50   *
51   * @author Christian Niles
52   */
53  public class STAXEventWriter {
54      
55      /*** The event stream to which events are written. */
56      private XMLEventConsumer consumer;
57      
58      /*** The event factory used to construct events. */
59      private XMLEventFactory factory = XMLEventFactory.newInstance();
60      
61      private XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
62      
63      public STAXEventWriter() {
64      }
65  
66      /***
67       * Constructs a <code>STAXEventWriter</code> that writes events to the
68       * provided file.
69       *
70       * @param file The file to which events will be written.
71       * @throws XMLStreamException If an error occurs creating an event writer from
72       *     the file.
73       * @throws IOException If an error occurs openin the file for writing.
74       */
75      public STAXEventWriter(File file) throws XMLStreamException, IOException {
76          consumer = outputFactory.createXMLEventWriter(new FileWriter(file));
77      }
78      
79      /***
80       * Constructs a <code>STAXEventWriter</code> that writes events to the
81       * provided character stream.
82       *
83       * @param writer The character stream to which events will be written.
84       * @throws XMLStreamException If an error occurs constructing an event writer
85       *     from the character stream.
86       */
87      public STAXEventWriter(Writer writer) throws XMLStreamException {
88          consumer = outputFactory.createXMLEventWriter(writer);
89      }
90  
91      /***
92       * Constructs a <code>STAXEventWriter</code> that writes events to the
93       * provided stream.
94       *
95       * @param stream The output stream to which events will be written.
96       * @throws XMLStreamException If an error occurs constructing an event writer
97       *     from the stream.
98       */
99      public STAXEventWriter(OutputStream stream) throws XMLStreamException {
100         consumer = outputFactory.createXMLEventWriter(stream);
101     }
102     
103     /***
104      * Constructs a <code>STAXEventWriter</code> that writes events to the
105      * provided event stream.
106      *
107      * @param consumer The event stream to which events will be written.
108      */
109     public STAXEventWriter(XMLEventConsumer consumer) {
110         this.consumer = consumer;
111     }
112     
113     /***
114      * Returns a reference to the underlying event consumer to which events are
115      * written.
116      *
117      * @return The underlying event consumer to which events are written.
118      */
119     public XMLEventConsumer getConsumer() {
120         return consumer;
121     }
122     
123     /***
124      * Sets the underlying event consumer to which events are written.
125      *
126      * @param consumer The event consumer to which events should be written.
127      */
128     public void setConsumer(XMLEventConsumer consumer) {
129         this.consumer = consumer;
130     }
131     
132     /***
133      * Returns a reference to the event factory used to construct STAX events.
134      *
135      * @return The event factory used to construct STAX events.
136      */
137     public XMLEventFactory getEventFactory() {
138         return factory;
139     }
140     
141     /***
142      * Sets the event factory used to construct STAX events.
143      *
144      * @param factory The new event factory.
145      */
146     public void setEventFactory(XMLEventFactory factory) {
147         this.factory = factory;
148     }
149     
150     /***
151      * Writes a DOM4J {@link Node} to the stream. This method is simply a
152      * gateway to the overloaded methods such as {@link #writeElement(Element)}.
153      *
154      * @param n The DOM4J {@link Node} to write to the stream.
155      * @throws XMLStreamException If an error occurs writing to the stream.
156      */
157     public void writeNode(Node n) throws XMLStreamException {
158         switch (n.getNodeType()) {
159             
160             case Node.ELEMENT_NODE :
161                 writeElement((Element) n);
162                 break;
163                 
164             case Node.TEXT_NODE :
165                 writeText((Text) n);
166                 break;
167                 
168             case Node.ATTRIBUTE_NODE :
169                 writeAttribute((Attribute) n);
170                 break;
171                 
172             case Node.NAMESPACE_NODE :
173                 writeNamespace((Namespace) n);
174                 break;
175                 
176             case Node.COMMENT_NODE :
177                 writeComment((Comment) n);
178                 break;
179                 
180             case Node.CDATA_SECTION_NODE :
181                 writeCDATA((CDATA) n);
182                 break;
183                 
184             case Node.PROCESSING_INSTRUCTION_NODE :
185                 writeProcessingInstruction((ProcessingInstruction) n);
186                 break;
187                 
188             case Node.ENTITY_REFERENCE_NODE :
189                 writeEntity((Entity) n);
190                 break;
191                 
192             case Node.DOCUMENT_NODE :
193                 writeDocument((Document) n);
194                 break;
195                 
196             case Node.DOCUMENT_TYPE_NODE :
197                 writeDocumentType((DocumentType) n);
198                 break;
199                 
200             default :
201                 throw new XMLStreamException("Unsupported DOM4J Node: " + n);
202         }
203     }
204     
205     /***
206      * Writes each child node within the provided {@link Branch} instance. This
207      * method simply iterates through the {@link Branch}'s nodes and calls
208      * {@link #writeNode(Node)}.
209      *
210      * @param branch The node whose children will be written to the stream.
211      * @throws XMLStreamException If an error occurs writing to the stream.
212      */
213     public void writeChildNodes(Branch branch) throws XMLStreamException {
214         for (int i = 0, s = branch.nodeCount(); i < s; i++) {
215             Node n = branch.node(i);
216             writeNode(n);
217         }
218     }
219     
220     /***
221      * Writes a DOM4J {@link Element} node and its children to the stream.
222      *
223      * @param elem The {@link Element} node to write to the stream.
224      * @throws XMLStreamException If an error occurs writing to the stream.
225      */
226     public void writeElement(Element elem) throws XMLStreamException {
227         consumer.add(createStartElement(elem));
228         writeChildNodes(elem);
229         consumer.add(createEndElement(elem));
230     }
231     
232     /***
233      * Constructs a STAX {@link StartElement} event from a DOM4J
234      * {@link Element}.
235      *
236      * @param elem The {@link Element} from which to construct the event.
237      * @return The newly constructed {@link StartElement} event.
238      */
239     public StartElement createStartElement(Element elem) {
240         
241         // create name
242         QName tagName = createQName(elem.getQName());
243         
244         // create attribute & namespace iterators
245         Iterator attrIter = new AttributeIterator(elem.attributeIterator());
246         Iterator nsIter = new NamespaceIterator(
247                 elem.declaredNamespaces().iterator());
248         
249         // create start event
250         return factory.createStartElement(tagName, attrIter, nsIter);
251     }
252     
253     /***
254      * Constructs a STAX {@link EndElement} event from a DOM4J {@link Element}.
255      *
256      * @param elem The {@link Element} from which to construct the event.
257      * @return The newly constructed {@link EndElement} event.
258      */
259     public EndElement createEndElement(Element elem) {
260         QName tagName = createQName(elem.getQName());
261         Iterator nsIter = new NamespaceIterator(
262         elem.declaredNamespaces().iterator());
263         
264         return factory.createEndElement(tagName, nsIter);
265     }
266     
267     /***
268      * Writes a DOM4J {@link Attribute} to the stream.
269      *
270      * @param attr The {@link Attribute} to write to the stream.
271      * @throws XMLStreamException If an error occurs writing to the stream.
272      */
273     public void writeAttribute(Attribute attr) throws XMLStreamException {
274         consumer.add(createAttribute(attr));
275     }
276     
277     /***
278      * Constructs a STAX {@link javax.xml.stream.events.Attribute} event from
279      * a DOM4J {@link Attribute}.
280      *
281      * @param attr The {@link Attribute} from which to construct the event.
282      * @return The newly constructed {@link javax.xml.stream.events.Attribute}
283      * 		event.
284      */
285     public javax.xml.stream.events.Attribute createAttribute(Attribute attr) {
286         QName attrName = createQName(attr.getQName());
287         String value = attr.getValue();
288         
289         return factory.createAttribute(attrName, value);
290     }
291     
292     /***
293      * Writes a DOM4J {@link Namespace} to the stream.
294      *
295      * @param ns The {@link Namespace} to write to the stream.
296      * @throws XMLStreamException If an error occurs writing to the stream.
297      */
298     public void writeNamespace(Namespace ns) throws XMLStreamException {
299         consumer.add(createNamespace(ns));
300     }
301     
302     /***
303      * Constructs a STAX {@link javax.xml.stream.events.Namespace} event from
304      * a DOM4J {@link Namespace}.
305      *
306      * @param ns The {@link Namespace} from which to construct the event.
307      * @return The constructed {@link javax.xml.stream.events.Namespace} event.
308      */
309     public javax.xml.stream.events.Namespace createNamespace(Namespace ns) {
310         String prefix = ns.getPrefix();
311         String uri = ns.getURI();
312         
313         return factory.createNamespace(prefix, uri);
314     }
315     
316     /***
317      * Writes a DOM4J {@link Text} to the stream.
318      *
319      * @param text The {@link Text} to write to the stream.
320      * @throws XMLStreamException If an error occurs writing to the stream.
321      */
322     public void writeText(Text text) throws XMLStreamException {
323         consumer.add(createCharacters(text));
324     }
325     
326     /***
327      * Constructs a STAX {@link Characters} event from a DOM4J {@link Text}.
328      *
329      * @param text The {@link Text} from which to construct the event.
330      * @return The constructed {@link Characters} event.
331      */
332     public Characters createCharacters(Text text) {
333         return factory.createCharacters(text.getText());
334     }
335     
336     /***
337      * Writes a DOM4J {@link CDATA} to the event stream.
338      *
339      * @param cdata The {@link CDATA} to write to the stream.
340      * @throws XMLStreamException If an error occurs writing to the stream.
341      */
342     public void writeCDATA(CDATA cdata) throws XMLStreamException {
343         consumer.add(createCharacters(cdata));
344     }
345     
346     /***
347      * Constructs a STAX {@link Characters} event from a DOM4J {@link CDATA}.
348      *
349      * @param cdata The {@link CDATA} from which to construct the event.
350      * @return The newly constructed {@link Characters} event.
351      */
352     public Characters createCharacters(CDATA cdata) {
353         return factory.createCData(cdata.getText());
354     }
355     
356     /***
357      * Writes a DOM4J {@link Comment} to the stream.
358      *
359      * @param comment The {@link Comment} to write to the stream.
360      * @throws XMLStreamException If an error occurs writing to the stream.
361      */
362     public void writeComment(Comment comment) throws XMLStreamException {
363         consumer.add(createComment(comment));
364     }
365     
366     /***
367      * Constructs a STAX {@link javax.xml.stream.events.Comment} event from a
368      * DOM4J {@link Comment}.
369      *
370      * @param comment The {@link Comment} from which to construct the event.
371      * @return The constructed {@link javax.xml.stream.events.Comment} event.
372      */
373     public javax.xml.stream.events.Comment createComment(Comment comment) {
374         return factory.createComment(comment.getText());
375     }
376     
377     /***
378      * Writes a DOM4J {@link ProcessingInstruction} to the stream.
379      *
380      * @param pi The {@link ProcessingInstruction} to write to the stream.
381      * @throws XMLStreamException If an error occurs writing to the stream.
382      */
383     public void writeProcessingInstruction(ProcessingInstruction pi) throws XMLStreamException {
384         consumer.add(createProcessingInstruction(pi));
385     }
386     
387     /***
388      * Constructs a STAX {@link javax.xml.stream.events.ProcessingInstruction}
389      * event from a DOM4J {@link ProcessingInstruction}.
390      *
391      * @param pi The {@link ProcessingInstruction} from which to construct the
392      *        event.
393      * @return The constructed
394      * 		{@link javax.xml.stream.events.ProcessingInstruction} event.
395      */
396     public javax.xml.stream.events.ProcessingInstruction createProcessingInstruction(
397             ProcessingInstruction pi) {
398         
399         String target = pi.getTarget();
400         String data = pi.getText();
401         
402         return factory.createProcessingInstruction(target, data);
403     }
404     
405     /***
406      * Writes a DOM4J {@link Entity} to the stream.
407      *
408      * @param entity The {@link Entity} to write to the stream.
409      * @throws XMLStreamException If an error occurs writing to the stream.
410      */
411     public void writeEntity(Entity entity) throws XMLStreamException {
412         consumer.add(createEntityReference(entity));
413     }
414     
415     /***
416      * Constructs a STAX {@link EntityReference} event from a DOM4J
417      * {@link Entity}.
418      *
419      * @param entity The {@link Entity} from which to construct the event.
420      * @return The constructed {@link EntityReference} event.
421      */
422     private EntityReference createEntityReference(Entity entity) {
423         return factory.createEntityReference(entity.getName(), null);
424     }
425     
426     /***
427      * Writes a DOM4J {@link DocumentType} to the stream.
428      *
429      * @param docType The {@link DocumentType} to write to the stream.
430      * @throws XMLStreamException If an error occurs writing to the stream.
431      */
432     public void writeDocumentType(DocumentType docType) throws XMLStreamException {
433         consumer.add(createDTD(docType));
434     }
435     
436     /***
437      * Constructs a STAX {@link DTD} event from a DOM4J {@link DocumentType}.
438      *
439      * @param docType The {@link DocumentType} from which to construct the
440      *        event.
441      * @return The constructed {@link DTD} event.
442      */
443     public DTD createDTD(DocumentType docType) {
444         StringWriter decl = new StringWriter();
445         try {
446             docType.write(decl);
447         } catch (IOException e) {
448             throw new RuntimeException("Error writing DTD", e);
449         }
450         
451         return factory.createDTD(decl.toString());
452     }
453     
454     /***
455      * Writes a DOM4J {@link Document} node, and all its contents, to the
456      * stream.
457      *
458      * @param doc The {@link Document} to write to the stream.
459      * @throws XMLStreamException If an error occurs writing to the stream.
460      */
461     public void writeDocument(Document doc) throws XMLStreamException {
462         consumer.add(createStartDocument(doc));
463         
464         writeChildNodes(doc);
465         
466         consumer.add(createEndDocument(doc));
467     }
468     
469     /***
470      * Constructs a STAX {@link StartDocument} event from a DOM4J
471      * {@link Document}.
472      *
473      * @param doc The {@link Document} from which to construct the event.
474      * @return The constructed {@link StartDocument} event.
475      */
476     public StartDocument createStartDocument(Document doc) {
477         String encoding = doc.getXMLEncoding();
478         if (encoding != null) {
479             return factory.createStartDocument(encoding);
480         } else {
481             return factory.createStartDocument();
482         }
483     }
484     
485     /***
486      * Constructs a STAX {@link EndDocument} event from a DOM4J
487      * {@link Document}.
488      *
489      * @param doc The {@link Document} from which to construct the event.
490      * @return The constructed {@link EndDocument} event.
491      */
492     public EndDocument createEndDocument(Document doc) {
493         return factory.createEndDocument();
494     }
495     
496     /***
497      * Constructs a STAX {@link QName} from a DOM4J {@link org.dom4j.QName}.
498      *
499      * @param qname The {@link org.dom4j.QName} from which to construct the
500      *        STAX {@link QName}.
501      * @return The constructed {@link QName}.
502      */
503     public QName createQName(org.dom4j.QName qname) {
504         return new QName(qname.getNamespaceURI(), qname.getName(),
505         qname.getNamespacePrefix());
506     }
507     
508     /***
509      * Internal {@link Iterator} implementation used to pass DOM4J
510      * {@link Attribute}s to the stream.
511      */
512     private class AttributeIterator implements Iterator {
513         
514         /*** The underlying DOm4J attribute iterator. */
515         private Iterator iter;
516         
517         public AttributeIterator(Iterator iter) {
518             this.iter = iter;
519         }
520         
521         public boolean hasNext() {
522             return iter.hasNext();
523         }
524         
525         public Object next() {
526             Attribute attr = (Attribute) iter.next();
527             QName attrName = createQName(attr.getQName());
528             String value = attr.getValue();
529             
530             return factory.createAttribute(attrName, value);
531         }
532         
533         public void remove() {
534             throw new UnsupportedOperationException();
535         }
536         
537     }
538     
539     /***
540      * Internal {@link Iterator} implementation used to pass DOM4J
541      * {@link Namespace}s to the stream.
542      */
543     private class NamespaceIterator implements Iterator {
544         
545         private Iterator iter;
546         
547         public NamespaceIterator(Iterator iter) {
548             this.iter = iter;
549         }
550         
551         public boolean hasNext() {
552             return iter.hasNext();
553         }
554         
555         public Object next() {
556             Namespace ns = (Namespace) iter.next();
557             String prefix = ns.getPrefix();
558             String nsURI = ns.getURI();
559             
560             return factory.createNamespace(prefix, nsURI);
561         }
562         
563         public void remove() {
564             throw new UnsupportedOperationException();
565         }
566         
567     }
568     
569 }
570 
571 
572 
573 
574 /*
575  * Redistribution and use of this software and associated documentation
576  * ("Software"), with or without modification, are permitted provided
577  * that the following conditions are met:
578  *
579  * 1. Redistributions of source code must retain copyright
580  *    statements and notices.  Redistributions must also contain a
581  *    copy of this document.
582  *
583  * 2. Redistributions in binary form must reproduce the
584  *    above copyright notice, this list of conditions and the
585  *    following disclaimer in the documentation and/or other
586  *    materials provided with the distribution.
587  *
588  * 3. The name "DOM4J" must not be used to endorse or promote
589  *    products derived from this Software without prior written
590  *    permission of MetaStuff, Ltd.  For written permission,
591  *    please contact dom4j-info@metastuff.com.
592  *
593  * 4. Products derived from this Software may not be called "DOM4J"
594  *    nor may "DOM4J" appear in their names without prior written
595  *    permission of MetaStuff, Ltd. DOM4J is a registered
596  *    trademark of MetaStuff, Ltd.
597  *
598  * 5. Due credit should be given to the DOM4J Project - 
599  *    http://www.dom4j.org
600  *
601  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
602  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
603  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
604  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
605  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
606  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
607  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
608  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
609  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
610  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
611  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
612  * OF THE POSSIBILITY OF SUCH DAMAGE.
613  *
614  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
615  *
616  * $Id: STAXEventWriter.java,v 1.6 2004/07/14 19:32:23 maartenc Exp $
617  */