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 > 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 < 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 < 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 |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] transformation of any XML, Dimitre Novatchev | Thread | RE: [xsl] A really easy (hopefully), Evan Lenz |
Re: [xsl] XSLT and XPATH test suite, G. Ken Holman | Date | [xsl] XSLT 1.1 comments, Uche Ogbuji |
Month |