Skip to content

Instantly share code, notes, and snippets.

@jelovirt
Last active October 9, 2016 17:13
Show Gist options
  • Save jelovirt/cd100d71f723fd3e25818baa0b2c54ea to your computer and use it in GitHub Desktop.
Save jelovirt/cd100d71f723fd3e25818baa0b2c54ea to your computer and use it in GitHub Desktop.
Inline matching in pseudo-XSLT
<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>
@jelovirt
Copy link
Author

jelovirt commented Oct 7, 2016

This could be compiled to

<xsl:template match="metasyntactic">
  <xsl:apply-templates select="." mode="gen1"/>
</xsl:template>
<xsl:template match="foo" mode="gen1">foo</xsl:template>
<xsl:template match="bar" mode="gen1">bar</xsl:template>
<xsl:template match="baz" mode="gen1">baz</xsl:template>

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.

@georgebina
Copy link

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

@georgebina
Copy link

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.

@eerohele
Copy link

eerohele commented Oct 9, 2016

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