Render2selector.xslt

Some time ago the TEI-C created a new mechanism for indicating default rendition. It (the new @selector mechanism) became available in 2.9.1, and the old (@render) mechanism will be withdrawn in the next release (probably 3.2.0 in a month or so).

Here are two routines for converting from the old to the new. There is no use reason to have two of them, the reason for separation is purely pedagogical. Doing this conversion on a simple TEI file that has a single &lt;TEI> element and only has default renditions for elements in the TEI namespace is really easy. It only takes 2 templates (in addition to the identity transform): 1 to add @selector to &lt;rendition> and another to delete namespace.

Doing this for the general case, in which there may be multiple &lt;TEI> elements, each with multiple &lt;namespace> elements (from various namespaces), which namespaces may or may not also be in scope turns out to be very hard (IMHO), and even then I had to concede the point that there may be a prefix definition used somewhere in the file that we can’t find.

So I have left the simple version in for those who want to puzzle through what it does and how it does it. I have left the second, more complicated version in for general use, and for XSLT programmers to puzzle through and improve.

WARNING: The first version deletes &lt;namespace></tt> without checking to see if there is useful information in there or not. The more complex version is smarter about this. If you do not have any prose, but just counts and counts-with-ID in your &lt;namespace></tt>, you can easily re-generate it using Generate_tagsDecl_P5.xslt. If you actually have useful content in any of your &lt;tagUsage></tt> elements, do not use the first version.

NB: The second version tries to take into account that an @xml:id</tt> or @gi</tt> may have whitespace around the value; the first version does not.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="#all" xpath-default-namespace="http://www.tei-c.org/ns/1.0" xmlns="http://www.tei-c.org/ns/1.0" version="2.0">

<xsl:template match="node"> <xsl:if test="not(ancestor::*)"> <xsl:text>&amp;#x0A;</xsl:text> </xsl:if> <xsl:copy> <xsl:apply-templates select="@* | node"/> </xsl:copy> </xsl:template> <xsl:template match="@*"> <xsl:copy/> </xsl:template>

<xsl:template match="namespace"/>

<xsl:template match="rendition"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="selector"> <xsl:variable name="id" select="concat('#', normalize-space( @xml:id ) )"/> <xsl:value-of select="string-join( ../namespace/tagUsage[@render eq $id]/@gi, ', ')"/> </xsl:attribute> <xsl:apply-templates select="node"/> </xsl:copy> </xsl:template>

</xsl:stylesheet>

-- above: simple version, easy to read

below: works on more complex files --

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="#all" xmlns:sch="http://purl.oclc.org/dsdl/schematron" xpath-default-namespace="http://www.tei-c.org/ns/1.0" xmlns="http://www.tei-c.org/ns/1.0" version="2.0">

<xsl:key name="namespace_elements" match="/TEI/teiHeader/encodingDesc/tagsDecl/namespace" use="true"/> <xsl:key name="namespace_elements" match="/teiCorpus/TEI/teiHeader/encodingDesc/tagsDecl/namespace" use="true"/>

<xsl:variable name="NSs" as="element+"> <xsl:variable name="me" select="key('namespace_elements', true )[1]"/> <xsl:for-each select="in-scope-prefixes($me)"> <sch:ns prefix="{.}" uri="{namespace-uri-for-prefix(.,$me)}"/> </xsl:for-each> </xsl:variable>

<xsl:template match="node"> <xsl:if test="not(ancestor::*)"> <xsl:text>&amp;#x0A;</xsl:text> </xsl:if> <xsl:copy> <xsl:apply-templates select="@* | node"/> </xsl:copy> </xsl:template> <xsl:template match="@*"> <xsl:copy/> </xsl:template>

<xsl:template match="namespace[not(tagUage/@occurs | tagUsage/@withId | tagUsage/text[not(normalize-space(.) eq '')])]"/> <xsl:template match="text[ preceding-sibling::*[1][self::namespace[not(tagUage/@occurs | tagUsage/@withId | tagUsage/text[not(normalize-space(.) eq '')])]]]"/>

<xsl:template match="rendition[@xml:id][not(@selector)]"> <xsl:variable name="idrf" select="concat('#', normalize-space(@xml:id))"/> <xsl:variable name="TEI_gis" as="item*" select="key('namespace_elements', true )[normalize-space(@name) eq 'http://www.tei-c.org/ns/1.0']/tagUsage[normalize-space(@render) eq $idrf]/@gi/normalize-space"/> <xsl:variable name="other_gis" as="item*"> <xsl:for-each select="key('namespace_elements', true )[normalize-space(@name) ne 'http://www.tei-c.org/ns/1.0']"> <xsl:variable name="nsuri" select="normalize-space(@name)"/> <xsl:for-each select="tagUsage[normalize-space(@render) eq $idrf]"> <xsl:variable name="prefix"> <xsl:choose> <xsl:when test="($NSs)[@uri eq $nsuri]"> <xsl:value-of select="($NSs)[@uri eq $nsuri][1]/@prefix"/> </xsl:when> <xsl:otherwise> <xsl:variable name="temp_prefix" select="concat('ns', parent::namespace/count( preceding::namespace ) )"/> <xsl:message>Warning: using prefix '<xsl:value-of select="$temp_prefix"/>' for namespace URI <xsl:value-of select="$nsuri"/></xsl:message> <xsl:value-of select="$temp_prefix"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="concat( $prefix, '|', normalize-space( @gi ) )"/> </xsl:for-each> </xsl:for-each> </xsl:variable> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="selector"> <xsl:value-of select="string-join( distinct-values( ( $TEI_gis, $other_gis ) ), ', ')"/> </xsl:attribute> <xsl:apply-templates select="node"/> </xsl:copy> </xsl:template>

<xsl:template match="rendition[@selector]"> <xsl:message>This &lt;rendition> (#<xsl:value-of       select="          if (@xml:id) then            @xml:id          else            count(preceding::rendition) + 1"      />) already has a @selector, so I'm not going to mess with it. (Are you sure you should be running render2selector on this file?)</xsl:message> <xsl:copy> <xsl:apply-templates select="@* | node"/> </xsl:copy> </xsl:template>

</xsl:stylesheet>