Re: Understanding difficulties with call-template

Subject: Re: Understanding difficulties with call-template
From: Mike Brown <mike@xxxxxxxx>
Date: Wed, 25 Oct 2000 17:50:14 -0600 (MDT)
robert@xxxxxxxxxxxx wrote:
> I have the following code
> 
> <xsl:template name="comma-block">
> <xsl:for-each select=".">
>         <xsl:value-of select="."/>
> <xsl:if test="not(position()=last())">, </xsl:if>
> </xsl:for-each>
> </xsl:template>
> 
> <xsl:template match="preferred-locations">
> <xsl:element name="p">
> Preferred Locations:
> <xsl:call-template name="comma-block"/>
> </xsl:element>
> </xsl:template>
> 
> now when I call the template it correct outputs the correct nodes but
> the xsl:if test doesn't work  .... position==last for all nodes.
> 
> When I inline the above code instead of using call-template the
> xsl:if test works as expected.

You are probably assuming that the current node list is defined by the
template match pattern. Sections 1 (starting around "A template is
instantiated for...") and 5.1 (one paragraph) of the XSLT spec explain the
actual processing model.

The node-set that has been selected for processing is the current node
list. There is always a current node list, and from that list there is
always a current node being processed. You don't have direct access to the
list, but you can get some information about it via functions like
position(), last(), and count(). You do have access to the current node
('.' in your patterns and expressions).

The current node list starts out as being just the root node. The best
matching template for that node is instantiated, and processing ends. For
further node processing to occur, the template must contain an
xsl:apply-templates or xsl:for-each instruction, either of which will
select a new set of nodes for processing. In the case of apply-templates,
the best matching template for each node is determined, and the
instructions therein are executed. In the case of for-each, the content of
the for-each element is the template used for each node.

Each selected node is processed one by one, each becoming the current node
while the best template is instantiated, and the current node list staying
the same (the set that was originally selected). When you do a
call-template, variables from the calling-template go out of scope, but
the current node and current node list stay the same.

Somehow you are getting to a point where you have at least one
preferred-locations element being processed. How are you getting there?
Elsehwere in your stylesheet there must be an xsl:apply-templates that is
selecting preferred-locations elements. It is there that the current node
list is being established. It is relative to this list that position() and
last() will operate outside of that xsl:for-each in your named template.
Inside the xsl:for-each, you've resent the current node list to the
current node only, so position() and last() are both going to be 1 no
matter how many times you call that template.

At the point where you are selecting the preferred-locations elements for
processing, you'll want to put

<xsl:element name="p">
  <xsl:text>Preferred Locations: </xsl:text>
  <xsl:call-template name="comma-block">
    <xsl:with-param name="nodes" select="preferred-locations"/>
  </xsl:call-template>
</xsl:element>

and then have

<xsl:template name="comma-block">
  <xsl:param name="nodes"/>
  <xsl:for-each select="$nodes">
    <xsl:value-of select="."/>
    <xsl:if test="position() != last()">, </xsl:if>
  </xsl:for-each>
</xsl:template>

There is no need for a template to match the preferred-locations elements
because you're using the contents of the for-each as the template for
them.


   - Mike
____________________________________________________________________
Mike J. Brown, software engineer at         My XML/XSL resources:
webb.net in Denver, Colorado, USA           http://www.skew.org/xml/


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


Current Thread