This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: lookup-table thoughts (was Re: matching multiple times, outputting once?
- To: xsl-list at lists dot mulberrytech dot com
- Subject: [xsl] Re: lookup-table thoughts (was Re: matching multiple times, outputting once?
- From: Tom Myers <tommy at cs dot colgate dot edu>
- Date: Tue, 06 Nov 2001 20:47:59 -0500
- Reply-To: xsl-list at lists dot mulberrytech dot com
I'm still basically hoping that somebody will say something
to contribute to my education, and I see that since I started
with Dimitre Novatchev's solution to Marty McKeever's problem
rather than with the problem itself, I was thinking backwards.
Sorry. Okay, we want to go from
----------------------------------------------
<contents>
<emphasis bold="yes">Hello</emphasis>
<emphasis italic="yes" bold="yes">Hello</emphasis>
<emphasis underline="yes" italic="yes" bold="yes">Hello</emphasis>
</contents>
----------------------------------------------
to
----------------------------------------------
<b>Hello</b>
<i><b>Hello</b></i>
<u><i><b>Hello</b></i></u>
----------------------------------------------
rather than the other way around, and I really liked Jim Fuller's
<xsl:template match="*[@bold='Yes' ]"> ...
<xsl:template match="*[@bold='Yes' and @italic='Yes' ]"> ...
<xsl:template match="*[@bold='Yes' and @italic='Yes' and @underline='Yes' ]">...
except that you really need six of those, and in general the number of
templates will be N! where N is the number of options...at least I
assume that Marty's three examples were not intended to exclude the
possibility of <emphasis italic="Yes">Hello</emphasis>, which
presumably has the output <i>Hello</i> and which therefore for
Jim requires a separate rule. And so I think that a recursive
solution really is better here. (Unless I'm missing something,
again.)
Still, I'd like to propose a variation on the Kay/Novatchev
solutions. My version is longer, but I think that I think
that coming up with it requires less original thought than
either of theirs, because rather than writing a specific
recursion I'm editting a general version. For instance, I'm going
to keep the bold -> b, italic -> i, underline -> u mapping
in a data table, so even though it requires several lines it
requires no thought, 'cos I can start out a stylesheet like so,
just editting the data from my backwards-solution post or from
any similar lookup-table-based stylesheet:
----------------------------------------------
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:trans="myTrans" exclude-result-prefixes="trans"
>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<trans:trans>
<in>bold</in> <out>b</out>
<in>italic</in> <out>i</out>
<in>underline</in> <out>u</out>
</trans:trans>
<xsl:key name="transkey" match="out" use="preceding-sibling::in[1]"/>
<xsl:template name="lookup-trans">
<xsl:param name="in" select="''"/>
<xsl:for-each select="document('')"> <!-- or whatever doc holds table -->
<xsl:value-of select="key('transkey',$in)"/>
</xsl:for-each>
</xsl:template>
----------------------------------------------
Okay, up to here I have really said no more than that
bold -> b, italic -> i, and underline -> u
while preserving the option of adding more later, including some that
might share initial letters. Now, for the most part, I'll copy input to
output (at least for a test) so it takes no thought at all to bring in
----------------------------------------------
<xsl:template match="@node()|@*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
----------------------------------------------
Now, I want to override that for "emphasis" elements; I want to
do something to "emphasis" elements that will run through the
sequence of their attributes, using child nodes as base. I'm
thinking of a functional-programming pattern
accum(f,[],base) = base
accum(f,[hd]+tail,base) = f(hd,accum(f,tail,base));
(the sum of a sequence S is accum(add,S,0); the product is
accum(multiply,S,1); the minimum is accum(min,S,+infinity);
and so on. Whatever. I'm not actually reusing code here, but
I am re-using a pattern for the umpteenth time since learning
it from _The Little Lisper_ many long years ago. So I copy
in the pattern, matching "emphasis" nodes with a call on "accum"
and just leaving two blank slots for filling in:
----------------------------------------------
<xsl:template match="emphasis">
<xsl:call-template name="accum">
<xsl:with-param name="seq" select="@*"/>
<xsl:with-param name="base" select="node()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="accum">
<xsl:param name="seq" select="''"/>
<xsl:param name="base" select="''"/>
<xsl:choose>
<xsl:when test="$seq">
<xsl:variable name="hdvalue">
--- something depending only on $seq[1] ----
</xsl:call-template>
</xsl:variable>
<xsl:variable name="recresult">
<xsl:call-template name="accum">
<xsl:with-param name="seq" select="$seq[position()!=1]"/>
<xsl:with-param name="base" select="$base"/>
</xsl:call-template>
</xsl:variable>
--- output depending only on $hdvalue and $recresult --
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$base"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
----------------------------------------------
As you can see, there are just two slots to be filled in;
what to do with each $seq value, and how to put hdvalue
and recresult together. Here I fill them in:
----------------------------------------------
<xsl:template name="accum">
<xsl:param name="seq" select="''"/>
<xsl:param name="base" select="''"/>
<xsl:choose>
<xsl:when test="$seq">
<xsl:variable name="hdvalue">
<xsl:call-template name="lookup-trans">
<xsl:with-param name="in" select="name($seq[1])"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="recresult">
<xsl:call-template name="accum">
<xsl:with-param name="seq" select="$seq[position()!=1]"/>
<xsl:with-param name="base" select="$base"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="{$hdvalue}">
<xsl:copy-of select="$recresult"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$base"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
----------------------------------------------
And that seems to handle the problem...unless I'm missing something.
It's interesting, I think, that even when filled in, the accumulator
pattern has no idea that the nodelist is a list of attributes...and
info about the mapping of names is confined to the lookup table. Of
course you can make it a little shorter and faster in various ways,
e.g. by bringing the call-template that defines "recresult" into
the one and only place $recresult is used, avoiding the copy-of.
But mainly it's interesting that so little thought is required.
And I'm wondering what I'm missing this time. Oh well, back to
data bases.
Tom Myers
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list