package cyrille.xml.xsl;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import junit.framework.TestCase;
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Test case of <a href="https://issues.apache.org/jira/browse/XALANJ-2244">XALANJ-2244 : xsl:sort does not work with nodeset parameters</a>.
*
* {@link #testSortedForEachWithParameterizedNodeSet()} fails with Xalan-2.7.0. The workaround wrapping the parameterized nodeset in a
* <code>nodeset</code> works as shown in {@link #testSortedForEachWithWorkAroundedParameterizedNodeSet()}.
*
* @author <a href="mailto:cleclerc@xebia.fr">Cyrille Le Clerc</a>
*/
public class XALANJ_2444_Test extends TestCase {
protected String xml = "<root>\n" + " <parent>\n" + " <child>\n" + " <sort-key>1</sort-key>\n"
+ " <label>item-1</label>\n" + " </child>\n" + " <child>\n" + " <sort-key>3</sort-key>\n"
+ " <label>item-3</label>\n" + " </child>\n" + " <child>\n" + " <sort-key>2</sort-key>\n"
+ " <label>item-2</label>\n" + " </child>\n" + " </parent>\n" + "</root>";
public void dump(String xml, String xsl, String generated) {
System.out.println("===============================");
System.out.println("XML");
System.out.println();
System.out.println(xml);
System.out.println();
System.out.println("===============================");
System.out.println("XSL");
System.out.println();
System.out.println(xsl);
System.out.println();
System.out.println("===============================");
System.out.println("GENERATED");
System.out.println();
System.out.println(generated);
System.out.println();
}
/**
* Directly using the template's parameter <code>$path</code> in the <code>select</code> attribute of the <code>xsl:for-each</code>
* element fails as described in <a href="https://issues.apache.org/jira/browse/XALANJ-2244">XALANJ-2244 : xsl:sort does not work with
* nodeset parameters</a>.
*/
public void testSortedForEachWithParameterizedNodeSet() throws Exception {
String xsl = "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns='http://www.w3.org/TR/xhtml1/strict'>\n"
+ " <xsl:template match='root'>\n"
+ " <xsl:call-template name='test-sorted-for-each-parameterized'>\n"
+ " <xsl:with-param name='path' select='parent/child' />\n"
+ " </xsl:call-template>\n"
+ " </xsl:template>\n"
+ " \n"
+ " <xsl:template name='test-sorted-for-each-parameterized'>\n"
+ " <xsl:param name='path'/>\n"
+ " <test-sorted-for-each-parameterized>\n"
+ " <xsl:for-each select='$path'>\n"
+ " <xsl:sort select='sort-key' data-type='number' />\n"
+ " <xsl:element name='{label}' />\n"
+ " </xsl:for-each>\n" + " </test-sorted-for-each-parameterized>\n" + " </xsl:template>\n" + "</xsl:stylesheet>";
String actual = transform(xml, xsl);
String expected = "<test-sorted-for-each-parameterized xmlns=\"http://www.w3.org/TR/xhtml1/strict\"><item-1/><item-2/><item-3/></test-sorted-for-each-parameterized>";
dump(xml, xsl, actual);
assertEquals(expected, actual);
}
/**
* Workaround using <code>nodeset()</code> function in the <code>select</code> attribute of the <code>for-each</code> element.
* Looks like <code><xsl:for-each select='nodeset($path)' > ... </xsl:for-each></code>.
*/
public void testSortedForEachWithWorkAroundedParameterizedNodeSet() throws Exception {
String xsl = "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns='http://www.w3.org/TR/xhtml1/strict'>\n"
+ " <xsl:template match='root'>\n"
+ " <xsl:call-template name='test-sorted-for-each-parameterized-workaround'>\n"
+ " <xsl:with-param name='path' select='parent/child' />\n"
+ " </xsl:call-template>\n"
+ " </xsl:template>\n"
+ " \n"
+ " <xsl:template name='test-sorted-for-each-parameterized-workaround'>\n"
+ " <xsl:param name='path'/>\n"
+ " <test-sorted-for-each-parameterized-workaround>\n"
+ " <xsl:for-each select='nodeset($path)'>\n"
+ " <xsl:sort select='sort-key' data-type='number' />\n"
+ " <xsl:element name='{label}' />\n"
+ " </xsl:for-each>\n"
+ " </test-sorted-for-each-parameterized-workaround>\n"
+ " </xsl:template>\n"
+ "</xsl:stylesheet>";
String actual = transform(xml, xsl);
String expected = "<test-sorted-for-each-parameterized-workaround xmlns=\"http://www.w3.org/TR/xhtml1/strict\"><item-1/><item-2/><item-3/></test-sorted-for-each-parameterized-workaround>";
dump(xml, xsl, actual);
assertEquals(expected, actual);
}
/**
* Nodeset used in the <code>select</code> attribute of the <code>xsl:for-each</code> is passed as an <code>xsl:variable</code> to
* ensure <a href="https://issues.apache.org/jira/browse/XALANJ-860">XALANJ-860</a> is fixed and is not the same as XALANJ-2244.
*/
public void testSortedForEachWithVariabilizedNodeSet() throws Exception {
String xsl = "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns='http://www.w3.org/TR/xhtml1/strict'>\n"
+ " <xsl:template match='root'>\n"
+ " <xsl:variable name='path' select='parent/child' />\n"
+ " <test-sorted-for-each-variabilized>\n"
+ " <xsl:for-each select='$path'>\n"
+ " <xsl:sort select='sort-key' data-type='number' />\n"
+ " <xsl:element name='{label}' />\n"
+ " </xsl:for-each>\n" + " </test-sorted-for-each-variabilized>\n" + " </xsl:template>\n" + "</xsl:stylesheet>";
String actual = transform(xml, xsl);
String expected = "<test-sorted-for-each-variabilized xmlns=\"http://www.w3.org/TR/xhtml1/strict\"><item-1/><item-2/><item-3/></test-sorted-for-each-variabilized>";
dump(xml, xsl, actual);
assertEquals(expected, actual);
}
/**
* Helper method to perform the XSLT transformation ensuring we use an XSLTC Transformer (relying on {@link org.apache.xalan.xsltc.trax.TransformerFactoryImpl}).
*/
public String transform(String xml, String xsl) throws Exception {
// force usage of Xalan trax implementation
TransformerFactory transformerFactory = (TransformerFactory) Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance();
Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(xsl)));
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StringWriter result = new StringWriter();
transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(result));
return result.toString();
}
}