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  */