View Javadoc

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