How to Preprocess XSLT using SAX

The xsl:include/@mode patchset I have developed to ease XML Pipeline in XSLT is not good. The major problem is it won't work on closed-source XSLT processors (Saxon PE/EE for example).

As suggested by Michael Kay, there is a standard compliant way to implement above:

<xsl:include href="link1.xsl?mode=link1"/>

It would work by means of custom URIResolver performing XSLT preprocessing.

Using this approach chain.xsl would look like

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">

    <xsl:output method="xml" omit-xml-declaration="no" indent="no" encoding="UTF-8"/>

    <xsl:include href="link1.xsl?mode=link1"/>
    <xsl:include href="link2.xsl?mode=link2"/>
    <xsl:include href="link3.xsl?mode=link3"/>

    <xsl:template match="/">
        <xsl:variable name="link1">
            <xsl:apply-templates mode="link1" select="node()"/>
        </xsl:variable>
        <xsl:variable name="link2">
            <xsl:apply-templates mode="link2" select="exsl:node-set($link1)/node()"/>
        </xsl:variable>
        <xsl:variable name="link3">
            <xsl:apply-templates mode="link3" select="exsl:node-set($link2)/node()"/>
        </xsl:variable>

        <xsl:copy-of select="exsl:node-set($link3)/node()"/>
    </xsl:template>

</xsl:stylesheet>

, link1.xsl, link2.xsl, link3.xsl and identity.xsl will be the same as in previous variant.

I have implemented ?mode in xsl:include/@href, and it works great.

The implementation works by using custom URIResolver and SAX ContentHandler transformation. The ContentHandler transformation is implemented in Java and is on-the-fly transforming XSLT source code adding mode attribute where necessary.

The reason I choose SAX ContentHandler approach over simpler XSLT like

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	<xsl:output method="xml" omit-xml-declaration="no" indent="no" encoding="UTF-8"/>

	<xsl:param name="mode"/>

	<xsl:template priority="-9" match="@*|node()">
		<xsl:copy>
			<xsl:apply-templates select="@*|node()"/>
		</xsl:copy>
	</xsl:template>

	<xsl:template match="xsl:template[@match and not(@mode)] | xsl:apply-templates[not(@mode)]">
		<xsl:copy>
			<xsl:attribute name="mode">
		 		<xsl:value-of select="$mode"/>
			</xsl:attribute>
			<xsl:apply-templates select="@*|node()"/>
		</xsl:copy>
	</xsl:template>

	<xsl:template match="xsl:include/@href | xsl:import/@href">
		<xsl:attribute name="{name()}">
			<xsl:choose>
				<xsl:when test="contains(., '?')">
					<xsl:value-of select="concat(., '&amp;mode=', $mode)"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="concat(., '?mode=', $mode)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:attribute>
	</xsl:template>

</xsl:stylesheet>

is because ContentHandler approach preserves line number information: if you'll make an error in the source, processor will report exact line/col position of error; w/ above XSLT that is not possible.

You can find ZIP w/ full source code attached below.

AttachmentSize
xml-pipeline3.zip8.88 KB

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.