1
2
3
4
5
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
270 QName tagName = createQName(elem.getQName());
271
272
273 Iterator attrIter = new AttributeIterator(elem.attributeIterator());
274 Iterator nsIter = new NamespaceIterator(elem.declaredNamespaces()
275 .iterator());
276
277
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
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685