Last active
October 9, 2016 17:13
-
-
Save jelovirt/cd100d71f723fd3e25818baa0b2c54ea to your computer and use it in GitHub Desktop.
Inline matching in pseudo-XSLT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<xsl:template match="metasyntactic"> | |
<xsl:match select="."> | |
<xsl:case match="foo">foo</xsl:case> | |
<xsl:case match="bar">bar</xsl:case> | |
<xsl:case match="baz">baz</xsl:case> | |
</xsl:match> | |
</xsl:template> |
Here it is a Schematron schema which detects this and provides a quick fix to implement this as a refactoring action:
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node() | @*" mode="case">
<xsl:copy>
<xsl:apply-templates select="node() | @*" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xsl:case" mode="case">
<xsl:param name="mode"/>
<xsl:element name="xsl:template">
<xsl:attribute name="mode" select="$mode"/>
<xsl:apply-templates select="node() | @*" mode="copy"/>
</xsl:element>
</xsl:template>
<xsl:template match="node() | @*" mode="copy">
<xsl:copy>
<xsl:apply-templates select="node() | @*" mode="copy"/>
</xsl:copy>
</xsl:template>
<sch:pattern>
<sch:rule context="xsl:match">
<sch:report test="xsl:case" see="https://gist.github.com/jelovirt/cd100d71f723fd3e25818baa0b2c54ea"
sqf:fix="convertMatch" role="info">
This is not supported in XSLT, but it can be turned
into an equivalent XSLT code using apply-templates and
matching in a dedicated mode</sch:report>
<sqf:fix id="convertMatch">
<sqf:description>
<sqf:title>Convert xsl:match equivalent XSLT code</sqf:title>
</sqf:description>
<sch:let name="mode" value="generate-id()"/>
<sch:let name="cases" value="node()"/>
<sqf:add match="ancestor::xsl:template" position="after">
<xsl:for-each select="$cases">
<xsl:apply-templates select="." mode="case">
<xsl:with-param name="mode" select="$mode"/>
</xsl:apply-templates>
</xsl:for-each>
</sqf:add>
<sqf:add position="after">
<xsl:element name="xsl:apply-templates">
<xsl:attribute name="mode" select="$mode"/>
</xsl:element>
</sqf:add>
<sqf:delete/>
</sqf:fix>
</sch:rule>
</sch:pattern>
</sch:schema>
This turns
<xsl:template match="metasyntactic">
<xsl:match select=".">
<xsl:case match="foo">foo</xsl:case>
<xsl:case match="bar">bar</xsl:case>
<xsl:case match="baz">baz</xsl:case>
</xsl:match>
</xsl:template>
into
<xsl:template match="metasyntactic">
<xsl:apply-templates mode="d2e6"/>
</xsl:template>
<xsl:template match="foo" mode="d2e6">foo</xsl:template>
<xsl:template match="bar" mode="d2e6">bar</xsl:template>
<xsl:template match="baz" mode="d2e6">baz</xsl:template>
Regards,
George
To test this in oXygen, the Schematron schema can be added to the XSLT framework, or you can define a validation scenario (from the validation toolbar drop down, select "Configure Validation Scenario") and add a validation unit that validated the document as an XML document with this schema.
I don't know anything about Schematron Quick Fix, so I don't really know why you'd use it here. Couldn't you just use XSLT?
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:local="local"
exclude-result-prefixes="local xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:namespace-alias result-prefix="xsl" stylesheet-prefix="local"/>
<xsl:template match="xsl:template[xsl:match]">
<xsl:variable name="mode" select="generate-id(xsl:match)" as="xs:string"/>
<xsl:next-match>
<xsl:with-param name="mode" select="$mode" as="xs:string" tunnel="yes"/>
</xsl:next-match>
<xsl:apply-templates select="xsl:match/xsl:case">
<xsl:with-param name="mode" select="$mode" as="xs:string" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="xsl:match">
<xsl:param name="mode" as="xs:string" tunnel="yes"/>
<local:apply-templates>
<xsl:attribute name="mode" select="$mode"/>
<xsl:apply-templates select="@select"/>
</local:apply-templates>
</xsl:template>
<xsl:template match="xsl:case">
<xsl:param name="mode" as="xs:string" tunnel="yes"/>
<local:template mode="{$mode}">
<xsl:sequence select="node()"/>
</local:template>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But yeah, this kind of thing should really be baked into the language itself, since debugging generated stylesheets isn't a whole lot of fun.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This could be compiled to
IMO the this could be a productivity boost, because I've noticed that I don't use modes in some cases because I don't want to split the functionality into multiple templates. Syntax sugar like this would be nice; it's kinda like literal result elements, that are just terse alternative to
xsl:element
.