Wattle Software - producers of XMLwriter XML editor
 Home | Site Map 
XMLwriter
 Screenshots
 Features
 About Latest Version
 Awards & Reviews
 User Comments
 Customers
Download
 Download XMLwriter
 Download Plug-ins
 Download Help Manual
 Downloading FAQ
Buy
 Buy XMLwriter
 Pricing
 Upgrading
 Sales Support
 Sales FAQ
Support
 Sales Support
 Technical Support
 Submit a Bug Report
 Feedback & Requests
 Technical FAQ
Resources
 XML Links
 XML Training
 XMLwriter User Tools
 The XML Guide
 XML Book Samples
Wattle Software
 About Us
 Contact Details
Professional Visual Basic 6 XML

Buy this book

Back Contents Next

eval and script

First of all a warning: be careful – the eval and script elements are not specified in the XSLT recommendation. They are Microsoft extensions, available only in their MSXML implementation. So, if you want to be able to switch XSLT implementations later on, do not use these features.

 

Still, the elements are so convenient that we will cover them here. After all, most Visual Basic programmers will be using the MSXML library anyway. It is the most widely available implementation on the Windows platform. Microsoft has stated that they will continue support for the eval and script elements in later versions, so if you plan to stick with Microsoft, there is no reason for not using eval.

 

So what is it then? The eval element allows you to generate a text node in the destination document using script. If you have created Active Server Pages or Windows Scripting Host scripts before, you will be familiar with the ActiveX Scripting Engine. This is a generic scripting platform that several languages can be plugged into. Standard available scripting languages are VBScript and JScript.

 

The script node can hold a piece of script that can hold function definitions. These can be called from the eval element.

 

This way you can do something like this:

 

<xsl:template match="TEMP[@scale='F']">

  <xsl:eval language="VBScript">

      Celsius(this.getAttribute('value'))

  </xsl:eval>

</xsl:template>

 

<xsl:script language="VBScript"><![CDATA[

  Function Celsius(fDegrees)

    Celsius = (fDegrees - 32) * 5 / 9

  End Function

  ]]>

</xsl:script>

 

If this template is used on an XML element like this:

 

<TEMP scale="F" value="80"/>

 

It would generate this in the output document:

 

26.66666667

 

Using this extension, more complex calculations can be placed inside the script block. As scripting languages will sometimes contain characters that are not allowable in XML, it is good practice to place the script blocks in CDATA nodes.

 

The eval element contains a function call to the Celsius function in the script element. The return value of the function is outputted by the eval element to the destination document. Note the use of this to refer to the context node.

 

Functions defined inside a script element can be called from an eval element, but also from attributes that are evaluated as an expression (see for example the if element in the section "Control of Flow").

Commands

apply-templates

The xsl:apply-templates element is the most typical example of a command element (an element that starts the processing of a node). Because it is so important, we have already covered it before. However, there are more ways to invoke another template.

apply-imports

When we described the import element, we saw that sometimes the main XSLT document contains templates that match the same XPath expression as one of the templates in the imported document. In these cases, the template in the main document overrides the imported template. Using this feature, XSLT authors can create new transformations based on existing ones, extending them with new templates or changing existing templates. If you are familiar with object-oriented design, you will like this idea.

 

If you are overruling a template in your document, you will often want to invoke the original template from your newer implementation. Let's look at an example.

 

The source document contains data about books and the data about a book's author is always coded in an AUTHOR tag. A typical AUTHOR tag looks like this:

 

<AUTHOR firstname="Teun" lastname="Duynstee" initials="L.W.A." nobelprize="no"/>

 

Several different XSLT transformations exist in our organization, all dealing with book information. Some repeating transformations were put together in a stylesheet that is frequently imported into other XSLT documents. It looks like this:

 

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!—- many other templates -->

 

<xsl:template match="AUTHOR">

   <xsl:value-of match="@firstname"/>

   <xsl:text> </xsl:text>

   <xsl:value-of match="@lastname"/>

</xsl:template>

 

</xsl:stylesheet>

 

The template transforms any matched AUTHOR attribute to a text node consisting of the firstname attribute joined to the lastname attribute by a single space. In our sample source, the output would be something like:

 

Teun Duynstee

 

Now we want to write an XSLT document that will transform XML data about a book into an HTML document. In the HTML document, the author's full name should appear, formatted in italics. We already have a transformation rule that creates the full name of the author, but it does no formatting. We can now import the standard author transformation and modify it slightly in our overriding template:

 

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:import href="booklib.xsl"/>

 

<xsl:template match="AUTHOR">

   <I><xsl:apply-import/></I>

</xsl:template>

 

</xsl:stylesheet>

 

The new template just specifies the literal element I that will cause italic fonts in HTML, and inside the I element, it calls the template which it overrules from the import. The result would be this:

 

<I>Teun Duynstee</I>

call-template

The call-template element is another element that can be used to organize the templates in your document. For calling a template with call-template, the template must have a name attribute. It works like apply-templates, but there is no change of context node. In fact call-template is very much like calling a function in a procedural programming language. Here's an example:

 

<xsl:template name="fullname output">

  <xsl:value-of match="@firstname"/>

  <xsl:text> </xsl:text>

  <xsl:value-of match="@lastname"/>

</xsl:template>

 

<xsl:template match="AUTHOR[@nobelprize='no']">

  <xsl:call-template name="fullname output"/>

</xsl:template>

 

<xsl:template match="AUTHOR[@nobelprize='yes']">

  <B>

  <xsl:call-template name="fullname output"/>

  </B>

</xsl:template>

 

The two templates for AUTHOR elements (for Nobel prize winners and others) both call the same template for formatting the attributes into a full name string.

 

If a template is called by name the match attribute is ignored and vice versa.

What if Several Templates Match?

If the XSLT processor is searching for a template to use for transforming a certain node, it's possible that it will find several templates that match (and are of the same mode – check the coming section on 'Modes'). The XSLT specification defines a set of rules to determine which template is the most appropriate. These rules are quite complex, but the general idea is not so hard to understand. In order of increasing importance:

 

An imported template is less important than one associated with the document (i.e. not imported).

The template imported later is more important than one imported earlier.

A high priority template is more important than a low priority template.

A more specific match attribute is more important than a more general matching expression.

 

When the processor starts searching for an appropriate template, it first creates a set of all available templates. From this set, it removes all templates that don't match. After that it removes templates that have a lower import precedence than others. When several documents are imported, and especially when imports are nested, things can get rather complicated. What the processor does is to build a tree of imported documents. Suppose Document A imports B and C (in that order), B imports D and C imports E, the tree would look like this:

 

 

 

Note how Document B is lower in the tree than Document C. This is because imports that occur later in the document prevail over imports done before. The higher in the tree a document is (in an absolute sense, not in a hierarchical sense), the higher the import precedence. The built-in templates are treated as if they are imported at the very beginning of the main document and therefore have the lowest priority of all.

 

If several templates remain (i.e. there is more than one matching template in the same document), the template with the highest priority is chosen. The priority of a template can be set using the priority attribute on the template element. This attribute can have any numeric value, both positive and negative. If the priority attribute is not set, a default priority is calculated for the template. This default priority depends on the match attribute. It is always a value between –0.5 and 0.5. Priority values are assigned as follows:

 

Kind of Pattern

Examples

Priority

Specified name along the child or attribute axis

child::PARA

TITLE

@fullname

0

Unspecified name along the child or attribute axis

*

attribute::*

-0.25

Only a node test

node()

text()

processing-instruction()

-0.5

All other cases

PARA/LINE

preceding::*

*[@*='yes']

0.5

 

If several templates exist in the same document with the same priority it causes an error, but most XSLT processors will not stall but will instead pick the one nearest to the bottom of the document. This happens quite often. In many cases, developers use no priority attributes. Because many matching patterns cause a priority of 0.5, templates with the same priority will no doubt occur.

Modes

If you need to have some templates used in some special cases, but not always, you can try using different modes. The mode attribute is an optional attribute on both the template and apply-templates elements. By calling apply-templates with the mode attribute set, you instruct the processor to use only templates that have the same mode attribute. This way, you can create several sets of templates that don't interfere with each other. Each mode also has its own built-in templates.

 

Looking at the code sample below, we have two templates now that both match on AUTHOR, but one is in the default mode and the other one in the formal mode:

 

<xsl:template match="AUTHOR">

  <xsl:value-of match="@firstname"/>

  <xsl:text> </xsl:text>

  <xsl:value-of match="@lastname"/>

</xsl:template>

 

<xsl:template match="AUTHOR" mode="formal">

  <xsl:value-of match="@initials"/>

  <xsl:text> </xsl:text>

  <xsl:value-of match="@lastname"/>

</xsl:template>

 

<xsl:template match="BOOK[@target-audience='management']">

  Authors:

  <xsl:apply-templates select="AUTHOR" mode="formal"/>

</xsl:template>

 

In formal mode, the name of the author is generated in a more formal format (using initials instead of first name). The template that processes the content of BOOK elements that have their target-audience attribute set to 'management' decides to display the authors' names in a formal way using the 'formal' mode. Presumably, other templates invoke the default templates for AUTHOR. Removing all templates with another mode attribute from the set happens before anything else.

The IE5 Case

When IE5 was released, the XSLT specification was far from ready, so the implementation shipped in IE5 does not conform to the recommendation, more on that in the section "The IE5 Implementation". One word of caution though should be mentioned here. The MSXML library in IE5 does not implement any of the rules for assessing priorities between templates. The strategy followed by the IE5 XSLT engine is shockingly simple – it tries to find a matching template in the document from the bottom upwards. As soon as it finds one, it stops. This means that templates placed lower in the document automatically have a higher priority. For example, if you had this code:

 

<xsl:template match="AUTHOR">

  <I><xsl:value-of select="@name"/></I>

</xsl:template>

 

<xsl:template match="*">

  <xsl:apply-templates/>

</xsl:template>

 

IE5 will never use the first template, because it will always find and use the lower one, which matches AUTHOR elements just as well as the first template. Be aware of this behavior when developing for IE5.

message

A very special and not very commonly used element is the message element. It is designed to give stylesheet authors the opportunity to issue warnings and error messages at certain times. Only one attribute can be specified: terminate. If terminate is set to yes, the processor stops after issuing the message, if it is set to no, the processor will attempt to continue.

 

The specification does not specify what a processor should do when such a message is issued. Possibilities could include raising errors, displaying message boxes, or writing to a log file. The command line based processors mentioned before (SAXON and XT) will generate a message on the console, while the developer's preview of MSXML does not yet implement xsl:message.

 

The content of the message element will be the content of the issued message. This example will display a warning whenever the stylesheet is used on a PET element.

 

<xsl:template match="PET">

  <xsl:message>

    Warning: Displaying information on pets in not yet implemented

  </xsl:message>

</xsl:template>

Control of Flow

Like most programming languages, XSLT includes a few keywords to let you control the flow of the processing. These include if/then construct, while loops and some extras. Like anything in XSLT, these features are implemented as XML elements.

if

The if element does one of the most common things in programming – checking a condition and executing an action if the condition is met. A simple example is:

 

<xsl:template match="AUTHOR">

  <xsl:if test="@name">  

    <I><xsl:value-of select="@name"/></I>

  </xsl:if>

</xsl:template>

 

The if element has a test attribute. If the XPath expression in it returns false (an empty node list), the content of the element is not executed. In the example, the XPath query @name returns true if an attribute with name name exists. The if element cannot be combined with an else element (as is common in many languages). If you need an else, you have to use the choose/when/otherwise construct. Several if elements can be used nested.

choose/when/otherwise

For more complex choices the choose, when and otherwise elements can be used. The elements when and otherwise can only be used within a choose element. The processor will check every when element from the top down. A when element works exactly like an if element. The first time the test attribute returns true, the content of that when element is executed. After this, the processor jumps past the choose element without further checking. If none of the when elements can be executed, the otherwise element will be executed (if there is one).

 

<xsl:template match="AUTHOR">

  <xsl:choose>

    <xsl:when test="@name = 'Duynstee'">

      <B><xsl:value-of select="@name"/></B>

    </xsl:when>

    <xsl:when test="@name">

      <I><xsl:value-of select="@name"/></I>

    </xsl:when>  

    <xsl:otherwise>

      No name available

    </xsl:otherwise>

  <xsl:choose>

</xsl:template>

 

In this example, the name attribute of the AUTHOR element we are processing is first checked for being 'Duynstee'. If it is, the value of the attribute it generated in the output as content of a B element and the processor jumps past the xsl:choose element in the stylesheet. If it is not, the next check is performed. If this is the case, the value of the name attribute is generated as content of an I element. If the second check fails too, then (and only then) will the processor execute the content of the xsl:otherwise element, which means generating the string 'No name available'.

for-each

For looping through a set of nodes, XSLT specifies the for-each element. The for-each element has a select attribute holding an XPath expression. The content of the for-each element is executed for each result from this query. Inside the loop, the context is moved to the current result set element.

 

<xsl:template match="PUBLISHER">

<xsl:for-each select="child::BOOK">

  <xsl:value-of select="attribute::title"/>

  <xsl:for-each select="child::AUTHOR">

     <xsl:value-of select="attribute::name"/>

  </ xsl:for-each>

  <xsl:text>&#13;</xsl:text><!—Insert a new line after each book -->

</ xsl:for-each>

 

</xsl:template>

 

In this example, for-each elements are nested. Because the context moves with the for-each loop, the select attribute of the inner loop is evaluated in the context of the results of the outer loop. This is of course what you would expect.

 

Note that there is not much difference between an apply-templates element with an appropriate template and a for-each loop. Basically, a for-each loop is an in-line template. However, a for-each loop is generally more easy to read, but separating the content over several templates makes reuse of the inner template easier.

sort

Whenever XSLT iterates along a set of nodes, it can be useful to set the order of iteration. By default this is always document order, but the destination format may expect another order (or you are transforming data to HTML and you need to display in a set order).

 

Sorting can be used on the elements apply-templates and for-each. The sort element is inserted as a child element of the apply-templates or for-each element. Consider this example:

 

<xsl:template match="FAMILY">

  <xsl:apply-templates select="PERSON">

    <xsl:sort select="@lastname"/>

    <xsl:sort select="@firstname"/>

  </xsl:apply-templates>

</xsl:template>

 

The select attribute holds an XPath expression. This expression is evaluated for each of the nodes in the set. The result of this expression determines the position in the sorted set.

 

By inserting multiple sort elements, the result set can be sorted primarily on the first criterion, and secondarily on the second one. In this case, the apply-templates element selects a set of all PERSON elements. These are sorted first on their lastname attribute, then on their firstname attribute. Only after the sorting does the XSLT processor start searching for the appropriate matching templates.

 

A number of extra optional attributes can be used with the sort element. The most important ones are:

 

order, for specifying 'ascending' or 'descending' sort order.

data-type, for sorting numerically or alphabetically. The default is alphabetically, causing 10 to be smaller than 9. Available values for data-type are 'text' and 'number'. Other values can also be used, but the meaning is not specified by the XSLT specification.

case-order, for specifying lower-first or upper-first. When case-order is lower-first, A sorts after a. There is no functionality for case insensitivity.

 


BackContentsNext
©1999 Wrox Press Limited, US and UK.

Buy this book



Select a Book

Beginning XML
Beginning XHTML
Professional XML
Professional ASP XML
Professional XML Design...
Professional XSLT...
Professional VB6 XML
Designing Distributed...
Professional Java XML...
Professional WAP

© Wattle Software 1998-2019. All rights reserved.