xsh:call-template


The <xsl:call-template> instruction is used to invoke a named template. Its effect is analogous to a procedure call or subroutine call in other programming languages.

Changes in 2.0

There are no syntactic changes to this instruction in XSLT 2.0. However, using the <xsl:sequence> instruction in the called template (see page 420) now allows the result of the <xsl:call-template> instruction to be any sequence, not only a sequence of nodes.

In many cases where it was appropriate to use <xsl:call-template> in XSLT 1.0, it may be more appropriate in 2.0 to use an XPath expression containing a call on a stylesheet function defined using <xsl:function> , which is described on page 300.

Many problems that required recursive use of <xsl:call-template> to process a string can be solved more conveniently in XSLT 2.0 using the <xsl:analyze-string> instruction (see page 176), and many problems that used recursion to process a sequence of nodes can now be tackled more easily using <xsl:for-each- group > , described on page 281.

It is now a compile-time error to supply a parameter that the called template does not declare: In XSLT 1.0, such a parameter was silently ignored. To preserve backwards compatibility, this rule is not enforced when the stylesheet specifies «version= "1.0" » .

Format

 <xsl:call-template   name = qname>   <!-- Content: xsl:with-param* --> </xsl:call-template> 

Position

<xsl:call-template> is an instruction; it is always used within a sequence constructor.

Attributes

Name

Value

Meaning

name

mandatory

QName

The name of the template to be called

Content

Zero or more <xsl:with-param> elements.

Effect

The sections below describe the rules for the template name, the rules for supplying parameters to the called template, and the way the context is affected.

The Template Name

The mandatory name attribute must be a lexical QName, and it must match the name attribute of an <xsl:template> element in the stylesheet. If the name has a namespace prefix, the names are compared using the corresponding namespace URI in the usual way. If there is no prefix, the namespace URI is null (the default namespace is not used). It is an error if there is no <xsl:template> element with a matching name.

If there is more than one <xsl:template> in the stylesheet with a matching name, they must have different import precedence , and the one with highest import precedence is used. For information about import precedence, see <xsl:import> on page 312.

The name of the template to be called must be written explicitly in the name attribute. There is no way of writing this name as a variable or an expression to be evaluated at runtime. If you want to make a runtime decision on which of several named templates to call, the only way to achieve this is to write an <xsl:choose> instruction. Alternatively, there is a technique for using templates as if they were higher order functions: This is described under Simulating Higher Order Functions on page 198.

Parameters

If the name of a child <xsl:with-param> element matches the name of an <xsl:param> element in the called <xsl:template> , then the <xsl:with-param> element is evaluated (in the same way as an <xsl:variable> element) and the value is assigned to the relevant <xsl:param> variable name within that named template.

In XSLT 2.0 a compile-time error is reported if there is a child <xsl:with-param> element that does not match the name of any <xsl:param> element in the selected <xsl:template> . However, if the <xsl:call-template> instruction is in a part of the stylesheet that specifies «[xsl:]version="1.0" » , the extra parameter is ignored as it was in XSLT 1.0.

If there is an <xsl:param> element in the selected <xsl:template> with no matching <xsl:with-param> element in the <xsl:call-template> element, then the <xsl:param> variable is given a default value. But if the <xsl:param> element specifies «required="yes" » , this is a compile-time error. See <xsl:param> on page 392 for details.

Context

The selected <xsl:template> is evaluated with no change to the context: It uses the same context item, context position, and context size as the calling template. There is also no change to the current template rule (a concept that is used only by <xsl: apply-imports > , described on page 184, and <xsl: next -match> , described on page 355).

Usage and Examples

The <xsl:call-template> element is similar to a subroutine call in conventional programming languages, and the parameters behave in the same way as conventional call- by-value parameters. It is useful wherever there is common logic to be called from different places in the stylesheet.

Using the Result

The result of an <xsl:call-template> instruction is the sequence returned by the sequence constructor inside the template that is called. Usually, this consists of nodes that are to be added to the result tree. However, you can also capture the result by calling <xsl:call-template> from a sequence constructor enclosed within an <xsl:variable> element, in which case the result of the called template becomes the value of the variable.

For example, the following template outputs the supplied string enclosed in parentheses:

  <xsl:template name="parenthesize">   <xsl:param name="string"/>   <xsl:sequence select="concat('(',$string,')')"/>   </xsl:template>  

This may be called as follows :

  <xsl:variable name="credit-in-paren"  as="xs:string">   <xsl:call-template name="parenthesize">   <xsl:with-param name="string" select="@credit"/>   </xsl:call-template>   </xsl:variable>  

If the value of the credit attribute is «120.00 » , the resulting value of the variable «$credit-in-paren » will be the string «(120.00) » .

If you omitted the «as="xs:string" » from the <xsl:variable> element, the result would not be a string, but a temporary tree consisting of a root node that owns a single text node, and the contents of that text node would be «(120.00) » ; but for all practical purposes the value could still be used as if it were a string. One difference is that you won't get such good type checking: For example, if you try to use the variable $credit-in-paren as defined above in a context where a number is required, this will be reported as an error, quite possibly at compile time. But if you leave off the as attribute, you will probably not get an error at all, just a wrong answer: The system will treat the value of the variable as NaN (not a number).

Changing the Context Item

If you want to use <xsl:call-template> to process an item that is not the context item, the easiest way to achieve this is to nest the <xsl:call-template> inside an <xsl:for-each> instruction. An alternative, however, is to give the target template a distinctive mode name, and call it using <xsl:apply-templates> with the specified mode.

For example, suppose you have written a template that returns the depth of the current node (the number of ancestors it has). The template has been given a unique name and an identical mode name:

  <xsl:template name="depth" mode="depth" match="node()">   <xsl:sequence select="count(ancestor::node())"/>   </xsl:template>  

Now, suppose you want to obtain the depth of a node other than the current node-let's say the depth of the next node in document order, which need not be on the same level as the current node. You can call this template in either of two ways.

Using <xsl:call-template> :

  <xsl:variable name="next-depth" as="xs:integer">   <xsl:for-each select="following::node()[1]">   <xsl:call-template name="depth"/>   </xsl:for-each>   </xsl:variable>  

or using <xsl:apply-templates> with a special mode:

  <xsl:variable name="next-depth" as="xs:integer">   <xsl:apply-templates select="following::node()[1]" mode="depth"/>   </xsl:variable>  

In both cases the variable $next-depth will, on return, hold a value, which is the depth in the tree of the node following the context node. If the context item is not a node, a runtime error will occur. Because the <xsl:variable> element has an «as » attribute, the result is of type xs:integer . Without the «as » attribute, the result would be a temporary tree containing a single text node, whose value is the string representation of this integer. For details, see <xsl:variable> on page 471.

Recursion: Processing a List of Values

Named templates are sometimes used to process a list of values. As XSLT has no updateable variables like a conventional programming language, it also has no conventional for or while loop, because these constructs can only terminate if there is a control variable whose value is changing.

In XSLT 2.0 (and XPath 2.0) most processing of sequences can be done iteratively, using the XSLT <xsl:for-each> instruction or the XPath 2.0 «for » expression, for example:

  sum(for $i in //item return $i/price * $i/quantity)  

When things get difficult, it is possible to use functions such as tokenize() or distinct-values () to define the sequence that needs to be processed , and to use instructions such as <xsl:analyze-string> and <xsl:for-each-group> to do the processing. In XSLT 1.0 it was often necessary to write recursive templates to perform such calculations.

Recursion is still needed in XSLT 2.0 to handle more complex algorithms, particularly those that navigate a hierarchy or a graph, but it will often be done more conveniently using XPath function calls and stylesheet functions written using <xsl:function> rather than using <xsl:call-template> . Nevertheless, recursive use of <xsl:call-template> still has a role to play, so I will present a couple of examples.

The typical logic used to process a sequence using recursion is illustrated by the following pseudocode:

  function process-sequence(sequence L) {   if (not-empty(L)) {   process(first(L));   process-sequence(remainder(L));   }   }  

That is, the function does nothing if the sequence is empty; otherwise it processes the first item in the sequence, and then calls itself to process the sequence containing all items except the first. The net effect is that each item in the sequence will be processed and the function will then exit. This particular approach to writing recursive algorithms is often known as head-tail recursion.

There are two main kinds of sequence that this logic is applied to: sequences of nodes, and strings containing separator characters . I will show one example of each kind; more complex examples can be found in Chapters 9 and 12.

Using Recursion' to Process a Sequence of Nodes
start example

Here's an example for processing a sequence of node. XPath 2.0 provides min() and max() functions for finding the minimum and maximum of a set of atomic values, but it doesn't provide a way of processing a set of nodes and returning the one whose value for some expression is least or greatest. This can be done by computing the value of the expression for each of the nodes, passing these values into the min() or max() function, and then searching the nodes to see which of them had this value; but this approach is rather inefficient because it involves visiting each node and calculating the expression twice. So we'll do it ourselves , using a recursive scan of the nodes, in a single pass. The specific task we will tackle is to look for the longest speech in a scene of a play.

Conceptually it's trivial: The maximum value of a set of numbers is either the first number or the maximum of the set of the numbers after the first, whichever is larger. We use XPath predicates for manipulating the node sequences: in particular, «[1] » to find the first node in the sequence, and «[position()!=1] » to find the remainder.

Source

The source file scene.xml is the scene of a play. It starts like this:

  <?xml version="1.0"?>   <SCENE><TITLE>SCENE I. Venice. A street.</TITLE>   <STAGEDIR>Enter RODERIGO and IAGO</STAGEDIR>   <SPEECH>   <SPEAKER>RODERIGO</SPEAKER>   <LINE>Tush! never tell me; I take it much unkindly</LINE>   <LINE>That thou, Iago, who hast had my purse</LINE>   <LINE>As if the strings were thine, shouldst know of this.</LINE>   </SPEECH>   <SPEECH>   <SPEAKER>IAGO</SPEAKER>   <LINE>'Sblood, but you will not hear me:</LINE>   <LINE>If ever I did dream of such a matter, Abhor me.</LINE>   </SPEECH>     etc    .    </SCENE>  

Stylesheet

The stylesheet longest-speech.xsl is shown below. It starts by defining a named template «max » . This template takes a node sequence called «list » as its parameter.

The first thing it does is to test whether this node sequence is nonempty ( <xsl:when test="$list"> ). If it isn't, it gets the number of <LINE> element children of the first node in the list into a variable «$first » . Then the template calls itself recursively, passing all nodes except the first as the parameter, to determine the maximum value for the rest of the list. It then returns either the first value, or the maximum for the rest of the list, whichever is greater. Finally, if the supplied list was empty, it returns zero.

The template rule for the root node of the source document simply calls the «longest-speech » template, passing the list of all <SPEECH> elements as a parameter.

  <xsl:transform   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   exclude-result-prefixes="xs"   version="2.0"   >   <xsl:template name="longest-speech" as="element(SPEECH)?">   <xsl:param name="list" as="element(SPEECH)*"/>   <xsl:choose>   <xsl:when test="$list">   <xsl:variable name="first" select="count($list[1]/LINE)"   as="xs:integer"/>   <xsl:variable name="longest-of-rest" as="element(SPEECH)?">   <xsl:call-template name="longest-speech">   <xsl:with-param name="list"   select="$list[position()!=1]"/>   </xsl:call-template>   </xsl:variable>   <xsl:choose>   <xsl:when test="$first gt count($longest-of-rest/LINE)">   <xsl:sequence select="$list[1]"/>   </xsl:when>   <xsl:otherwise>   <xsl:sequence select="$longest-of-rest"/>   </xsl:otherwise>   </xsl:choose>   </xsl:when>   </xsl:choose>   </xsl:template>   <xsl:template match="/">   <longest-speech>   <xsl:call-template name="longest-speech">   <xsl:with-param name="list" select="//SPEECH"/>   </xsl:call-template>   </longest-speech>   </xsl:template>   </xsl:transform>  

Output

The output gives the text of the longest speech in this scene. It starts like this:

  <?xml version="1.0"   encodings"UTF-8"?>   <longest-speech>   <SPEECH><SPEAKER>IAGO</SPEAKER>   <LINE>0, sir, content you;</LINE>   <LINE>I  follow him to serve my turn upon him:</LINE>   <LINE>We  cannot all be masters, nor all masters</LINE>   <LINE>Cannot be truly follow'd. You shall mark</LINE>   <LINE>Many a duteous  and knee-crooking knave,</LINE>   <LlNE>That, doting on his own obsequious bondage,</LINE>   <LINE>Wears out his time, much like his master's ass,</LINE>   <LlNE>For nought but provender, and when he's old,   cashier'd:</LINE>   <LINE>Whip me such honest knaves...  
end example
 

Note that this is taking advantage of some of the new features of XSLT 2.0. The template uses <xsl:sequence> to return a reference to an existing node, rather than creating a copy of the node using <xsl:copy-of> . It also declares the type of the parameters expected by the template, and the type of the result, which is useful documentation, and provides information that the XSLT processor can use for generating optimized code. I also found that while I was developing the stylesheet, many of my errors were trapped by the type checking. Note that the form «as=" element(SPEECH)" » can be used even when there is no schema. The example could have been rewritten to make much heavier use of XSLT 2.0 features, for example it could have been written using <xsl:function> rather than <xsl:template> , and the <xsl:choose> instruction could have been replaced by an XPath 2.0 «if » expression. The result would have occupied fewer lines of code, but it would not necessarily have been any more readable or more efficient.

There is another solution to this problem that may be more appropriate depending on the circumstances. This involves sorting the node-set, and taking the first or last element. It goes like this:

  <xsl:variable name="longest-speech" as="element(SPEECH)?>   <xsl:for-each select="SPEECH">   <xsl:sort select="count(LINE)"/>   <xsl:if test="position()=last()">   <xsl:sequence select="."/>   </xsl:if>   </xsl:for-each>   </xsl:variable>  

In principle, the recursive solution should be faster, because it only looks at each node once, whereas sorting all the values requires more work than is strictly necessary to find the largest. In practice, though, it rather depends on how efficiently recursion is implemented in the particular processor.

Another case where recursion has traditionally been useful is processing of a list presented in the form of a string containing a list of tokens. In XSLT 2.0, most such problems can be tackled much more conveniently using the XPath 2.0 tokenize() function, which breaks a string into a sequence by using regular expressions, or by using the <xsl:analyze-string> instruction described on page 176. But although these functions are excellent at breaking a string into a sequence of substrings, they don't by themselves provide any ability to process the resulting sequence in a nonlinear way. Sometimes recursion is still the best way of tackling such problems.

Using Recursion to Process a Sequence of Strings
start example

Suppose that you want to find all the lines within a play that contain the phrase «A and B » where A and B are both names of characters in the play.

Source

There is only one line in the whole of Othello that meets these criteria. So you will need to run the stylesheet against the full play, othello.xml .

Stylesheet

The stylesheet naming-lines.xsl starts by declaring a global variable whose value is the set of names of the characters in the play, with duplicates removed and case normalized for efficiency:

  <xsl:transform   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   xmlns:local="local-functions.uri"   exclude-result-prefixes="xs local"   version="2.0"   >   <xsl:variable name="speakers" as="xs:string*"   select="for $w in distinct-values(//SPEAKER) return upper-case($w)"/>  

We'll write a function that splits a line into its words. This was hard work in XSLT 1.0, but is now much easier.

  <xsl:function name="local:split" as="xs:string*">   <xsl:param name="line" as="xs:string"/>   <xsl:sequence select="tokenize($line, '\W')"/>   </xsl:function>  

The next step is a function that tests whether a given word is the name of a character in the play:

  <xsl:function name="local:is-character" as="xs:boolean">   <xsl:param name="word" as="xs:string"/>   <xsl:sequence select="upper-case($word)=$speakers"/>   </xsl:function>  

This way of doing case-independent matching isn't really recommended, it's better to use a collation designed for the purpose, but it works with this data. Note that we are relying on the "existential" properties of the «= » operator: that is, the fact that it compares the word on the left with every string in the $speakers sequence.

Now I'll write a named template that processes a sequence of words, and looks for the phrase «A and B » where A and B are both the names of characters.

  <xsl:template name="scan-line">   <xsl:param name="words" as="xs:string*"/>   <xsl:if test="count($words) ge 3">   <xsl:if test="local:is-character($words[1]) and   lower-case($words[2]) = 'and' and   local:is-character($words[3])">   <hit>   <xsl:value-of select="$words[position()=1 to 3]" separator=" "/>   </hit>   </xsl:if>   <xsl:call-template name="scan-line">   <xsl:with-param name="words"   select="$words[position() gt 1]"/>   </xsl:call-template>   </xsl:if>   </xsl:template>  

Then comes the "main program," the template rule that matches the root node. This simply calls the named template for each <LINE> element in the document, which causes <hit>

elements to be output for all matching sequences:

  <xsl:template match="/">   <naming-lines>   <xsl:for-each select="//LINE">   <xsl:call-template name="scan-line">   <xsl:with-param name="words" selects="local:split(.)"/>   </xsl:call-template>   </xsl:for-each>   </naming-lines>   </xsl:template>   </xsl:transform>  

Output

The output is simply:

  <?xml version="1.0" encoding="UTF-8"?>   <naming-lines>   <hit>Othello and Desdemona</hit>   </naming-lines>  
end example
 

See Also

  • <xsl:apply-templates> on page 187

  • <xsl:function> on page 300

  • <xsl:param> on page 392

  • <xsl:template> on page 450

  • <xsl:with-param> on page 488




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net