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;
9   
10  import junit.textui.TestRunner;
11  
12  import java.io.ByteArrayInputStream;
13  import java.io.ByteArrayOutputStream;
14  import java.io.StringReader;
15  import java.io.StringWriter;
16  
17  import org.dom4j.io.OutputFormat;
18  import org.dom4j.io.SAXReader;
19  import org.dom4j.io.XMLWriter;
20  import org.dom4j.tree.BaseElement;
21  import org.dom4j.tree.DefaultDocument;
22  
23  import org.xml.sax.ContentHandler;
24  import org.xml.sax.SAXException;
25  import org.xml.sax.helpers.AttributesImpl;
26  
27  /***
28   * A simple test harness to check that the XML Writer works
29   * 
30   * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
31   * @version $Revision: 1.7 $
32   */
33  public class XMLWriterTest extends AbstractTestCase {
34      protected static final boolean VERBOSE = false;
35  
36      public static void main(String[] args) {
37          TestRunner.run(XMLWriterTest.class);
38      }
39  
40      // Test case(s)
41      // -------------------------------------------------------------------------
42      public void testBug1119733() throws Exception {
43          Document doc = DocumentHelper
44                  .parseText("<root><code>foo</code> bar</root>");
45          
46          StringWriter out = new StringWriter();
47          XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
48          writer.write(doc);
49          writer.close();
50          
51          String xml = out.toString();
52  
53          System.out.println(xml);
54          assertEquals("whitespace problem", -1, xml.indexOf("</code>bar"));
55      }
56      
57      public void testBug1119733WithSAXEvents() throws Exception {
58          StringWriter out = new StringWriter();
59          XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
60          writer.startDocument();
61          writer.startElement(null, "root", "root", new AttributesImpl());
62          writer.startElement(null, "code", "code", new AttributesImpl());
63          writer.characters(new char[] {'f', 'o', 'o'}, 0, 3);
64          writer.endElement(null, "code", "code");
65          writer.characters(new char[] {' ', 'b', 'a', 'r'}, 0, 4);
66          writer.endElement(null, "root", "root");
67          writer.endDocument();
68          writer.close();
69          
70          String xml = out.toString();
71  
72          System.out.println(xml);
73          assertEquals("whitespace problem", -1, xml.indexOf("</code>bar"));
74      }
75      
76      
77  
78      public void testWriter() throws Exception {
79          Object object = document;
80          StringWriter out = new StringWriter();
81  
82          XMLWriter writer = new XMLWriter(out);
83          writer.write(object);
84          writer.close();
85  
86          String text = out.toString();
87  
88          if (VERBOSE) {
89              log("Text output is [");
90              log(text);
91              log("]. Done");
92          }
93  
94          assertTrue("Output text is bigger than 10 characters",
95                  text.length() > 10);
96      }
97  
98      public void testEncodingFormats() throws Exception {
99          testEncoding("UTF-8");
100         testEncoding("UTF-16");
101         testEncoding("ISO-8859-1");
102     }
103 
104     public void testWritingEmptyElement() throws Exception {
105         Document doc = DocumentFactory.getInstance().createDocument();
106         Element grandFather = doc.addElement("grandfather");
107         Element parent1 = grandFather.addElement("parent");
108         Element child1 = parent1.addElement("child1");
109         Element child2 = parent1.addElement("child2");
110         child2.setText("test");
111 
112         Element parent2 = grandFather.addElement("parent");
113         Element child3 = parent2.addElement("child3");
114         child3.setText("test");
115 
116         StringWriter buffer = new StringWriter();
117         OutputFormat format = OutputFormat.createPrettyPrint();
118         XMLWriter writer = new XMLWriter(buffer, format);
119         writer.write(doc);
120 
121         String xml = buffer.toString();
122 
123         System.out.println(xml);
124 
125         assertTrue("child2 not present",
126                 xml.indexOf("<child2>test</child2>") != -1);
127     }
128 
129     protected void testEncoding(String encoding) throws Exception {
130         ByteArrayOutputStream out = new ByteArrayOutputStream();
131 
132         OutputFormat format = OutputFormat.createPrettyPrint();
133         format.setEncoding(encoding);
134 
135         XMLWriter writer = new XMLWriter(out, format);
136         writer.write(document);
137         writer.close();
138 
139         log("Wrote to encoding: " + encoding);
140     }
141 
142     public void testWriterBug() throws Exception {
143         Element project = new BaseElement("project");
144         Document doc = new DefaultDocument(project);
145 
146         ByteArrayOutputStream out = new ByteArrayOutputStream();
147         XMLWriter writer = new XMLWriter(out, new OutputFormat("\t", true,
148                 "ISO-8859-1"));
149         writer.write(doc);
150 
151         ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
152         SAXReader reader = new SAXReader();
153         Document doc2 = reader.read(in);
154 
155         assertTrue("Generated document has a root element", doc2
156                 .getRootElement() != null);
157         assertEquals("Generated document has corrent named root element", doc2
158                 .getRootElement().getName(), "project");
159     }
160 
161     public void testNamespaceBug() throws Exception {
162         Document doc = DocumentHelper.createDocument();
163 
164         Element root = doc.addElement("root", "ns1");
165         Element child1 = root.addElement("joe", "ns2");
166         child1.addElement("zot", "ns1");
167 
168         StringWriter out = new StringWriter();
169         XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
170         writer.write(doc);
171 
172         String text = out.toString();
173 
174         // System.out.println( "Generated:" + text );
175         Document doc2 = DocumentHelper.parseText(text);
176         root = doc2.getRootElement();
177         assertEquals("root has incorrect namespace", "ns1", root
178                 .getNamespaceURI());
179 
180         Element joe = (Element) root.elementIterator().next();
181         assertEquals("joe has correct namespace", "ns2", joe.getNamespaceURI());
182 
183         Element zot = (Element) joe.elementIterator().next();
184         assertEquals("zot has correct namespace", "ns1", zot.getNamespaceURI());
185     }
186 
187     /***
188      * This test harness was supplied by Lari Hotari
189      * 
190      * @throws Exception
191      *             DOCUMENT ME!
192      */
193     public void testContentHandler() throws Exception {
194         StringWriter out = new StringWriter();
195         OutputFormat format = OutputFormat.createPrettyPrint();
196         format.setEncoding("iso-8859-1");
197 
198         XMLWriter writer = new XMLWriter(out, format);
199         generateXML(writer);
200         writer.close();
201 
202         String text = out.toString();
203 
204         if (VERBOSE) {
205             log("Created XML");
206             log(text);
207         }
208 
209         // now lets parse the output and test it with XPath
210         Document doc = DocumentHelper.parseText(text);
211         String value = doc.valueOf("/processes[@name='arvojoo']");
212         assertEquals("Document contains the correct text", "jeejee", value);
213     }
214 
215     /***
216      * This test was provided by Manfred Lotz
217      * 
218      * @throws Exception
219      *             DOCUMENT ME!
220      */
221     public void testWhitespaceBug() throws Exception {
222         String notes = "<notes> This is a      multiline\n\rentry</notes>";
223         Document doc = DocumentHelper.parseText(notes);
224 
225         OutputFormat format = new OutputFormat();
226         format.setEncoding("UTF-8");
227         format.setIndentSize(4);
228         format.setNewlines(true);
229         format.setTrimText(true);
230         format.setExpandEmptyElements(true);
231 
232         StringWriter buffer = new StringWriter();
233         XMLWriter writer = new XMLWriter(buffer, format);
234         writer.write(doc);
235 
236         String xml = buffer.toString();
237         log(xml);
238 
239         Document doc2 = DocumentHelper.parseText(xml);
240         String text = doc2.valueOf("/notes");
241         String expected = "This is a multiline entry";
242 
243         assertEquals("valueOf() returns the correct text padding", expected,
244                 text);
245 
246         assertEquals("getText() returns the correct text padding", expected,
247                 doc2.getRootElement().getText());
248     }
249 
250     /***
251      * This test was provided by Manfred Lotz
252      * 
253      * @throws Exception
254      *             DOCUMENT ME!
255      */
256     public void testWhitespaceBug2() throws Exception {
257         Document doc = DocumentHelper.createDocument();
258         Element root = doc.addElement("root");
259         Element meaning = root.addElement("meaning");
260         meaning.addText("to li");
261         meaning.addText("ve");
262 
263         OutputFormat format = new OutputFormat();
264         format.setEncoding("UTF-8");
265         format.setIndentSize(4);
266         format.setNewlines(true);
267         format.setTrimText(true);
268         format.setExpandEmptyElements(true);
269 
270         StringWriter buffer = new StringWriter();
271         XMLWriter writer = new XMLWriter(buffer, format);
272         writer.write(doc);
273 
274         String xml = buffer.toString();
275         log(xml);
276 
277         Document doc2 = DocumentHelper.parseText(xml);
278         String text = doc2.valueOf("/root/meaning");
279         String expected = "to live";
280 
281         assertEquals("valueOf() returns the correct text padding", expected,
282                 text);
283 
284         assertEquals("getText() returns the correct text padding", expected,
285                 doc2.getRootElement().element("meaning").getText());
286     }
287 
288     public void testPadding() throws Exception {
289         Document doc = DocumentFactory.getInstance().createDocument();
290         Element root = doc.addElement("root");
291         root.addText("prefix    ");
292         root.addElement("b");
293         root.addText("      suffix");
294 
295         OutputFormat format = new OutputFormat("", false);
296         format.setOmitEncoding(true);
297         format.setSuppressDeclaration(true);
298         format.setExpandEmptyElements(true);
299         format.setPadText(true);
300         format.setTrimText(true);
301 
302         StringWriter buffer = new StringWriter();
303         XMLWriter writer = new XMLWriter(buffer, format);
304         writer.write(doc);
305 
306         String xml = buffer.toString();
307 
308         System.out.println("xml: " + xml);
309 
310         String expected = "<root>prefix <b></b> suffix</root>";
311         assertEquals(expected, xml);
312     }
313 
314     public void testPadding2() throws Exception {
315         Document doc = DocumentFactory.getInstance().createDocument();
316         Element root = doc.addElement("root");
317         root.addText("prefix");
318         root.addElement("b");
319         root.addText("suffix");
320 
321         OutputFormat format = new OutputFormat("", false);
322         format.setOmitEncoding(true);
323         format.setSuppressDeclaration(true);
324         format.setExpandEmptyElements(true);
325         format.setPadText(true);
326         format.setTrimText(true);
327 
328         StringWriter buffer = new StringWriter();
329         XMLWriter writer = new XMLWriter(buffer, format);
330         writer.write(doc);
331 
332         String xml = buffer.toString();
333 
334         System.out.println("xml: " + xml);
335 
336         String expected = "<root>prefix<b></b>suffix</root>";
337         assertEquals(expected, xml);
338     }
339 
340     /*
341      * This must be tested manually to see if the layout is correct.
342      */
343     public void testPrettyPrinting() throws Exception {
344         Document doc = DocumentFactory.getInstance().createDocument();
345         doc.addElement("summary").addAttribute("date", "6/7/8").addElement(
346                 "orderline").addText("puffins").addElement("ranjit")
347                 .addComment("Ranjit is a happy Puffin");
348 
349         XMLWriter writer = new XMLWriter(System.out, OutputFormat
350                 .createPrettyPrint());
351         writer.write(doc);
352 
353         doc = DocumentFactory.getInstance().createDocument();
354         doc.addElement("summary").addAttribute("date", "6/7/8").addElement(
355                 "orderline").addText("puffins").addElement("ranjit")
356                 .addComment("Ranjit is a happy Puffin").addComment(
357                         "another comment").addElement("anotherElement");
358         writer.write(doc);
359     }
360 
361     public void testAttributeQuotes() throws Exception {
362         Document doc = DocumentFactory.getInstance().createDocument();
363         doc.addElement("root").addAttribute("test", "text with ' in it");
364 
365         StringWriter out = new StringWriter();
366         XMLWriter writer = new XMLWriter(out, OutputFormat
367                 .createCompactFormat());
368         writer.write(doc);
369 
370         String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
371                 + "<root test=\"text with ' in it\"/>";
372         assertEquals(expected, out.toString());
373     }
374 
375     public void testBug868408() throws Exception {
376         Document doc = getDocument("/xml/web.xml");
377         Document doc2 = DocumentHelper.parseText(doc.asXML());
378         assertEquals(doc.asXML(), doc2.asXML());
379     }
380 
381     public void testBug923882() throws Exception {
382         Document doc = DocumentFactory.getInstance().createDocument();
383         Element root = doc.addElement("root");
384         root.addText("this is ");
385         root.addText(" sim");
386         root.addText("ple text ");
387         root.addElement("child");
388         root.addText(" contai");
389         root.addText("ning spaces and");
390         root.addText(" multiple textnodes");
391 
392         OutputFormat format = new OutputFormat();
393         format.setEncoding("UTF-8");
394         format.setIndentSize(4);
395         format.setNewlines(true);
396         format.setTrimText(true);
397         format.setExpandEmptyElements(true);
398 
399         StringWriter buffer = new StringWriter();
400         XMLWriter writer = new XMLWriter(buffer, format);
401         writer.write(doc);
402 
403         String xml = buffer.toString();
404         log(xml);
405 
406         int start = xml.indexOf("<root");
407         int end = xml.indexOf("/root>") + 6;
408         String eol = "\n"; // System.getProperty("line.separator");
409         String expected = "<root>this is simple text" + eol
410                 + "    <child></child>containing spaces and multiple textnodes"
411                 + eol + "</root>";
412         System.out.println("Expected:");
413         System.out.println(expected);
414         System.out.println("Obtained:");
415         System.out.println(xml.substring(start, end));
416         assertEquals(expected, xml.substring(start, end));
417     }
418 
419     public void testEscapeXML() throws Exception {
420         ByteArrayOutputStream os = new ByteArrayOutputStream();
421         OutputFormat format = new OutputFormat(null, false, "ISO-8859-2");
422         format.setSuppressDeclaration(true);
423 
424         XMLWriter writer = new XMLWriter(os, format);
425 
426         Document document = DocumentFactory.getInstance().createDocument();
427         Element root = document.addElement("root");
428         root.setText("bla &#c bla");
429 
430         writer.write(document);
431 
432         String result = os.toString();
433         System.out.println(result);
434 
435         Document doc2 = DocumentHelper.parseText(result);
436         doc2.normalize(); // merges adjacant Text nodes
437         System.out.println(doc2.getRootElement().getText());
438         assertNodesEqual(document, doc2);
439     }
440 
441     public void testWriteEntities() throws Exception {
442         String xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
443                 + "<!DOCTYPE xml [<!ENTITY copy \"&#169;\"> "
444                 + "<!ENTITY trade \"&#8482;\"> "
445                 + "<!ENTITY deg \"&#x00b0;\"> " + "<!ENTITY gt \"&#62;\"> "
446                 + "<!ENTITY sup2 \"&#x00b2;\"> "
447                 + "<!ENTITY frac14 \"&#x00bc;\"> "
448                 + "<!ENTITY quot \"&#34;\"> "
449                 + "<!ENTITY frac12 \"&#x00bd;\"> "
450                 + "<!ENTITY euro \"&#x20ac;\"> "
451                 + "<!ENTITY Omega \"&#937;\"> ]>\n" + "<root />";
452 
453         SAXReader reader = new SAXReader("org.apache.xerces.parsers.SAXParser");
454         reader.setIncludeInternalDTDDeclarations(true);
455 
456         Document doc = reader.read(new StringReader(xml));
457         StringWriter wr = new StringWriter();
458         XMLWriter writer = new XMLWriter(wr);
459         writer.write(doc);
460 
461         String xml2 = wr.toString();
462         System.out.println(xml2);
463 
464         Document doc2 = DocumentHelper.parseText(xml2);
465 
466         assertNodesEqual(doc, doc2);
467     }
468 
469     public void testEscapeChars() throws Exception {
470         Document document = DocumentFactory.getInstance().createDocument();
471         Element root = document.addElement("root");
472         root.setText("blahblah " + '\u008f');
473 
474         XMLWriter writer = new XMLWriter();
475         StringWriter strWriter = new StringWriter();
476         writer.setWriter(strWriter);
477         writer.setMaximumAllowedCharacter(127);
478         writer.write(document);
479 
480         String xml = strWriter.toString();
481     }
482 
483     public void testEscapeText() throws SAXException {
484         StringWriter writer = new StringWriter();
485         XMLWriter xmlWriter = new XMLWriter(writer);
486         xmlWriter.setEscapeText(false);
487 
488         String txt = "<test></test>";
489 
490         xmlWriter.startDocument();
491         xmlWriter.characters(txt.toCharArray(), 0, txt.length());
492         xmlWriter.endDocument();
493 
494         String output = writer.toString();
495         System.out.println(output);
496         assertTrue(output.indexOf("<test>") != -1);
497     }
498 
499     public void testNullCData() {
500         Element e = DocumentHelper.createElement("test");
501         e.add(DocumentHelper.createElement("another").addCDATA(null));
502 
503         Document doc = DocumentHelper.createDocument(e);
504 
505         assertEquals(-1, e.asXML().indexOf("null"));
506         assertEquals(-1, doc.asXML().indexOf("null"));
507 
508         System.out.println(e.asXML());
509         System.out.println(doc.asXML());
510     }
511 
512     protected void generateXML(ContentHandler handler) throws SAXException {
513         handler.startDocument();
514 
515         AttributesImpl attrs = new AttributesImpl();
516         attrs.clear();
517         attrs.addAttribute("", "", "name", "CDATA", "arvojoo");
518         handler.startElement("", "", "processes", attrs);
519 
520         String text = "jeejee";
521         char[] textch = text.toCharArray();
522         handler.characters(textch, 0, textch.length);
523         handler.endElement("", "", "processes");
524         handler.endDocument();
525     }
526 }
527 
528 /*
529  * Redistribution and use of this software and associated documentation
530  * ("Software"), with or without modification, are permitted provided that the
531  * following conditions are met:
532  * 
533  * 1. Redistributions of source code must retain copyright statements and
534  * notices. Redistributions must also contain a copy of this document.
535  * 
536  * 2. Redistributions in binary form must reproduce the above copyright notice,
537  * this list of conditions and the following disclaimer in the documentation
538  * and/or other materials provided with the distribution.
539  * 
540  * 3. The name "DOM4J" must not be used to endorse or promote products derived
541  * from this Software without prior written permission of MetaStuff, Ltd. For
542  * written permission, please contact dom4j-info@metastuff.com.
543  * 
544  * 4. Products derived from this Software may not be called "DOM4J" nor may
545  * "DOM4J" appear in their names without prior written permission of MetaStuff,
546  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
547  * 
548  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
549  * 
550  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
551  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
552  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
553  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
554  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
555  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
556  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
557  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
558  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
559  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
560  * POSSIBILITY OF SUCH DAMAGE.
561  * 
562  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
563  */