This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: lookup-table thoughts (was Re: matching multiple times, outputting once?


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]