RE: [xsl] A really easy (hopefully) question --the current() f unction--

Subject: RE: [xsl] A really easy (hopefully) question --the current() f unction--
From: Eric Vermetten <EVermetten@xxxxxxxxxxxxx>
Date: Sun, 11 Feb 2001 00:50:38 -0000
Evan wrote:
>Technically, you don't even need the current() function
>in XSLT, because you can always declare a variable
>bound to the current node and then reference
>it in the XPath expression.

I think there is a little more to this.
If you are inside the body (i.e. not in the XPath stuff as in
<xsl:template match="...) then there is no difference
between the current node [current()] and context node [.].
You can then easily bind this value to a variable
[<xsl:variable name="tst" select="." />]
However if you happen to call a new template
[<xsl:call-template ...] then this $tst will be out of scope,
but the current node [as current node in the nodelist] will
be the same [switches of current take place for example
with <xsl:for-each... and <xsl:tamplate match="...] *and*
can still be referenced by the current() function

-----------------------------------------------------------
This, I accidently sent to Evan and the owner-list...
(now I'm sure Tommie Usdin will not grant my request to
resent some bounced digests ...) my apologies to Tommie!
__________________________________
Evan wrote back:
> ....
>an example would be helpful.

from the XSLT REC (1 Introduction)
"When a template is instantiated, it is always instantiated
with respect to a current node and a current node list.
The current node is always a member of the current node list.
Many operations in XSLT are relative to the current node.
Only a few instructions change the current node list or the
current node (see [5 Template Rules] and [8 Repetition]);
during the instantiation of one of these instructions,
the current node list changes to a new list of nodes and
each member of this new list becomes the current node in turn;
after the instantiation of the instruction is complete,
the current node and current node list revert to what
they were before the instruction was instantiated."

[You have to read this (and the referenced stuff)
 just once or twice to understand it...]

Here's something more on the practical side:
In an app. I had to check every character.
Here I only consider characters in element content.

+++++++ first try ++++++++++++++++++++++++++++++++++
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">
<xsl:output method="html"/>

<xsl:template name="process-char">
   <xsl:param name="string"/>
   <xsl:param name="length"/>

   <xsl:if test="$length &gt; 0">
       <xsl:element name="char" >
          <!-- do something (more) usefull with your char here -->
          <xsl:value-of select="substring($string,1,1)" />
       </xsl:element>

       <xsl:variable name="tail" select="substring($string, 2)" />
           <xsl:call-template name="process-char">
              <xsl:with-param name="string" select="$tail"/>
              <xsl:with-param name="length" select="$length - 1"/>
           </xsl:call-template>
   </xsl:if>
</xsl:template>

<xsl:template match="text()">
  <xsl:variable name="string" select="."/>
  <xsl:variable name="length" select="string-length($string)"/>

  <xsl:call-template name="process-char">
     <xsl:with-param name="string" select="$string"/>
     <xsl:with-param name="length" select="$length"/>
  </xsl:call-template>
</xsl:template>
</xsl:stylesheet>

Here you "drag" the better part of the total content of
the matched text node (a string) from one recursive call
to the next (on average: 0.5 * string length of the text node and
that string-length times [i.e. # of recursive calls]).
You don't want to stroll leisurely through the characters of an
XML document, but you want to process them with some speed
(preferably warp 9.9 or so).

However:
*) you cannot assign the text node of each match to
   a global variable at the upper level (the level of the
   <xsl:template ...> instructions)  for obvious reasons.
*) you cannot assign the value of the matched
   text node to a variable ($local-var) (at [*])
   because referenced  at [**] it will be out off scope:

+++++++ second try ++++++++++++++++++++++++++++++++++
        <!-- this doesn't work -->
<xsl:template name="process-char">
   <xsl:param name="index"/>

   <xsl:if test="$index &lt; string-length($local-var) + 1">
       <xsl:element name="char" >
         <xsl:value-of select="substring($local-var, $index, 1)" />
                     <!-- [**] out off scope -->
       </xsl:element>

           <xsl:call-template name="process-char">
              <xsl:with-param name="index" select="$index + 1"/>
           </xsl:call-template>
   </xsl:if>
</xsl:template>

<xsl:template match="text()">
  <xsl:variable name="local-var" select="." />

  <xsl:call-template name="process-char">
     <xsl:with-param name="index" select="1"/>
  </xsl:call-template>
</xsl:template>

So I looked for guidance in Mike Kay's book. There is an excellent
explanation of the difference between context node and current node
(p. 439) (i.e. context node used in an XPath expression versus
current node of an xsl:for-each) but that was not what was needed
here.
So I found "using string recursion to process a string" (p.173) but
that also used a parameter containing the tail of the string just
like the first try. The XSLT rec talks about nodelists and of
the current node that is being processed of a nodelist (see opening
quote), all of which was pretty unclear to me until then.
For the problem at hand, however, this opens up new possibilities:
(what was proces-char is now forward, bare with me)

+++++++ third try ++++++++++++++++++++++++++++++++++
<xsl:template name="forward">
   <xsl:param name="index" />

   <xsl:if test="$index &lt; string-length(current())  + 1">
       <xsl:element name="char" >
          <xsl:value-of
               select="substring(current(),$index,1)" />
       </xsl:element>

        <xsl:call-template name="forward">
           <xsl:with-param name="index" select="$index + 1" />
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template match="text()">
   <xsl:call-template name="forward">
      <xsl:with-param name="index" select="1"/>
   </xsl:call-template>
</xsl:template>

For every match of a text node, that text node becomes the
current node. With the call of forward (<xsl:call-template ...>)
the current node is *not* changed.Therefore it can be used to
reference the content of the text node.

A small list:  (Jeni T. has given a broader perspective :))
Change of current node:
<xsl:template ...>
<xsl:for-each ...>

NO change of current node:
<xsl:call-template ...>

Now we have to get rid of that string-length function
within the recursion (although XSLT processors could
optimize here):
(now the string of the text node is "recursed" in reverse order
instead of forward [characters do come out as in the XML doc])

+++++++ final ++++++++++++++++++++++++++++++++++
<xsl:template name="reverse">
   <xsl:param name="index" />

    <xsl:if test="$index != 0">
        <xsl:call-template name="reverse">
           <xsl:with-param name="index" select="$index - 1" />
        </xsl:call-template>

       <xsl:element name="char" >
          <xsl:value-of select="substring(current(), $index, 1)" />
       </xsl:element>
    </xsl:if>
</xsl:template>

<xsl:template match="text()">
   <xsl:call-template name="reverse">
      <xsl:with-param name="index" select="string-length(.)"/>
   </xsl:call-template>
</xsl:template>

This is as fast as XSLT is going to take us,
not really warp 9.9, but since we have arrived at the Borg era
("ISO/IEC 10646 is the Borg of character set standards"
   Charles Goldfarb, XML handbook 2nd ed. p808)
and with the dawn of transwarp,
warpspeed isn't what it used to be anyway.

<!-- ++++ bringing it all together +++++-->
I think string processing is probably the only stuff where you
can gain time in this way. If, instead of a string, you select a
nodeset to pass along as parameter i.e. //chapter (all your chapters
of your favourite book) then in all probability only references
to those nodes are passed. Typically, I would say there aren't that
many chapters around as characters...
Until now we've been saving time with a complete text() node, but
sometimes you want only a part of it and well, those good old comma
seperated lists keep reapering... So here's my last example (keep in
mind that, as stated earlier <xsl:for-each.. *does* change the current
node)

<!--++++____ t.xsl ____++++-->
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:msxml="urn:schemas-microsoft-com:xslt"
version="1.0">
<xsl:output method="html"/>

<xsl:template name="reverse">
   <xsl:param name="index" />

    <xsl:if test="$index != 0">
        <xsl:call-template name="reverse">
           <xsl:with-param name="index" select="$index - 1" />
        </xsl:call-template>

       <xsl:element name="char" >
          <xsl:value-of select="substring(current(), $index, 1)" />
       </xsl:element>
    </xsl:if>
</xsl:template>

<xsl:template match="a">
<xsl:variable name="string-part" select="substring-after(., ',')" />

    <!--<xsl:for-each select="msxml:node-set($string-part)" >-->
       <xsl:call-template name="reverse">
          <xsl:with-param name="index" select="string-length(.)"/>
       </xsl:call-template>
    <!--</xsl:for-each>-->
</xsl:template>
</xsl:stylesheet>

<!--++++____ t.xml ____++++-->
<?xml version="1.0"?>
<wrap><a>01,blup</a></wrap>

Now try and see what happens when you activate 
the <xsl:for-each...

[you can see why node-sets in general (and those generated
by the node-set function) play such a crucial role.
Without the node-set function you just get what is called
a result tree fragment(RTF). You can do either of two things 
with that:
1) write them as output or 
2) process them as strings [very very cumbersome]
This is why XSLT v1.1 will convert RTF's automatically to
node-sets. Do not underestimate the power of node sets.
With this you can create a variable turn it into a node
set and reuse it, enabling you to build complete data 
structures all in one and the same XSLT stylesheet.]

Enjoy and 
Kapla, (or whatever Klingons say when they part)
Eric

<!--++++++++++++++++++++-->
Small question:
This leaves me wondering: isn't there something about
recursive string processing with current() in Mike's book
or, --equally possible-- did I miss it?

Question:
see new thread: "recursion troubles  (Xalan, Saxon, msxml, xt)"

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread