Wendell Piez The original question went like this.
I have a set of combinations for various renderings of <title> according
to attributes and XPath position that handle titles through in my
articles and other documents and so forth that I write using TEI
(present code shown below).
I now want to add one more universal style application that should also
apply in other attribute situations when necessary: I want to apply the TITUS font
to all cases of <title lang="sa"> (Language = Sanskrit). I can't add
another <xsl:template match="title">, and I can't simply add it as an
choose/if/when option, since it is not simply an option, but a
document-wide application. I guess there must be a way of doing this.
<xsl:template match="title">
<xsl:choose>
<xsl:when test="ancestor::listBibl">
<xsl:choose>
<xsl:when test="@level='m'">
<span style="font-style:italic">
<xsl:apply-templates/>. </span>
</xsl:when>
<xsl:when test="@level='j'">
<span style="font-style:italic">
<xsl:apply-templates/>.
</span>
</xsl:when>
<xsl:when test="@level='a'">
"<xsl:apply-templates/>."
</xsl:when>
<xsl:when test="@level='u'">
"<xsl:apply-templates/>."
</xsl:when>
<xsl:when test="@rend='bold'">
<span style="font-weight:bold"><xsl:apply-templates/></span>
</xsl:when>
<xsl:otherwise>
<span style="font-style:italic"><xsl:apply-templates/></span>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@level='m'">
<span style="font-style:italic"><xsl:apply-templates/></span>
</xsl:when>
<xsl:when test="@level='j'">
<span style="font-style:italic">
<xsl:apply-templates/></span>
</xsl:when>
<xsl:when test="@level='a'">
"<xsl:apply-templates/>"
</xsl:when>
<xsl:when test="@level='u'">
"<xsl:apply-templates/>"
</xsl:when>
<xsl:otherwise>
<span style="font-style:italic"><xsl:apply-templates/></span>
</xsl:otherwise>
</xsl:choose></xsl:otherwise>
</xsl:choose>
</xsl:template>
Wendell came back with I actually think Chuck's Sanskrit spanning is a good use case. The only
reason it's not fully clear yet is that he didn't provide a sample of input
data. If you put this bit of input together with the code he provided in
the thread, you'll get the idea:
<biblStruct id="Wayman-1984">
<monogr>
<author>Wayman, Alex</author>
<title level="m" lang="sa">The Sarvarahasyatantra</title>
<imprint>...</imprint>
</monogr>
<series>
<title level="s">Acta Indologica</title>
</series>
</biblStruct>
The problem is to get the title with @lang="sa" into an extra span in the
output (over and above what you get for being a title[@level="m"]).
Discriminate two templates that you will use to handle your titles. One
will handle their basic output based on their @level or @rend (what you
already have); the other will provide the extra span wrapper around the
Sanskrit ones.
To do this we first have to determine which will be the "outer" template.
In your case, I think it's the one that will fire based on the setting of
@lang. Your <span style="font-family: 'Titus'"> will wrap the spans and
such you're already generating.
To achieve this we put it in a named template:
<xsl:template name="Sanskrit-wrap">
<xsl:choose>
<xsl:when test="@lang = 'sa'">
<span style="font-family: 'Titus'">
<xsl:apply-templates/>
</span>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
But we don't want just to fire this -- we want it to apply not to the
title, but to the result of processing our other template. We do this by
extending the new template so it does not simply process down the tree as
normally, but instead wraps a parameter we send it:
<xsl:template name="Sanskrit-wrap">
<xsl:param name="contents">
<xsl:apply-templates/>
</xsl:param>
<xsl:choose>
<xsl:when test="@lang = 'sa'">
<span style="font-family: 'Titus'">
<xsl:copy-of select="$contents"/>
</span>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$contents"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Notice we provide our parameter with a default, namel the same
result-tree-fragment we get from applying templates to the children of the
current node -- this will come in handy. Now we have to fix our first template so it calls the second -- the trick
here is, now we have a way to get our result *into* the Sanskrit wrapper --
<xsl:template match="title">
<xsl:call-template name="Sanskrit-wrap">
<xsl:with-param name="contents">
<xsl:choose>
<xsl:when test="ancestor::listBibl">
<xsl:choose>
<xsl:when test="@level='m'">
<span style="font-style:italic"><xsl:apply-templates/>.
</span>
</xsl:when>
<xsl:when test="@level='j'">
<span style="font-style:italic"><xsl:apply-templates/>.
</span>
</xsl:when>
<xsl:when
test="@level='a'">"<xsl:apply-templates/>."</xsl:when>
<xsl:when
test="@level='u'">"<xsl:apply-templates/>."</xsl:when>
<xsl:when test="@rend='bold'">
<span style="font-weight:bold"><xsl:apply-templates/></span>
</xsl:when>
<xsl:otherwise>
<span
style="font-style:italic"><xsl:apply-templates/></span>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@level='m'">
<span
style="font-style:italic"><xsl:apply-templates/></span>
</xsl:when>
<xsl:when test="@level='j'">
<span
style="font-style:italic"><xsl:apply-templates/></span>
</xsl:when>
<xsl:when
test="@level='a'">"<xsl:apply-templates/>"</xsl:when>
<xsl:when
test="@level='u'">"<xsl:apply-templates/>"</xsl:when>
<xsl:otherwise>
<span
style="font-style:italic"><xsl:apply-templates/></span>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
I haven't figured out what to call this technique, although I'm finding
lots of uses for it. Note that you can also wrap other processing results
in <span style="font-family: 'Titus'"> simply by sending in other
parameters to the same template -- or none, since it defaults to
xsl:apply-templates, which is what you usually want.
So imagine, if you will:
<xsl:template match="title">
<xsl:call-template name="Sanskrit-wrap">
<xsl:with-param name="contents">
<xsl:choose>
<xsl:when test="ancestor::listBibl">
<xsl:choose>
<xsl:when test="@level='m' or @level='j'">
<xsl:call-template name="italicize">
<xsl:with-param name="contents">
<xsl:apply-templates/>. <xsl:text/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:when test="@level='a' or @level='u'">
<xsl:call-template name="quote">
<xsl:with-param name="contents">
<xsl:apply-templates/>.<xsl:text/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:when test="@rend='bold'">
<xsl:call-template name="embolden"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="italicize"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@level='a' or @level='u'">
<xsl:call-template name="quote"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="italicize"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Jeni adds Wendell wrote:
> Discriminate two templates that you will use to handle your titles.
> One will handle their basic output based on their @level or @rend
> (what you already have); the other will provide the extra span
> wrapper around the Sanskrit ones.
For interest, XSLT 2.0 provides a really neat way of dealing with this
kind of problem: using <xsl:next-match>. Basically what
<xsl:next-match> says is "find the next best matching template, apply
that to the current node, and insert the result here".
So in this case, you would have one template with a high priority that
recognised the Sanskrit titles, created the <span> and put the result
of using the next match inside the <span>:
<xsl:template match="title[@lang = 'sa']" priority="4">
<span style="font-family: 'Titus'">
<xsl:next-match />
</span>
</xsl:template>
Then you could have another set of templates, at a lower priority,
that would deal with the formatting based on the level and rend
attributes. Note that because <title> elements without a lang
attribute with the value 'sa' wouldn't match the above template,
they'd fall immediately through to this template instead.
<xsl:template match="title" priority="3">
<xsl:choose>
<xsl:when test="@level = ('m', 'j')">
<span style="font-style:italic"><xsl:next-match /></span>
</xsl:when>
<xsl:when test="@level = ('a', 'u')">"<xsl:next-match />"</xsl:when>
<xsl:when test="@rend = 'bold'">
<span style="font-weight:bold"><xsl:next-match /></span>
</xsl:when>
<xsl:otherwise>
<span style="font-style:italic"><xsl:next-match /></span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
and finally have a level that deals with those <title> elements within
<listBibl> elements, which need to have a . put after their values:
<xsl:template match="listBible//title" priority="2">
<xsl:next-match /><xsl:text>.</xsl:text>
</xsl:template>
When there aren't any matching templates in the stylesheet itself, the
built-in template gets used instead.
You can do the same kind of thing in XSLT 1.0 using
<xsl:apply-imports>, but you have to split the different layers of
processing into different stylesheets (which imports the stylesheet
containing the templates with the next highest priority), which can be
a real pain. Or the other thing I'd do here in XSLT 1.0 is to use modes: <xsl:template match="title[@lang = 'sa']">
<span style="font-family: 'Titus'">
<xsl:apply-templates select="." mode="style" />
</span>
</xsl:template>
<xsl:template match="title">
<xsl:apply-templates select="." mode="style" />
</xsl:template>
<xsl:template match="title" mode="style">
<xsl:choose>
<xsl:when test="@level = 'm' or @level = 'j'"> *** 1
<span style="font-style:italic">
<xsl:apply-templates select="." mode="listBibl" />
</span>
</xsl:when>
<xsl:when test="@level = 'a' or @level = 'u'">
<xsl:text>"</xsl:text>
<xsl:apply-templates select="." mode="listBibl" />
<xsl:text>"</xsl:text>
</xsl:when>
<xsl:when test="@rend = 'bold'">
<span style="font-weight:bold">
<xsl:apply-templates select="." mode="listBibl" />
</span>
</xsl:when>
<xsl:otherwise>
<span style="font-style:italic">
<xsl:apply-templates select="." mode="listBibl" />
</span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="listBible//title" mode="listBibl">
<xsl:apply-templates /><xsl:text>.</xsl:text>
</xsl:template>
<xsl:template match="title" mode="listBibl">
<xsl:apply-templates />
</xsl:template>
Note *** 1:
if one of the operands of the general
comparison operators (=, !=, < or >) is a sequence then you get true
if any of the comparisons between the the items in that sequence and
the other operand are true. Now that you can create sequences of strings, numbers etc. then it's a
lot easier to do: @level = ('m', 'j', ...)
rather than: @level = 'm' or @level = 'j' or ...
|