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 8174025 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, xml:space property, mixed 73 * node types. 74 */ 75 @DataProvider(name = "xml-data") 76 public Object[][] xmlData() throws Exception { 77 return new Object[][] { 78 { "xmltest1.xml", "xmltest1.out" }, 79 { "xmltest2.xml", "xmltest2.out" }, 80 { "xmltest3.xml", "xmltest3.out" }, 81 { "xmltest4.xml", "xmltest4.out" }, 82 { "xmltest6.xml", "xmltest6.out" }, 83 { "xmltest8.xml", "xmltest8.out" } }; 84 } 85 86 /* 87 * @bug 8087303 88 * Test the xml document are serialized with pretty-print by 89 * LSSerializer and transformer correctly 90 * 91 */ 92 @Test(dataProvider = "xml-data") 93 public void testXMLPrettyPrint(String sourceFile, String expectedFile) throws Exception { 94 String source = read(sourceFile); 95 String expected = read(expectedFile); 96 // test it's no change if no pretty-print 97 String result = serializerWrite(toXmlDocument(source), false); 98 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 99 // test pretty-print 100 assertEquals(serializerWrite(toXmlDocument(source), true), expected); 101 // test it's no change if no pretty-print 102 result = transform(toXmlDocument(source), false); 103 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 104 // test pretty-print 105 assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected); 106 } 107 108 109 /* 110 * @bug 8087303 111 * Test a single text node is serialized with pretty-print by 112 * LSSerializer and transformer correctly 113 * 114 */ 115 @Test 116 public void testSingleTextNode() throws Exception { 117 Node xml = newTextNode(read("nodetest1.txt")); 118 String expected = read("nodetest1.out"); 119 assertEquals(serializerWrite(xml, true), expected); 120 assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected); 121 } 122 123 /* 124 * @bug 8087303 125 * Test the transformer shall keep all whitespace text node in 126 * sequent text nodes 127 * 128 */ 129 @Test 130 public void testSequentTextNodesWithTransformer() throws Exception { 131 Node xml = createDocWithSequentTextNodes(); 132 String expected = read("nodetest2.out"); 133 assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected); 134 } 135 136 /* 137 * @bug 8087303 138 * Test LSSerializer shall eliminate the whitespace text node 139 * in sequent text nodes 140 * 141 */ 142 @Test 143 public void testSequentTextNodesWithLSSerializer() throws Exception { 144 Node xml = createDocWithSequentTextNodes(); 145 String expected = read("nodetest2ls.out"); 146 assertEquals(serializerWrite(xml, true), expected); 147 } 148 149 150 /* 151 * test whitespace and element, nested xml:space property. 152 */ 153 @DataProvider(name = "xml-data-whitespace-ls") 154 public Object[][] whitespaceLS() throws Exception { 155 return new Object[][] { 156 { "xmltest5.xml", "xmltest5ls.out" }, 157 { "xmltest7.xml", "xmltest7ls.out" } }; 158 } 159 160 /* 161 * @bug 8087303 162 * Test LSSerializer shall eliminate the whitespace text node 163 * unless xml:space="preserve" 164 * 165 */ 166 @Test(dataProvider = "xml-data-whitespace-ls") 167 public void testWhitespaceWithLSSerializer(String sourceFile, String expectedFile) throws Exception { 168 String source = read(sourceFile); 169 String expected = read(expectedFile); 170 // test it's no change if no pretty-print 171 String result = serializerWrite(toXmlDocument(source), false); 172 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 173 // test pretty-print 174 assertEquals(serializerWrite(toXmlDocument(source), true), expected); 175 } 176 177 /* 178 * test whitespace and element, nested xml:space property. 179 */ 180 @DataProvider(name = "xml-data-whitespace-xslt") 181 public Object[][] whitespaceXSLT() throws Exception { 182 return new Object[][] { 183 { "xmltest5.xml", "xmltest5xslt.out" }, 184 { "xmltest7.xml", "xmltest7xslt.out" } }; 185 } 186 187 /* 188 * @bug 8087303 189 * Test the transformer shall format the output but keep all 190 * whitespace text node even if xml:space="preserve" 191 * 192 */ 193 @Test(dataProvider = "xml-data-whitespace-xslt") 194 public void testWhitespaceWithTransformer(String sourceFile, String expectedFile) throws Exception { 195 String source = read(sourceFile); 196 String expected = read(expectedFile); 197 // test it's no change if no pretty-print 198 String result = transform(toXmlDocument(source), false); 199 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); 200 // test pretty-print 201 assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected); 202 } 203 204 /* 205 * test block element, inline element, text, and mixed elements. 206 */ 207 @DataProvider(name = "html-data") 208 public Object[][] htmlData() throws Exception { 209 return new Object[][] { 210 { "htmltest1.xml", "htmltest1.out" }, 211 { "htmltest2.xml", "htmltest2.out" }, 212 { "htmltest3.xml", "htmltest3.out" }, 213 { "htmltest4.xml", "htmltest4.out" }, 214 { "htmltest5.xml", "htmltest5.out" }, 215 { "htmltest6.xml", "htmltest6.out" }, 216 /* @bug 8174025, test whitespace between inline elements */ 217 { "htmltest7.xml", "htmltest7.out" } }; 218 } 219 220 /* 221 * @bug 8087303 222 * Transform to HTML, test Pretty Print for HTML. 223 * 224 */ 225 @Test(dataProvider = "html-data") 226 public void testTransformToHTML(String sourceFile, String expectedFile) throws Exception { 227 String source = read(sourceFile); 228 String expected = read(expectedFile); 229 // test it's no change if no pretty-print 230 StringWriter writer = new StringWriter(); 231 getTransformer(true, false).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); 232 assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(writer.toString())), "The actual is: " + writer.toString()); 233 234 // test pretty-print 235 writer = new StringWriter(); 236 getTransformer(true, true).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); 237 assertEquals(writer.toString().replaceAll("\r\n", "\n"), expected); 238 } 239 240 /* 241 * @bug 8174025 242 * Test the serializer can handle <xsl:text disable-output-escaping="yes"> correctly. 243 * 244 */ 245 @Test 246 public void testDisableOutputEscaping() throws Exception { 247 final String xsl ="generate-catalog.xsl"; 248 final String xml ="simple-entity-resolver-config.xml"; 249 final String expectedOutput ="simple-entity-resolver-config-transformed.xml"; 250 TransformerFactory factory = TransformerFactory.newInstance(); 251 Transformer transformer = factory.newTemplates(new StreamSource(new StringReader(read(xsl)))).newTransformer(); 252 253 String key = "schemaBase"; 254 String value = "schemas"; 255 transformer.setParameter(key, value); 256 StringWriter writer = new StringWriter(); 257 transformer.transform(new StreamSource(new StringReader(read(xml))), new StreamResult(writer)); 258 assertEquals(writer.toString().replaceAll("\r\n", "\n"), read(expectedOutput)); 259 } 260 261 @Test 262 public void testLSSerializerFormatPrettyPrint() { 263 264 final String XML_DOCUMENT = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n" 265 + "<hello>before child element<child><children/><children/></child>after child element</hello>"; 266 /**JDK-8035467 267 * no newline in default output 268 */ 269 final String XML_DOCUMENT_DEFAULT_PRINT = 270 "<?xml version=\"1.0\" encoding=\"UTF-16\"?>" 271 + "<hello>" 272 + "before child element" 273 + "<child><children/><children/></child>" 274 + "after child element</hello>"; 275 276 final String XML_DOCUMENT_PRETTY_PRINT = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><hello>\n" + 277 " before child element\n" + 278 " <child>\n" + 279 " <children/>\n" + 280 " <children/>\n" + 281 " </child>\n" + 282 " after child element\n" + 283 "</hello>\n"; 284 285 // it all begins with a Document 286 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 287 DocumentBuilder documentBuilder = null; 288 try { 289 documentBuilder = documentBuilderFactory.newDocumentBuilder(); 290 } catch (ParserConfigurationException parserConfigurationException) { 291 parserConfigurationException.printStackTrace(); 292 Assert.fail(parserConfigurationException.toString()); 293 } 294 Document document = null; 295 296 StringReader stringReader = new StringReader(XML_DOCUMENT); 297 InputSource inputSource = new InputSource(stringReader); 298 try { 299 document = documentBuilder.parse(inputSource); 300 } catch (SAXException saxException) { 301 saxException.printStackTrace(); 302 Assert.fail(saxException.toString()); 303 } catch (IOException ioException) { 304 ioException.printStackTrace(); 305 Assert.fail(ioException.toString()); 306 } 307 308 // query DOM Interfaces to get to a LSSerializer 309 DOMImplementation domImplementation = documentBuilder.getDOMImplementation(); 310 DOMImplementationLS domImplementationLS = (DOMImplementationLS) domImplementation; 311 LSSerializer lsSerializer = domImplementationLS.createLSSerializer(); 312 313 System.out.println("Serializer is: " + lsSerializer.getClass().getName() + " " + lsSerializer); 314 315 // get configuration 316 DOMConfiguration domConfiguration = lsSerializer.getDomConfig(); 317 318 // query current configuration 319 Boolean defaultFormatPrettyPrint = (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT); 320 Boolean canSetFormatPrettyPrintFalse = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); 321 Boolean canSetFormatPrettyPrintTrue = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); 322 323 System.out.println(DOM_FORMAT_PRETTY_PRINT + " default/can set false/can set true = " + defaultFormatPrettyPrint + "/" 324 + canSetFormatPrettyPrintFalse + "/" + canSetFormatPrettyPrintTrue); 325 326 // test values 327 assertEquals(defaultFormatPrettyPrint, Boolean.FALSE, "Default value of " + DOM_FORMAT_PRETTY_PRINT + " should be " + Boolean.FALSE); 328 329 assertEquals(canSetFormatPrettyPrintFalse, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.FALSE + " should be " 330 + Boolean.TRUE); 331 332 assertEquals(canSetFormatPrettyPrintTrue, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.TRUE + " should be " 333 + Boolean.TRUE); 334 335 // get default serialization 336 String prettyPrintDefault = lsSerializer.writeToString(document); 337 System.out.println("(default) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 338 + ": \n\"" + prettyPrintDefault + "\""); 339 340 assertEquals(prettyPrintDefault, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with default value, " + DOM_FORMAT_PRETTY_PRINT + "==" 341 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 342 343 // configure LSSerializer to not format-pretty-print 344 domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); 345 String prettyPrintFalse = lsSerializer.writeToString(document); 346 System.out.println("(FALSE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 347 + ": \n\"" + prettyPrintFalse + "\""); 348 349 assertEquals(prettyPrintFalse, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with FALSE value, " + DOM_FORMAT_PRETTY_PRINT + "==" 350 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 351 352 // configure LSSerializer to format-pretty-print 353 domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); 354 String prettyPrintTrue = lsSerializer.writeToString(document); 355 System.out.println("(TRUE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) 356 + ": \n\"" + prettyPrintTrue + "\""); 357 358 assertEquals(prettyPrintTrue, XML_DOCUMENT_PRETTY_PRINT, "Invalid serialization with TRUE value, " + DOM_FORMAT_PRETTY_PRINT + "==" 359 + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); 360 } 361 362 private String serializerWrite(Node xml, boolean pretty) throws Exception { 363 DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); 364 DOMImplementationLS domImplementation = (DOMImplementationLS) registry.getDOMImplementation("LS"); 365 StringWriter writer = new StringWriter(); 366 LSOutput formattedOutput = domImplementation.createLSOutput(); 367 formattedOutput.setCharacterStream(writer); 368 LSSerializer domSerializer = domImplementation.createLSSerializer(); 369 domSerializer.getDomConfig().setParameter(DOM_FORMAT_PRETTY_PRINT, pretty); 370 domSerializer.getDomConfig().setParameter("xml-declaration", false); 371 domSerializer.write(xml, formattedOutput); 372 return writer.toString(); 373 } 374 375 private String transform(Node xml, boolean pretty) throws Exception { 376 Transformer transformer = getTransformer(false, pretty); 377 StringWriter writer = new StringWriter(); 378 transformer.transform(new DOMSource(xml), new StreamResult(writer)); 379 return writer.toString(); 380 } 381 382 private Document toXmlDocument(String xmlString) throws Exception { 383 InputSource xmlInputSource = new InputSource(new StringReader(xmlString)); 384 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 385 dbf.setValidating(true); 386 DocumentBuilder xmlDocumentBuilder = dbf.newDocumentBuilder(); 387 Document node = xmlDocumentBuilder.parse(xmlInputSource); 388 return node; 389 } 390 391 private Text newTextNode(String text) throws Exception { 392 DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 393 return db.newDocument().createTextNode(text); 394 } 395 396 private Document createDocWithSequentTextNodes() throws Exception { 397 DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 398 Document doc = db.newDocument(); 399 Node root = doc.createElement("root"); 400 doc.appendChild(root); 401 root.appendChild(doc.createTextNode("\n")); 402 root.appendChild(doc.createTextNode("\n")); 403 root.appendChild(doc.createTextNode("\n")); 404 root.appendChild(doc.createTextNode(" ")); 405 root.appendChild(doc.createTextNode("t")); 406 root.appendChild(doc.createTextNode("\n")); 407 root.appendChild(doc.createTextNode("t")); 408 root.appendChild(doc.createTextNode(" ")); 409 Node child1 = doc.createElement("child1"); 410 root.appendChild(child1); 411 child1.appendChild(doc.createTextNode(" ")); 412 child1.appendChild(doc.createTextNode("\n")); 413 root.appendChild(doc.createTextNode("t")); 414 Node child2 = doc.createElement("child2"); 415 root.appendChild(child2); 416 child2.appendChild(doc.createTextNode(" ")); 417 root.appendChild(doc.createTextNode(" ")); 418 Node child3 = doc.createElement("child3"); 419 root.appendChild(child3); 420 child3.appendChild(doc.createTextNode(" ")); 421 root.appendChild(doc.createTextNode(" ")); 422 Node child4 = doc.createElement("child4"); 423 root.appendChild(child4); 424 child4.appendChild(doc.createTextNode(" ")); 425 426 root.appendChild(doc.createTextNode(" ")); 427 Node child5 = doc.createElement("child5"); 428 root.appendChild(child5); 429 child5.appendChild(doc.createTextNode("t")); 430 431 Node child51 = doc.createElement("child51"); 432 child5.appendChild(child51); 433 child51.appendChild(doc.createTextNode(" ")); 434 Node child511 = doc.createElement("child511"); 435 child51.appendChild(child511); 436 child511.appendChild(doc.createTextNode("t")); 437 child51.appendChild(doc.createTextNode(" ")); 438 child5.appendChild(doc.createTextNode("t")); 439 440 root.appendChild(doc.createTextNode(" ")); 441 root.appendChild(doc.createComment(" test comment ")); 442 root.appendChild(doc.createTextNode(" \n")); 443 root.appendChild(doc.createComment(" ")); 444 root.appendChild(doc.createTextNode("\n")); 445 root.appendChild(doc.createProcessingInstruction("target1", "test")); 446 root.appendChild(doc.createTextNode(" ")); 447 root.appendChild(doc.createTextNode(" ")); 448 return doc; 449 } 450 451 private Transformer getTransformer(boolean html, boolean pretty) throws Exception { 452 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 453 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 454 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 455 if (html) 456 transformer.setOutputProperty(OutputKeys.METHOD, "html"); 457 transformer.setOutputProperty(OutputKeys.INDENT, pretty ? "yes" : "no"); 458 return transformer; 459 } 460 461 462 private String read(String filename) throws Exception { 463 try (InputStream in = PrettyPrintTest.class.getResourceAsStream(filename)) { 464 return new String(in.readAllBytes()); 465 } 466 } 467 468 private static final String DOM_FORMAT_PRETTY_PRINT = "format-pretty-print"; 469 }