TransformerTest.java revision 1051:7f3e970af45c
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
24package transform;
25
26import static jaxp.library.JAXPTestUtilities.getSystemProperty;
27
28import java.io.BufferedReader;
29import java.io.ByteArrayInputStream;
30import java.io.ByteArrayOutputStream;
31import java.io.File;
32import java.io.FileNotFoundException;
33import java.io.FileReader;
34import java.io.IOException;
35import java.io.StringReader;
36import java.io.StringWriter;
37
38import javax.xml.parsers.DocumentBuilderFactory;
39import javax.xml.parsers.ParserConfigurationException;
40import javax.xml.transform.Transformer;
41import javax.xml.transform.TransformerException;
42import javax.xml.transform.TransformerFactory;
43import javax.xml.transform.dom.DOMResult;
44import javax.xml.transform.dom.DOMSource;
45import javax.xml.transform.sax.SAXSource;
46import javax.xml.transform.stream.StreamResult;
47import javax.xml.transform.stream.StreamSource;
48
49import org.testng.Assert;
50import org.testng.AssertJUnit;
51import org.testng.annotations.Listeners;
52import org.testng.annotations.Test;
53import org.w3c.dom.Document;
54import org.w3c.dom.Element;
55import org.w3c.dom.Node;
56import org.w3c.dom.NodeList;
57import org.xml.sax.ContentHandler;
58import org.xml.sax.DTDHandler;
59import org.xml.sax.EntityResolver;
60import org.xml.sax.ErrorHandler;
61import org.xml.sax.InputSource;
62import org.xml.sax.SAXException;
63import org.xml.sax.SAXNotRecognizedException;
64import org.xml.sax.SAXNotSupportedException;
65import org.xml.sax.XMLReader;
66import org.xml.sax.helpers.AttributesImpl;
67
68import com.sun.org.apache.xml.internal.serialize.OutputFormat;
69import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
70
71/*
72 * @test
73 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
74 * @run testng/othervm -DrunSecMngr=true transform.TransformerTest
75 * @run testng/othervm transform.TransformerTest
76 * @summary Transformer Tests
77 * @bug 6272879 6305029 6505031 8150704 8162598 8169112 8169772
78 */
79@Listeners({jaxp.library.FilePolicy.class})
80public class TransformerTest {
81
82    /**
83     * Reads the contents of the given file into a string.
84     * WARNING: this method adds a final line feed even if the last line of the file doesn't contain one.
85     *
86     * @param f
87     * The file to read
88     * @return The content of the file as a string, with line terminators as \"n"
89     * for all platforms
90     * @throws IOException
91     * If there was an error reading
92     */
93    private String getFileContentAsString(File f) throws IOException {
94        try (BufferedReader reader = new BufferedReader(new FileReader(f))) {
95            String line;
96            StringBuilder sb = new StringBuilder();
97            while ((line = reader.readLine()) != null) {
98                sb.append(line).append("\n");
99            }
100            return sb.toString();
101        }
102    }
103
104    /**
105     * Utility method for testBug8162598().
106     * Provides a convenient way to check/assert the expected namespaces
107     * of a Node and its siblings.
108     *
109     * @param test
110     * The node to check
111     * @param nstest
112     * Expected namespace of the node
113     * @param nsb
114     * Expected namespace of the first sibling
115     * @param nsc
116     * Expected namespace of the first sibling of the first sibling
117     */
118    private void checkNodeNS8162598(Node test, String nstest, String nsb, String nsc) {
119        String testNodeName = test.getNodeName();
120        if (nstest == null) {
121            Assert.assertNull(test.getNamespaceURI(), "unexpected namespace for " + testNodeName);
122        } else {
123            Assert.assertEquals(test.getNamespaceURI(), nstest, "unexpected namespace for " + testNodeName);
124        }
125        Node b = test.getChildNodes().item(0);
126        if (nsb == null) {
127            Assert.assertNull(b.getNamespaceURI(), "unexpected namespace for " + testNodeName + "->b");
128        } else {
129            Assert.assertEquals(b.getNamespaceURI(), nsb, "unexpected namespace for " + testNodeName + "->b");
130        }
131        Node c = b.getChildNodes().item(0);
132        if (nsc == null) {
133            Assert.assertNull(c.getNamespaceURI(), "unexpected namespace for " + testNodeName + "->b->c");
134        } else {
135            Assert.assertEquals(c.getNamespaceURI(), nsc, "unexpected namespace for " + testNodeName + "->b->c");
136        }
137    }
138
139    private class XMLReaderFor6305029 implements XMLReader {
140        private static final String NAMESPACES = "http://xml.org/sax/features/namespaces";
141        private static final String NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";
142        private boolean namespaces = true;
143        private boolean namespacePrefixes = false;
144        private EntityResolver resolver;
145        private DTDHandler dtdHandler;
146        private ContentHandler contentHandler;
147        private ErrorHandler errorHandler;
148
149        public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
150            if (name.equals(NAMESPACES)) {
151                return namespaces;
152            } else if (name.equals(NAMESPACE_PREFIXES)) {
153                return namespacePrefixes;
154            } else {
155                throw new SAXNotRecognizedException();
156            }
157        }
158
159        public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
160            if (name.equals(NAMESPACES)) {
161                namespaces = value;
162            } else if (name.equals(NAMESPACE_PREFIXES)) {
163                namespacePrefixes = value;
164            } else {
165                throw new SAXNotRecognizedException();
166            }
167        }
168
169        public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
170            return null;
171        }
172
173        public void setProperty(final String name, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
174        }
175
176        public void setEntityResolver(final EntityResolver theResolver) {
177            this.resolver = theResolver;
178        }
179
180        public EntityResolver getEntityResolver() {
181            return resolver;
182        }
183
184        public void setDTDHandler(final DTDHandler theHandler) {
185            dtdHandler = theHandler;
186        }
187
188        public DTDHandler getDTDHandler() {
189            return dtdHandler;
190        }
191
192        public void setContentHandler(final ContentHandler handler) {
193            contentHandler = handler;
194        }
195
196        public ContentHandler getContentHandler() {
197            return contentHandler;
198        }
199
200        public void setErrorHandler(final ErrorHandler handler) {
201            errorHandler = handler;
202        }
203
204        public ErrorHandler getErrorHandler() {
205            return errorHandler;
206        }
207
208        public void parse(final InputSource input) throws IOException, SAXException {
209            parse();
210        }
211
212        public void parse(final String systemId) throws IOException, SAXException {
213            parse();
214        }
215
216        private void parse() throws SAXException {
217            contentHandler.startDocument();
218            contentHandler.startPrefixMapping("prefix", "namespaceUri");
219
220            AttributesImpl atts = new AttributesImpl();
221            if (namespacePrefixes) {
222                atts.addAttribute("", "xmlns:prefix", "xmlns:prefix", "CDATA", "namespaceUri");
223            }
224
225            contentHandler.startElement("namespaceUri", "localName", namespacePrefixes ? "prefix:localName" : "", atts);
226            contentHandler.endElement("namespaceUri", "localName", namespacePrefixes ? "prefix:localName" : "");
227            contentHandler.endPrefixMapping("prefix");
228            contentHandler.endDocument();
229        }
230    }
231
232    /*
233     * @bug 6272879
234     * @summary Test for JDK-6272879
235     */
236    @Test
237    public final void testBug6272879() throws IOException, TransformerException {
238        final String LINE_SEPARATOR = getSystemProperty("line.separator");
239
240        final String xsl =
241                "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" + LINE_SEPARATOR +
242                "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" + LINE_SEPARATOR +
243                "<xsl:output method=\"xml\" indent=\"no\" encoding=\"ISO-8859-1\"/>" + LINE_SEPARATOR +
244                "<xsl:template match=\"/\">" + LINE_SEPARATOR +
245                "<xsl:element name=\"TransformateurXML\">" + LINE_SEPARATOR +
246                "  <xsl:for-each select=\"XMLUtils/test\">" + LINE_SEPARATOR +
247                "  <xsl:element name=\"test2\">" + LINE_SEPARATOR +
248                "    <xsl:element name=\"valeur2\">" + LINE_SEPARATOR +
249                "      <xsl:attribute name=\"attribut2\">" + LINE_SEPARATOR +
250                "        <xsl:value-of select=\"valeur/@attribut\"/>" + LINE_SEPARATOR +
251                "      </xsl:attribute>" + LINE_SEPARATOR +
252                "      <xsl:value-of select=\"valeur\"/>" + LINE_SEPARATOR +
253                "    </xsl:element>" + LINE_SEPARATOR +
254                "  </xsl:element>" + LINE_SEPARATOR +
255                "  </xsl:for-each>" + LINE_SEPARATOR +
256                "</xsl:element>" + LINE_SEPARATOR +
257                "</xsl:template>" + LINE_SEPARATOR +
258                "</xsl:stylesheet>";
259
260        final String sourceXml =
261                "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" + LINE_SEPARATOR +
262                // "<!DOCTYPE XMLUtils [" + LINE_SEPARATOR +
263                // "<!ELEMENT XMLUtils (test*)>" + LINE_SEPARATOR +
264                // "<!ELEMENT test (valeur*)>" + LINE_SEPARATOR +
265                // "<!ELEMENT valeur (#PCDATA)>" + LINE_SEPARATOR +
266                // "<!ATTLIST valeur attribut CDATA #REQUIRED>]>" +
267                // LINE_SEPARATOR +
268                "<XMLUtils>" + LINE_SEPARATOR +
269                "  <test>" + LINE_SEPARATOR +
270                "    <valeur attribut=\"Attribut 1\">Valeur 1</valeur>" + LINE_SEPARATOR +
271                "  </test>" + LINE_SEPARATOR +
272                "  <test>" + LINE_SEPARATOR +
273                "    <valeur attribut=\"Attribut 2\">Valeur 2</valeur>" + LINE_SEPARATOR +
274                "  </test>" + LINE_SEPARATOR +
275                "</XMLUtils>";
276
277        System.out.println("Stylesheet:");
278        System.out.println("=============================");
279        System.out.println(xsl);
280        System.out.println();
281
282        System.out.println("Source before transformation:");
283        System.out.println("=============================");
284        System.out.println(sourceXml);
285        System.out.println();
286
287        // transform to DOM result
288        TransformerFactory tf = TransformerFactory.newInstance();
289        Transformer t = tf.newTransformer(new StreamSource(new ByteArrayInputStream(xsl.getBytes())));
290        DOMResult result = new DOMResult();
291        t.transform(new StreamSource(new ByteArrayInputStream(sourceXml.getBytes())), result);
292        Document document = (Document)result.getNode();
293
294        System.out.println("Result after transformation:");
295        System.out.println("============================");
296        OutputFormat format = new OutputFormat();
297        format.setIndenting(true);
298        new XMLSerializer(System.out, format).serialize(document);
299        System.out.println();
300
301        System.out.println("Node content for element valeur2:");
302        System.out.println("=================================");
303        NodeList nodes = document.getElementsByTagName("valeur2");
304        for (int i = 0; i < nodes.getLength(); i++) {
305            Node node = nodes.item(i);
306            System.out.println("  Node value: " + node.getFirstChild().getNodeValue());
307            System.out.println("  Node attribute: " + node.getAttributes().item(0).getNodeValue());
308
309            AssertJUnit.assertEquals("Node value mismatch", "Valeur " + (i + 1), node.getFirstChild().getNodeValue());
310            AssertJUnit.assertEquals("Node attribute mismatch", "Attribut " + (i + 1), node.getAttributes().item(0).getNodeValue());
311        }
312    }
313
314    /*
315     * @bug 6305029
316     * @summary Test for JDK-6305029
317     */
318    @Test
319    public final void testBug6305029() throws TransformerException {
320        final String XML_DOCUMENT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<prefix:localName xmlns:prefix=\"namespaceUri\"/>";
321
322        // test SAXSource
323        SAXSource saxSource = new SAXSource(new XMLReaderFor6305029(), new InputSource());
324        StringWriter resultWriter = new StringWriter();
325        TransformerFactory tf = TransformerFactory.newInstance();
326        tf.newTransformer().transform(saxSource, new StreamResult(resultWriter));
327        AssertJUnit.assertEquals("Identity transform of SAXSource", XML_DOCUMENT, resultWriter.toString());
328
329        // test StreamSource
330        StreamSource streamSource = new StreamSource(new StringReader(XML_DOCUMENT));
331        resultWriter = new StringWriter();
332        tf.newTransformer().transform(streamSource, new StreamResult(resultWriter));
333        AssertJUnit.assertEquals("Identity transform of StreamSource", XML_DOCUMENT, resultWriter.toString());
334    }
335
336    /*
337     * @bug 6505031
338     * @summary Test transformer parses keys and their values coming from different xml documents.
339     */
340    @Test
341    public final void testBug6505031() throws TransformerException {
342        TransformerFactory tf = TransformerFactory.newInstance();
343        Transformer t = tf.newTransformer(new StreamSource(getClass().getResource("transform.xsl").toString()));
344        t.setParameter("config", getClass().getResource("config.xml").toString());
345        t.setParameter("mapsFile", getClass().getResource("maps.xml").toString());
346        StringWriter sw = new StringWriter();
347        t.transform(new StreamSource(getClass().getResource("template.xml").toString()), new StreamResult(sw));
348        String s = sw.toString();
349        Assert.assertTrue(s.contains("map1key1value") && s.contains("map2key1value"));
350    }
351
352    /*
353     * @bug 8150704
354     * @summary Test that XSL transformation with lots of temporary result trees will not run out of DTM IDs.
355     */
356    @Test
357    public final void testBug8150704() throws TransformerException, IOException {
358        System.out.println("Testing transformation of Bug8150704-1.xml...");
359        TransformerFactory tf = TransformerFactory.newInstance();
360        Transformer t = tf.newTransformer(new StreamSource(getClass().getResource("Bug8150704-1.xsl").toString()));
361        StringWriter sw = new StringWriter();
362        t.transform(new StreamSource(getClass().getResource("Bug8150704-1.xml").toString()), new StreamResult(sw));
363        String resultstring = sw.toString().replaceAll("\\r\\n", "\n").replaceAll("\\r", "\n");
364        String reference = getFileContentAsString(new File(getClass().getResource("Bug8150704-1.ref").getPath()));
365        Assert.assertEquals(resultstring, reference, "Output of transformation of Bug8150704-1.xml does not match reference");
366        System.out.println("Passed.");
367
368        System.out.println("Testing transformation of Bug8150704-2.xml...");
369        t = tf.newTransformer(new StreamSource(getClass().getResource("Bug8150704-2.xsl").toString()));
370        sw = new StringWriter();
371        t.transform(new StreamSource(getClass().getResource("Bug8150704-2.xml").toString()), new StreamResult(sw));
372        resultstring = sw.toString().replaceAll("\\r\\n", "\n").replaceAll("\\r", "\n");
373        reference = getFileContentAsString(new File(getClass().getResource("Bug8150704-2.ref").getPath()));
374        Assert.assertEquals(resultstring, reference, "Output of transformation of Bug8150704-2.xml does not match reference");
375        System.out.println("Passed.");
376    }
377
378    /*
379     * @bug 8162598
380     * @summary Test XSLTC handling of namespaces, especially empty namespace definitions to reset the
381     *          default namespace
382     */
383    @Test
384    public final void testBug8162598() throws IOException, TransformerException {
385        final String LINE_SEPARATOR = getSystemProperty("line.separator");
386
387        final String xsl =
388            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + LINE_SEPARATOR +
389            "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">" + LINE_SEPARATOR +
390            "    <xsl:template match=\"/\">" + LINE_SEPARATOR +
391            "        <root xmlns=\"ns1\">" + LINE_SEPARATOR +
392            "            <xsl:call-template name=\"transform\"/>" + LINE_SEPARATOR +
393            "        </root>" + LINE_SEPARATOR +
394            "    </xsl:template>" + LINE_SEPARATOR +
395            "    <xsl:template name=\"transform\">" + LINE_SEPARATOR +
396            "        <test1 xmlns=\"ns2\"><b xmlns=\"ns2\"><c xmlns=\"\"></c></b></test1>" + LINE_SEPARATOR +
397            "        <test2 xmlns=\"ns1\"><b xmlns=\"ns2\"><c xmlns=\"\"></c></b></test2>" + LINE_SEPARATOR +
398            "        <test3><b><c xmlns=\"\"></c></b></test3>" + LINE_SEPARATOR +
399            "        <test4 xmlns=\"\"><b><c xmlns=\"\"></c></b></test4>" + LINE_SEPARATOR +
400            "        <test5 xmlns=\"ns1\"><b><c xmlns=\"\"></c></b></test5>" + LINE_SEPARATOR +
401            "        <test6 xmlns=\"\"/>" + LINE_SEPARATOR +
402            "    </xsl:template>" + LINE_SEPARATOR +
403            "</xsl:stylesheet>";
404
405
406        final String sourceXml =
407                "<?xml version=\"1.0\" encoding=\"UTF-8\"?><aaa></aaa>" + LINE_SEPARATOR;
408
409        System.out.println("Stylesheet:");
410        System.out.println("=============================");
411        System.out.println(xsl);
412        System.out.println();
413
414        System.out.println("Source before transformation:");
415        System.out.println("=============================");
416        System.out.println(sourceXml);
417        System.out.println();
418
419        // transform to DOM result
420        TransformerFactory tf = TransformerFactory.newInstance();
421        Transformer t = tf.newTransformer(new StreamSource(new ByteArrayInputStream(xsl.getBytes())));
422        DOMResult result = new DOMResult();
423        t.transform(new StreamSource(new ByteArrayInputStream(sourceXml.getBytes())), result);
424        Document document = (Document)result.getNode();
425
426        System.out.println("Result after transformation:");
427        System.out.println("============================");
428        OutputFormat format = new OutputFormat();
429        format.setIndenting(true);
430        new XMLSerializer(System.out, format).serialize(document);
431        System.out.println();
432        checkNodeNS8162598(document.getElementsByTagName("test1").item(0), "ns2", "ns2", null);
433        checkNodeNS8162598(document.getElementsByTagName("test2").item(0), "ns1", "ns2", null);
434        checkNodeNS8162598(document.getElementsByTagName("test3").item(0), null, null, null);
435        checkNodeNS8162598(document.getElementsByTagName("test4").item(0), null, null, null);
436        checkNodeNS8162598(document.getElementsByTagName("test5").item(0), "ns1", "ns1", null);
437        Assert.assertNull(document.getElementsByTagName("test6").item(0).getNamespaceURI(), "unexpected namespace for test6");
438    }
439
440    /**
441     * @bug 8169112
442     * @summary Test compilation of large xsl file with outlining.
443     *
444     * This test merely compiles a large xsl file and tests if its bytecode
445     * passes verification by invoking the transform() method for
446     * dummy content. The test succeeds if no Exception is thrown
447     */
448    @Test
449    public final void testBug8169112() throws FileNotFoundException,
450        TransformerException
451    {
452        TransformerFactory tf = TransformerFactory.newInstance();
453        String xslFile = getClass().getResource("Bug8169112.xsl").toString();
454        Transformer t = tf.newTransformer(new StreamSource(xslFile));
455        String xmlIn = "<?xml version=\"1.0\"?><DOCROOT/>";
456        ByteArrayInputStream bis = new ByteArrayInputStream(xmlIn.getBytes());
457        ByteArrayOutputStream bos = new ByteArrayOutputStream();
458        t.transform(new StreamSource(bis), new StreamResult(bos));
459    }
460
461    /**
462     * @bug 8169772
463     * @summary Test transformation of DOM with null valued text node
464     *
465     * This test would throw a NullPointerException during transform when the
466     * fix was not present.
467     */
468    @Test
469    public final void testBug8169772() throws ParserConfigurationException,
470        SAXException, IOException, TransformerException
471    {
472        // create a small DOM
473        Document doc = DocumentBuilderFactory.newInstance().
474            newDocumentBuilder().parse(
475                new ByteArrayInputStream(
476                    "<?xml version=\"1.0\"?><DOCROOT/>".getBytes()
477                )
478            );
479
480        // insert a bad element
481        Element e = doc.createElement("ERROR");
482        e.appendChild(doc.createTextNode(null));
483        doc.getDocumentElement().appendChild(e);
484
485        // transform
486        ByteArrayOutputStream bos = new ByteArrayOutputStream();
487        TransformerFactory.newInstance().newTransformer().transform(
488            new DOMSource(doc.getDocumentElement()), new StreamResult(bos)
489        );
490        System.out.println("Transformation result (DOM with null text node):");
491        System.out.println("================================================");
492        System.out.println(bos);
493    }
494}
495