/* * Copyright (c) 2014, 2016, 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; import static org.testng.Assert.assertEquals; import java.io.IOException; 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 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm -DrunSecMngr=true common.PrettyPrintTest * @run testng/othervm common.PrettyPrintTest * @summary Test serializing xml and html with indentation. */ @Listeners({jaxp.library.BasePolicy.class}) public class PrettyPrintTest { /* * test pure text content, CDATA, elements only, text and element, * whitespace and element, xml:space property and nested xml:space property, * mixed node types, sequent Text nodes. */ @DataProvider(name = "xml-data") public Object[][] xmlData() throws Exception { return new Object[][] { { newTextNode(" abc def \nline2 &a \n test"), " abc def \n" + "line2 &a \n" + " test" }, { toXmlDocument(""), "\n" + " \n" + "\n" }, { toXmlDocument(""), "\n" + " \n" + "\n" }, { toXmlDocument(""), "\n" + " \n" + " \n" + " \n" + "\n" }, { toXmlDocument("child1" + " abc test"), "\n" + " \n" + " child1\n" + " abc test\n" + " \n" + "\n" }, { toXmlDocument(" Java Tutorials and Examples 1 " + "en-us \n \n"), "\n" + " \n" + " Java Tutorials and Examples 1\n" + " en-us\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" }, { toXmlDocument(" " + "Java Tutorials and Examples 1 en-us"), "\n" + " Java Tutorials and Examples 1 en-us\n" + "\n" }, { toXmlDocument(" Java " + " " + " 5 \n " + " " + " " + " " + " " + " "), "\n" + " Java \n" + " 5\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" }, { toXmlDocument(MIXED_NODE_TYPES_XML), "\n" + " \n" + " t\n" + " \n" + " \n" + "t \n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + "\n" }, { createDocWithSequentTextNodes(), "\n" + " t\n" + "t \n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " t\n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " \n" + "\n" } }; } /* * @bug 8087303 * Test the whitespace text nodes are serialized with pretty-print by LSSerializer and transformer correctly * */ @Test(dataProvider = "xml-data") public void testXMLPrettyPrint(Node xml, String expected) throws Exception { assertEquals(serializerWrite(xml), expected); assertEquals(transform(xml).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[][] { { "Java Tutorials and Examples 1" + " en-us", "\n" + " \n" + " Java Tutorials and Examples 1\n" + " en-us\n" + " \n" + "\n" }, { "JavaTM", "\n" + " JavaTM\n" + "\n" }, { "

this is a test page

", "

\n" + " this is a test page\n" + "

\n" }, { "

A heading

new paragraph

an empty form
" + "test

", "\n" + " \n" + "

A heading

\n" + "

\n" + " new paragraph\n" + "

an empty form
\n" + " test\n" + "

\n" + " \n" + "\n" }, { "

this is a mixed test click

to the test" + "

pagelink end

", "\n" + "

\n" + " this is a mixed test click\n" + "

to the test

\n" + " pagelink end\n" + "

\n" + "\n" }, { "

text another

", "

\n" + " text\n" + "

\n" + " another\n" + " \n" + "

\n" }, { MIXED_NODE_TYPES_XML, "\n" + " \n" + " t\n" + " \n" + " \n" + "t \n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " t\n" + " \n" + " \n" + " \n" + " t\n" + " \n" + " \n" + "\n" } }; } /* * @bug 8087303 * Transform to HTML, test Pretty Print for HTML. * */ @Test(dataProvider = "html-data") public void testTransformToHTML(String source, String expected) throws Exception { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.METHOD, "html"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); transformer.transform(new StreamSource(new StringReader(source)), new StreamResult(writer)); assertEquals(writer.toString().replaceAll("\r\n", "\n"), expected); } @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) 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, true); domSerializer.getDomConfig().setParameter("xml-declaration", false); domSerializer.write(xml, formattedOutput); return writer.toString(); } private String transform(Node xml) throws Exception { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 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(); dbf.setValidating(true); 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(" ")); 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 static final String DOM_FORMAT_PRETTY_PRINT = "format-pretty-print"; private static final String MIXED_NODE_TYPES_XML = "\n" + " t\n" + "t \n" + " \n" + " t\n" + " \n" + " \n" + " t\n" + " \n" + " t\n" + " \n" + " t\n" + " \n" + " \n" + " \n"; }