1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package common.prettyprint; 25 26 import static org.testng.Assert.assertEquals; 27 import static org.testng.Assert.assertTrue; 28 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.StringReader; 32 import java.io.StringWriter; 33 34 import javax.xml.parsers.DocumentBuilder; 35 import javax.xml.parsers.DocumentBuilderFactory; 36 import javax.xml.parsers.ParserConfigurationException; 37 import javax.xml.transform.OutputKeys; 38 import javax.xml.transform.Transformer; 39 import javax.xml.transform.TransformerFactory; 40 import javax.xml.transform.dom.DOMSource; 41 import javax.xml.transform.stream.StreamResult; 42 import javax.xml.transform.stream.StreamSource; 43 44 import org.testng.Assert; 45 import org.testng.annotations.DataProvider; 46 import org.testng.annotations.Listeners; 47 import org.testng.annotations.Test; 48 import org.w3c.dom.DOMConfiguration; 49 import org.w3c.dom.DOMImplementation; 50 import org.w3c.dom.Document; 51 import org.w3c.dom.Node; 52 import org.w3c.dom.Text; 53 import org.w3c.dom.bootstrap.DOMImplementationRegistry; 54 import org.w3c.dom.ls.DOMImplementationLS; 55 import org.w3c.dom.ls.LSOutput; 56 import org.w3c.dom.ls.LSSerializer; 57 import org.xml.sax.InputSource; 58 import org.xml.sax.SAXException; 59 60 61 /* 62 * @test 63 * @bug 6439439 8087303 64 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest 65 * @run testng/othervm -DrunSecMngr=true common.prettyprint.PrettyPrintTest 66 * @run testng/othervm common.prettyprint.PrettyPrintTest 67 * @summary Test serializing xml and html with indentation. 68 */ 69 @Listeners({jaxp.library.FilePolicy.class}) 70 public class PrettyPrintTest { 71 /* 72 * test CDATA, elements only, text and element, whitespace and element, 73 * xml:space property and nested xml:space property, mixed node types. 74 */ 75 @DataProvider(name = "xml-data") 76 public Object[][] xmlData() throws Exception { 77 return new Object[][] { 78 { read("xmltest1.xml"), read("xmltest1.out") }, 79 { read("xmltest2.xml"), read("xmltest2.out") }, 80 { read("xmltest3.xml"), read("xmltest3.out") }, 81 { read("xmltest4.xml"), read("xmltest4.out") }, 82 { read("xmltest5.xml"), read("xmltest5.out") }, 83 { read("xmltest6.xml"), read("xmltest6.out") }, 84 { read("xmltest7.xml"), read("xmltest7.out") }, 85 { read("xmltest8.xml"), read("xmltest8.out") } }; 86 } 87 88 /* 89 * @bug 8087303 90 * Test the whitespace text nodes are serialized with pretty-print by LSSerializer and transformer correctly 91 * 92 */ 93 @Test(dataProvider = "xml-data") 94 public void testXMLPrettyPrint(String source, String expected) throws Exception { 95 // test it's no change if no pretty-print 96 String result = serializerWrite(toXmlDocument(source), false); 97 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 98 // test pretty-print 99 assertEquals(serializerWrite(toXmlDocument(source), true), expected); 100 // test it's no change if no pretty-print 101 result = transform(toXmlDocument(source), false); 102 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 103 // test pretty-print 104 assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected); 105 } 106 107 /* 108 * test pure text content, and sequent Text nodes. 109 */ 110 @DataProvider(name = "xml-node-data") 111 public Object[][] xmlNodeData() throws Exception { 112 return new Object[][] { 113 { newTextNode(read("nodetest1.txt")), read("nodetest1.out") }, 114 { createDocWithSequentTextNodes(), read("nodetest2.out") } }; 115 } 116 117 /* 118 * @bug 8087303 119 * Test the whitespace text nodes are serialized with pretty-print by LSSerializer and transformer correctly, 120 * doesn't compare with the source because the test data is Node object 121 * 122 */ 123 @Test(dataProvider = "xml-node-data") 124 public void testXMLNodePrettyPrint(Node xml, String expected) throws Exception { 125 assertEquals(serializerWrite(xml, true), expected); 126 assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected); 127 } 128 129 /* 130 * test block element, inline element, text, and mixed elements. 131 */ 132 @DataProvider(name = "html-data") 133 public Object[][] htmlData() throws Exception { 134 return new Object[][] { 135 { read("htmltest1.xml"), read("htmltest1.out") }, 136 { read("htmltest2.xml"), read("htmltest2.out") }, 137 { read("htmltest3.xml"), read("htmltest3.out") }, 138 { read("htmltest4.xml"), read("htmltest4.out") }, 139 { read("htmltest5.xml"), read("htmltest5.out") }, 140 { read("htmltest6.xml"), read("htmltest6.out") } }; 141 } 142 143 /* 144 * @bug 8087303 145 * Transform to HTML, test Pretty Print for HTML. 146 * 147 */ 148 @Test(dataProvider = "html-data") 149 public void testTransformToHTML(String source, String expected) throws Exception { 150 // test it's no change if no pretty-print 151 StringWriter writer = new StringWriter(); 152 getTransformer(true, false).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); 153 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(writer.toString())), "The actual is: " + writer.toString()); 154 155 // test pretty-print 156 writer = new StringWriter(); 157 getTransformer(true, true).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); 158 assertEquals(writer.toString().replaceAll("\r\n", "\n"), expected); 159 } 160 161 @Test 162 public void testLSSerializerFormatPrettyPrint() { 163 164 final String XML_DOCUMENT = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n" 165 + "<hello>before child element<child><children/><children/></child>after child element</hello>"; 166 /**JDK-8035467 167 * no newline in default output 168 */ 169 final String XML_DOCUMENT_DEFAULT_PRINT = 170 "<?xml version=\"1.0\" encoding=\"UTF-16\"?>" 171 + "<hello>" 172 + "before child element" 173 + "<child><children/><children/></child>" 174 + "after child element</hello>"; 175 176 final String XML_DOCUMENT_PRETTY_PRINT = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><hello>\n" + 177 " before child element\n" + 178 " <child>\n" + 179 " <children/>\n" + 180 " <children/>\n" + 181 " </child>\n" + 182 " after child element\n" + 183 "</hello>\n"; 184 185 // it all begins with a Document 186 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 187 DocumentBuilder documentBuilder = null; 188 try { 189 documentBuilder = documentBuilderFactory.newDocumentBuilder(); 190 } catch (ParserConfigurationException parserConfigurationException) { 191 parserConfigurationException.printStackTrace(); 192 Assert.fail(parserConfigurationException.toString()); 193 } 194 Document document = null; 195 196 StringReader stringReader = new StringReader(XML_DOCUMENT); 197 InputSource inputSource = new InputSource(stringReader); 198 try { 199 document = documentBuilder.parse(inputSource); 200 } catch (SAXException saxException) { 201 saxException.printStackTrace(); 202 Assert.fail(saxException.toString()); 203 } catch (IOException ioException) { 204 ioException.printStackTrace(); 205 Assert.fail(ioException.toString()); 206 } 207 208 // query DOM Interfaces to get to a LSSerializer 209 DOMImplementation domImplementation = documentBuilder.getDOMImplementation(); 210 DOMImplementationLS domImplementationLS = (DOMImplementationLS) domImplementation; 211 LSSerializer lsSerializer = domImplementationLS.createLSSerializer(); 212 213 System.out.println("Serializer is: " + lsSerializer.getClass().getName() + " " + lsSerializer); 214 215 // get configuration 216 DOMConfiguration domConfiguration = lsSerializer.getDomConfig(); 217 218 // query current configuration 219 Boolean defaultFormatPrettyPrint = (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT); 220 Boolean canSetFormatPrettyPrintFalse = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); 221 Boolean canSetFormatPrettyPrintTrue = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); 222 223 System.out.println(DOM_FORMAT_PRETTY_PRINT + " default/can set false/can set true = " + defaultFormatPrettyPrint + "/" 224 + canSetFormatPrettyPrintFalse + "/" + canSetFormatPrettyPrintTrue); 225 226 // test values 227 assertEquals(defaultFormatPrettyPrint, Boolean.FALSE, "Default value of " + DOM_FORMAT_PRETTY_PRINT + " should be " + Boolean.FALSE); 228 229 assertEquals(canSetFormatPrettyPrintFalse, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.FALSE + " should be " 230 + Boolean.TRUE); 231 232 assertEquals(canSetFormatPrettyPrintTrue, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.TRUE + " should be " 233 + Boolean.TRUE); 234 235 // get default serialization 236 String prettyPrintDefault = lsSerializer.writeToString(document); 237 System.out.println("(default) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 238 + ": \n\"" + prettyPrintDefault + "\""); 239 240 assertEquals(prettyPrintDefault, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with default value, " + DOM_FORMAT_PRETTY_PRINT + "==" 241 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 242 243 // configure LSSerializer to not format-pretty-print 244 domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); 245 String prettyPrintFalse = lsSerializer.writeToString(document); 246 System.out.println("(FALSE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 247 + ": \n\"" + prettyPrintFalse + "\""); 248 249 assertEquals(prettyPrintFalse, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with FALSE value, " + DOM_FORMAT_PRETTY_PRINT + "==" 250 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 251 252 // configure LSSerializer to format-pretty-print 253 domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); 254 String prettyPrintTrue = lsSerializer.writeToString(document); 255 System.out.println("(TRUE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 256 + ": \n\"" + prettyPrintTrue + "\""); 257 258 assertEquals(prettyPrintTrue, XML_DOCUMENT_PRETTY_PRINT, "Invalid serialization with TRUE value, " + DOM_FORMAT_PRETTY_PRINT + "==" 259 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 260 } 261 262 private String serializerWrite(Node xml, boolean pretty) throws Exception { 263 DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); 264 DOMImplementationLS domImplementation = (DOMImplementationLS) registry.getDOMImplementation("LS"); 265 StringWriter writer = new StringWriter(); 266 LSOutput formattedOutput = domImplementation.createLSOutput(); 267 formattedOutput.setCharacterStream(writer); 268 LSSerializer domSerializer = domImplementation.createLSSerializer(); 269 domSerializer.getDomConfig().setParameter(DOM_FORMAT_PRETTY_PRINT, pretty); 270 domSerializer.getDomConfig().setParameter("xml-declaration", false); 271 domSerializer.write(xml, formattedOutput); 272 return writer.toString(); 273 } 274 275 private String transform(Node xml, boolean pretty) throws Exception { 276 Transformer transformer = getTransformer(false, pretty); 277 StringWriter writer = new StringWriter(); 278 transformer.transform(new DOMSource(xml), new StreamResult(writer)); 279 return writer.toString(); 280 } 281 282 private Document toXmlDocument(String xmlString) throws Exception { 283 InputSource xmlInputSource = new InputSource(new StringReader(xmlString)); 284 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 285 dbf.setValidating(true); 286 DocumentBuilder xmlDocumentBuilder = dbf.newDocumentBuilder(); 287 Document node = xmlDocumentBuilder.parse(xmlInputSource); 288 return node; 289 } 290 291 private Text newTextNode(String text) throws Exception { 292 DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 293 return db.newDocument().createTextNode(text); 294 } 295 296 private Document createDocWithSequentTextNodes() throws Exception { 297 DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 298 Document doc = db.newDocument(); 299 Node root = doc.createElement("root"); 300 doc.appendChild(root); 301 root.appendChild(doc.createTextNode(" ")); 302 root.appendChild(doc.createTextNode("t")); 303 root.appendChild(doc.createTextNode("\n")); 304 root.appendChild(doc.createTextNode("t")); 305 root.appendChild(doc.createTextNode(" ")); 306 Node child1 = doc.createElement("child1"); 307 root.appendChild(child1); 308 child1.appendChild(doc.createTextNode(" ")); 309 child1.appendChild(doc.createTextNode("\n")); 310 root.appendChild(doc.createTextNode("t")); 311 Node child2 = doc.createElement("child2"); 312 root.appendChild(child2); 313 child2.appendChild(doc.createTextNode(" ")); 314 root.appendChild(doc.createTextNode(" ")); 315 Node child3 = doc.createElement("child3"); 316 root.appendChild(child3); 317 child3.appendChild(doc.createTextNode(" ")); 318 root.appendChild(doc.createTextNode(" ")); 319 Node child4 = doc.createElement("child4"); 320 root.appendChild(child4); 321 child4.appendChild(doc.createTextNode(" ")); 322 323 root.appendChild(doc.createTextNode(" ")); 324 Node child5 = doc.createElement("child5"); 325 root.appendChild(child5); 326 child5.appendChild(doc.createTextNode("t")); 327 328 Node child51 = doc.createElement("child51"); 329 child5.appendChild(child51); 330 child51.appendChild(doc.createTextNode(" ")); 331 Node child511 = doc.createElement("child511"); 332 child51.appendChild(child511); 333 child511.appendChild(doc.createTextNode("t")); 334 child51.appendChild(doc.createTextNode(" ")); 335 child5.appendChild(doc.createTextNode("t")); 336 337 root.appendChild(doc.createTextNode(" ")); 338 root.appendChild(doc.createComment(" test comment ")); 339 root.appendChild(doc.createTextNode(" \n")); 340 root.appendChild(doc.createComment(" ")); 341 root.appendChild(doc.createTextNode("\n")); 342 root.appendChild(doc.createProcessingInstruction("target1", "test")); 343 root.appendChild(doc.createTextNode(" ")); 344 root.appendChild(doc.createTextNode(" ")); 345 return doc; 346 } 347 348 private Transformer getTransformer(boolean html, boolean pretty) throws Exception { 349 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 350 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 351 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 352 if (html) 353 transformer.setOutputProperty(OutputKeys.METHOD, "html"); 354 transformer.setOutputProperty(OutputKeys.INDENT, pretty ? "yes" : "no"); 355 return transformer; 356 } 357 358 359 private String read(String filename) throws Exception { 360 try (InputStream in = PrettyPrintTest.class.getResourceAsStream(filename)) { 361 return new String(in.readAllBytes()); 362 } 363 } 364 365 private static final String DOM_FORMAT_PRETTY_PRINT = "format-pretty-print"; 366 }