Last active
December 15, 2015 22:39
-
-
Save ColinEberhardt/5334470 to your computer and use it in GitHub Desktop.
An XSLT siteswap validator by Colin E.
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
The aim was to create a vaildator that takes an XML document of the following form and annotate | |
it to state whether each individual siteswap was valid or invalid: | |
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<siteswaps> | |
<siteswap>34535</siteswap> | |
<siteswap>543</siteswap> | |
<siteswap>441</siteswap> | |
<siteswap>97531</siteswap> | |
<siteswap>24234</siteswap> | |
</siteswaps> | |
generating the following: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap valid="true">34535</siteswap> | |
<siteswap valid="false">543</siteswap> | |
<siteswap valid="true">441</siteswap> | |
<siteswap valid="true">97531</siteswap> | |
<siteswap valid="true">24234</siteswap> | |
</siteswaps> | |
However, the strength of XSLT lies in its ability to perform operations based on the structure | |
of the document tree, rather than text processing. Therefore the above document is not in a very | |
XSLT friendly format making it very difficult to validate it using a single tranformation. | |
After a bit of head scratching I decided to tackle this problem by creating a chain of transformations, | |
each on performing a specific task. This is much closer to the spirit of what XSLT was intended for. | |
======= Transformation #1: "expand_throws.xsl" ======= | |
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
<xsl:output indent="yes"/> | |
<xsl:template match="@*|node()"> | |
<xsl:copy> | |
<xsl:apply-templates select="@*|node()"/> | |
</xsl:copy> | |
</xsl:template> | |
<xsl:template match="siteswap"> | |
<xsl:element name="siteswap"> | |
<xsl:attribute name="value"> | |
<xsl:value-of select="."/> | |
</xsl:attribute> | |
<xsl:call-template name="splitThrows"> | |
<xsl:with-param name="siteswap" select="."/> | |
</xsl:call-template> | |
</xsl:element> | |
</xsl:template> | |
<xsl:template name="splitThrows"> | |
<xsl:param name="siteswap"/> | |
<xsl:if test="string-length($siteswap)>0"> | |
<xsl:element name="beat"> | |
<xsl:element name="throw"> | |
<xsl:value-of select="substring($siteswap,1,1)"/> | |
</xsl:element> | |
</xsl:element> | |
<xsl:call-template name="splitThrows"> | |
<xsl:with-param name="siteswap" select="substring($siteswap,2,string-length($siteswap)-1)"/> | |
</xsl:call-template> | |
</xsl:if> | |
</xsl:template> | |
</xsl:stylesheet> | |
The above transformation splits each siteswap up into a collection of beats, each on containing a throw. | |
for example the following XML file: | |
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<siteswaps> | |
<siteswap>4411</siteswap> | |
</siteswaps> | |
will be transformed to become the following: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap value="4411"> | |
<beat> | |
<throw>4</throw> | |
</beat> | |
<beat> | |
<throw>4</throw> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
</siteswap> | |
</siteswaps> | |
======= Transformation #2: "locate_catches.xsl" ======= | |
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
<xsl:output indent="yes"/> | |
<xsl:template match="@*|node()"> | |
<xsl:copy> | |
<xsl:apply-templates select="@*|node()"/> | |
</xsl:copy> | |
</xsl:template> | |
<xsl:template match="siteswap"> | |
<xsl:element name="siteswap"> | |
<xsl:apply-templates select="@*"/> | |
<xsl:variable name="siteswapLength" select="count(beat)"/> | |
<xsl:for-each select="beat"> | |
<xsl:element name="beat"> | |
<xsl:variable name="thisBeatPos" select="position()"/> | |
<xsl:apply-templates select="throw"/> | |
<xsl:for-each select="../beat/throw"> | |
<xsl:if test="((number(.)+position()) mod $siteswapLength)+1 = $thisBeatPos"> | |
<xsl:element name="catch"/> | |
</xsl:if> | |
</xsl:for-each> | |
</xsl:element> | |
</xsl:for-each> | |
</xsl:element> | |
</xsl:template> | |
</xsl:stylesheet> | |
The above transformation considers each beat in turn and inserts a catch element if | |
any of the throws within the siteswap are caught there. for example, the following XML: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap value="4411"> | |
<beat> | |
<throw>4</throw> | |
</beat> | |
<beat> | |
<throw>4</throw> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
</siteswap> | |
</siteswaps> | |
Is transformed to: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap value="4411"> | |
<beat> | |
<throw>4</throw> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>4</throw> | |
<catch/> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
</siteswap> | |
</siteswaps> | |
You should already be able to see that this siteswap is not valid, due to their being two | |
catches made on the second beat, but just a single throw. | |
======= Transformation #3: "validate_siteswap.xsl" ======= | |
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
<xsl:output indent="yes"/> | |
<xsl:template match="@*|node()"> | |
<xsl:copy> | |
<xsl:apply-templates select="@*|node()"/> | |
</xsl:copy> | |
</xsl:template> | |
<xsl:template match="siteswap"> | |
<xsl:element name="siteswap"> | |
<xsl:attribute name="valid"> | |
<xsl:variable name="invalid"> | |
<xsl:call-template name="findMultipleCatches"/> | |
</xsl:variable> | |
<xsl:choose> | |
<xsl:when test="$invalid=''"> | |
<xsl:text>true</xsl:text> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:text>false</xsl:text> | |
</xsl:otherwise> | |
</xsl:choose> | |
</xsl:attribute> | |
<xsl:value-of select="@value"/> | |
</xsl:element> | |
</xsl:template> | |
<xsl:template name="findMultipleCatches"> | |
<xsl:for-each select="beat"> | |
<xsl:if test="count(catch) > 1"> | |
<xsl:text>x</xsl:text> | |
</xsl:if> | |
</xsl:for-each> | |
</xsl:template> | |
</xsl:stylesheet> | |
The final transformation inspects all the beats within each siteswap to find multiple | |
catches. The document is transformed back into its original form, with the addition | |
of a valid attribute. for example, the following XML: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap value="4411"> | |
<beat> | |
<throw>4</throw> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>4</throw> | |
<catch/> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
<catch/> | |
</beat> | |
<beat> | |
<throw>1</throw> | |
</beat> | |
</siteswap> | |
</siteswaps> | |
Is transformed to: | |
<?xml version="1.0" encoding="UTF-8"?> | |
<siteswaps> | |
<siteswap valid="false">4411</siteswap> | |
</siteswaps> | |
======= Putting it all together ======= | |
if you use Ant, the following build file will execute the transformations | |
in the desired order: | |
<?xml version="1.0"?> | |
<project name="validate cocoon" default="validate" basedir="."> | |
<target name="validate"> | |
<xslt in="siteswaps.xml" | |
out="siteswaps_throws_expanded.xml" | |
style="expand_throws.xsl" /> | |
<xslt in="siteswaps_throws_expanded.xml" | |
out="siteswaps_catches.xml" | |
style="locate_catches.xsl" /> | |
<xslt in="siteswaps_catches.xml" | |
out="siteswaps_validated.xml" | |
style="validate_siteswap.xsl" /> | |
</target> | |
</project> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment