/* * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package common.prettyprint; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /* * @test * @bug 6439439 8087303 8174025 8223291 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm -DrunSecMngr=true common.prettyprint.PrettyPrintTest * @run testng/othervm common.prettyprint.PrettyPrintTest * @summary Test serializing xml and html with indentation. */ @Listeners({jaxp.library.FilePolicy.class}) public class PrettyPrintTest { /* * test CDATA, elements only, text and element, xml:space property, mixed * node types. */ @DataProvider(name = "xml-data") public Object[][] xmlData() throws Exception { return new Object[][] { { "xmltest1.xml", "xmltest1.out" }, { "xmltest2.xml", "xmltest2.out" }, { "xmltest3.xml", "xmltest3.out" }, { "xmltest4.xml", "xmltest4.out" }, { "xmltest6.xml", "xmltest6.out" }, { "xmltest8.xml", "xmltest8.out" } }; } /* * @bug 8087303 * Test the xml document are serialized with pretty-print by * LSSerializer and transformer correctly * */ @Test(dataProvider = "xml-data") public void testXMLPrettyPrint(String sourceFile, String expectedFile) throws Exception { String source = read(sourceFile); String expected = read(expectedFile); // test it's no change if no pretty-print String result = serializerWrite(toXmlDocument(source), false); assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); // test pretty-print assertEquals(serializerWrite(toXmlDocument(source), true), expected); // test it's no change if no pretty-print result = transform(toXmlDocument(source), false); assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); // test pretty-print assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected); } /* * @bug 8087303 * Test a single text node is serialized with pretty-print by * LSSerializer and transformer correctly * */ @Test public void testSingleTextNode() throws Exception { Node xml = newTextNode(read("nodetest1.txt")); String expected = read("nodetest1.out"); assertEquals(serializerWrite(xml, true), expected); assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected); } /* * @bug 8087303 * Test the transformer shall keep all whitespace text node in * sequent text nodes * */ @Test public void testSequentTextNodesWithTransformer() throws Exception { Node xml = createDocWithSequentTextNodes(); String expected = read("nodetest2.out"); assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected); } /* * @bug 8087303 * Test LSSerializer shall eliminate the whitespace text node * in sequent text nodes * */ @Test public void testSequentTextNodesWithLSSerializer() throws Exception { Node xml = createDocWithSequentTextNodes(); String expected = read("nodetest2ls.out"); assertEquals(serializerWrite(xml, true), expected); } /* * test whitespace and element, nested xml:space property. */ @DataProvider(name = "xml-data-whitespace-ls") public Object[][] whitespaceLS() throws Exception { return new Object[][] { { "xmltest5.xml", "xmltest5ls.out" }, { "xmltest7.xml", "xmltest7ls.out" } }; } /* * @bug 8087303 * Test LSSerializer shall eliminate the whitespace text node * unless xml:space="preserve" * */ @Test(dataProvider = "xml-data-whitespace-ls") public void testWhitespaceWithLSSerializer(String sourceFile, String expectedFile) throws Exception { String source = read(sourceFile); String expected = read(expectedFile); // test it's no change if no pretty-print String result = serializerWrite(toXmlDocument(source), false); assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); // test pretty-print assertEquals(serializerWrite(toXmlDocument(source), true), expected); } /* * test whitespace and element, nested xml:space property. */ @DataProvider(name = "xml-data-whitespace-xslt") public Object[][] whitespaceXSLT() throws Exception { return new Object[][] { { "xmltest5.xml", "xmltest5xslt.out" }, { "xmltest7.xml", "xmltest7xslt.out" } }; } /* * @bug 8087303 * Test the transformer shall format the output but keep all * whitespace text node even if xml:space="preserve" * */ @Test(dataProvider = "xml-data-whitespace-xslt") public void testWhitespaceWithTransformer(String sourceFile, String expectedFile) throws Exception { String source = read(sourceFile); String expected = read(expectedFile); // test it's no change if no pretty-print String result = transform(toXmlDocument(source), false); assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result); // test pretty-print assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected); } /* * test block element, inline element, text, and mixed elements. */ @DataProvider(name = "html-data") public Object[][] htmlData() throws Exception { return new Object[][] { { "htmltest1.xml", "htmltest1.out" }, { "htmltest2.xml", "htmltest2.out" }, { "htmltest3.xml", "htmltest3.out" }, { "htmltest4.xml", "htmltest4.out" }, { "htmltest5.xml", "htmltest5.out" }, { "htmltest6.xml", "htmltest6.out" }, /* @bug 8174025, test whitespace between inline elements */ { "htmltest7.xml", "htmltest7.out" } }; } /* * @bug 8087303 * Transform to HTML, test Pretty Print for HTML. * */ @Test(dataProvider = "html-data") public void testTransformToHTML(String sourceFile, String expectedFile) throws Exception { String source = read(sourceFile); String expected = read(expectedFile); // test it's no change if no pretty-print StringWriter writer = new StringWriter(); getTransformer(true, false).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(writer.toString())), "The actual is: " + writer.toString()); // test pretty-print writer = new StringWriter(); getTransformer(true, true).transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); assertEquals(writer.toString().replaceAll("\r\n", "\n"), expected); } /* * @bug 8174025 * Test the serializer can handle correctly. * */ @Test public void testDisableOutputEscaping() throws Exception { final String xsl ="generate-catalog.xsl"; final String xml ="simple-entity-resolver-config.xml"; final String expectedOutput ="simple-entity-resolver-config-transformed.xml"; TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTemplates(new StreamSource(new StringReader(read(xsl)))).newTransformer(); String key = "schemaBase"; String value = "schemas"; transformer.setParameter(key, value); StringWriter writer = new StringWriter(); transformer.transform(new StreamSource(new StringReader(read(xml))), new StreamResult(writer)); assertEquals(writer.toString().replaceAll("\r\n", "\n"), read(expectedOutput)); } @Test public void testLSSerializerFormatPrettyPrint() { final String XML_DOCUMENT = "\n" + "before child elementafter child element"; /**JDK-8035467 * no newline in default output */ final String XML_DOCUMENT_DEFAULT_PRINT = "" + "" + "before child element" + "" + "after child element"; final String XML_DOCUMENT_PRETTY_PRINT = "\n" + " before child element\n" + " \n" + " \n" + " \n" + " \n" + " after child element\n" + "\n"; // it all begins with a Document DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = null; try { documentBuilder = documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException parserConfigurationException) { parserConfigurationException.printStackTrace(); Assert.fail(parserConfigurationException.toString()); } Document document = null; StringReader stringReader = new StringReader(XML_DOCUMENT); InputSource inputSource = new InputSource(stringReader); try { document = documentBuilder.parse(inputSource); } catch (SAXException saxException) { saxException.printStackTrace(); Assert.fail(saxException.toString()); } catch (IOException ioException) { ioException.printStackTrace(); Assert.fail(ioException.toString()); } // query DOM Interfaces to get to a LSSerializer DOMImplementation domImplementation = documentBuilder.getDOMImplementation(); DOMImplementationLS domImplementationLS = (DOMImplementationLS) domImplementation; LSSerializer lsSerializer = domImplementationLS.createLSSerializer(); System.out.println("Serializer is: " + lsSerializer.getClass().getName() + " " + lsSerializer); // get configuration DOMConfiguration domConfiguration = lsSerializer.getDomConfig(); // query current configuration Boolean defaultFormatPrettyPrint = (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT); Boolean canSetFormatPrettyPrintFalse = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); Boolean canSetFormatPrettyPrintTrue = (Boolean) domConfiguration.canSetParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); System.out.println(DOM_FORMAT_PRETTY_PRINT + " default/can set false/can set true = " + defaultFormatPrettyPrint + "/" + canSetFormatPrettyPrintFalse + "/" + canSetFormatPrettyPrintTrue); // test values assertEquals(defaultFormatPrettyPrint, Boolean.FALSE, "Default value of " + DOM_FORMAT_PRETTY_PRINT + " should be " + Boolean.FALSE); assertEquals(canSetFormatPrettyPrintFalse, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.FALSE + " should be " + Boolean.TRUE); assertEquals(canSetFormatPrettyPrintTrue, Boolean.TRUE, "Can set " + DOM_FORMAT_PRETTY_PRINT + " to " + Boolean.TRUE + " should be " + Boolean.TRUE); // get default serialization String prettyPrintDefault = lsSerializer.writeToString(document); System.out.println("(default) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) + ": \n\"" + prettyPrintDefault + "\""); assertEquals(prettyPrintDefault, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with default value, " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); // configure LSSerializer to not format-pretty-print domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.FALSE); String prettyPrintFalse = lsSerializer.writeToString(document); System.out.println("(FALSE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) + ": \n\"" + prettyPrintFalse + "\""); assertEquals(prettyPrintFalse, XML_DOCUMENT_DEFAULT_PRINT, "Invalid serialization with FALSE value, " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); // configure LSSerializer to format-pretty-print domConfiguration.setParameter(DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); String prettyPrintTrue = lsSerializer.writeToString(document); System.out.println("(TRUE) " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT) + ": \n\"" + prettyPrintTrue + "\""); assertEquals(prettyPrintTrue, XML_DOCUMENT_PRETTY_PRINT, "Invalid serialization with TRUE value, " + DOM_FORMAT_PRETTY_PRINT + "==" + (Boolean) domConfiguration.getParameter(DOM_FORMAT_PRETTY_PRINT)); } private String serializerWrite(Node xml, boolean pretty) throws Exception { DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); DOMImplementationLS domImplementation = (DOMImplementationLS) registry.getDOMImplementation("LS"); StringWriter writer = new StringWriter(); LSOutput formattedOutput = domImplementation.createLSOutput(); formattedOutput.setCharacterStream(writer); LSSerializer domSerializer = domImplementation.createLSSerializer(); domSerializer.getDomConfig().setParameter(DOM_FORMAT_PRETTY_PRINT, pretty); domSerializer.getDomConfig().setParameter("xml-declaration", false); domSerializer.write(xml, formattedOutput); return writer.toString(); } private String transform(Node xml, boolean pretty) throws Exception { Transformer transformer = getTransformer(false, pretty); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(xml), new StreamResult(writer)); return writer.toString(); } private Document toXmlDocument(String xmlString) throws Exception { InputSource xmlInputSource = new InputSource(new StringReader(xmlString)); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder xmlDocumentBuilder = dbf.newDocumentBuilder(); Document node = xmlDocumentBuilder.parse(xmlInputSource); return node; } private Text newTextNode(String text) throws Exception { DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return db.newDocument().createTextNode(text); } private Document createDocWithSequentTextNodes() throws Exception { DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = db.newDocument(); Node root = doc.createElement("root"); doc.appendChild(root); root.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createTextNode("t")); root.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createTextNode("t")); root.appendChild(doc.createTextNode(" ")); Node child1 = doc.createElement("child1"); root.appendChild(child1); child1.appendChild(doc.createTextNode(" ")); child1.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createTextNode("t")); Node child2 = doc.createElement("child2"); root.appendChild(child2); child2.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createTextNode(" ")); Node child3 = doc.createElement("child3"); root.appendChild(child3); child3.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createTextNode(" ")); Node child4 = doc.createElement("child4"); root.appendChild(child4); child4.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createTextNode(" ")); Node child5 = doc.createElement("child5"); root.appendChild(child5); child5.appendChild(doc.createTextNode("t")); Node child51 = doc.createElement("child51"); child5.appendChild(child51); child51.appendChild(doc.createTextNode(" ")); Node child511 = doc.createElement("child511"); child51.appendChild(child511); child511.appendChild(doc.createTextNode("t")); child51.appendChild(doc.createTextNode(" ")); child5.appendChild(doc.createTextNode("t")); root.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createComment(" test comment ")); root.appendChild(doc.createTextNode(" \n")); root.appendChild(doc.createComment(" ")); root.appendChild(doc.createTextNode("\n")); root.appendChild(doc.createProcessingInstruction("target1", "test")); root.appendChild(doc.createTextNode(" ")); root.appendChild(doc.createTextNode(" ")); return doc; } private Transformer getTransformer(boolean html, boolean pretty) throws Exception { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); if (html) transformer.setOutputProperty(OutputKeys.METHOD, "html"); transformer.setOutputProperty(OutputKeys.INDENT, pretty ? "yes" : "no"); return transformer; } private String read(String filename) throws Exception { try (InputStream in = PrettyPrintTest.class.getResourceAsStream(filename)) { return new String(in.readAllBytes()); } } private static final String DOM_FORMAT_PRETTY_PRINT = "format-pretty-print"; }