XSL Overview

by Andrew Taylor <andy@benow.ca>
At its core, the BeNOW Web Framework delivers html to a browser. The html may be static, or it might be dynamically generated. Dynamically generated html is done within the framework by implementing pages. Pages are structured XSL templates which take input and deliver html. An overview of XSL is demonstrated in this tutorial. After this tutorial, you should be able to implement the processing within simple pages and be ready to create a new page.
Contents
  1. Pages and XSL
  2. Creating a Static Page
Pages and XSL
Pages are a XSL of a certain structure. XSL is a language for manipulating XML. An XSL style sheet takes XML as input and outputs something (which might or might not be XML). The Web Framework uses XSL to format input XML to output HTML, which is delivered to the browser. The following is only a brief outline of XSL functionality, for more info, check out the Zvon.org XSL reference or the XSL specification itself.

Most people have never seen xsl, so we'll cover a tutorial page, which demonstrates the major XSL concepts. Open html/tutorial/index.page either with a text editor, or by going to the development page and choosing Dynamic Pages and tutorial/index.page.

xsl:stylesheet
The major sections of the page include the header:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet exclude-result-prefixes="java" version="1.0"
    xmlns:java="http://xml.apache.org/xslt/java" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The header and declaration as it appears is standard and should not need to be modified. The java result prefix will be demonstrated... it allows java to be called from the page.
xsl:include, xsl:param
The next section are includes and params:
<xsl:include href="libutil.xsl"/>
<xsl:include href="libcontrol.xsl"/>
<xsl:param name="user"/>
<xsl:param name="render"/>
all elements which the XSL transformer can handle are prefixed with xsl:. The xsl:include element includes another referenced XSL file. The templates within the included XSL can be used as if they were declared locally... more on that soon. The location for the referenced file can either be a url, an absolute file (on the local machine), a file relative to the containing stylesheet location, or, if not found, a file within the lib/xsl directory. In such a way, XSL libraries can be built up and reused across multiple pages.

The xsl:param declares a parameter which may be passed into the template. By default there are two parameters passed into each page, a user and a render object. The user is the current user, and is a org.benow.security.User. It will always be available, and will be the anonymous user if no specific user is logged in. Users will be further described later. The render param is a RenderControl object, and can be used to guide rendering.

xsl:template
An index template is defined next:
<xsl:template match="index">
this template will be called if the input xml contains a root element named index. Index templates are called by default when no operation has been specified in the request. The template may also have a name attribute which can be used to call the template directly.
<xsl:template name="anotherTemplate">
  <xsl:param name="toSpew">Params can be empty</xsl:param>
  <xsl:value-of select="$toSpew"/>
</xsl:template>
in this example (which can be found towards the end of template.page), a template is created with the name of anotherTemplate. The template may take a parameter named toSpew. If no value is given for toSpew, then a default value is used. In this simple example, the parameter is simply outputted, via xsl:value-of. Note, that as toSpew is a parameter, it is addressed as $toSpew.
xsl:call-template
This named template can now be called via:
<xsl:call-template name="anotherTemplate"/>
which results in the default value for toSpew to be spewed. To pass a different value for toSpew, use the xsl:with-param syntax:
<xsl:call-template name="anotherTemplate">
  <xsl:with-param name="toSpew">or they may be changed</xsl:with-param>
</xsl:call-template> 
Input Page XML, xsl:copy-of
There has been no mention of the input XML up to this point. The purpose of XSL is to reformat XML, and, as such, needs XML to reformat. The input XML is generated by the web framework. The easiest way to see the input XML is to dump the xml with xsl:copy-of, which is shown in the next lines:
<b>Input XML</b><br/>
<textarea rows="30" cols="140"><xsl:copy-of select="/"/></textarea>
This outputs HTML, specifically the bolded Input XML and a textarea. The contents of the textarea is a copy of the full input XML tree (select="/" chooses the root element of the XML). The input XML itself, generated by the web framework is: The root element is index which will match the template declared with match="index". The other sections of the input XML within the index element are:
  1. params - input parameters to the page. Can be used to access variables passed in on the url
  2. params/pageName - the name of the called page
  3. url - the full url used to access the page
  4. headers - a list of headers passed to the web service by the requesting browser. Used to present information according to the requesting browser
  5. session - a list of items in the session. Used to display information held across requests
  6. menu - the items which appear in the menu. Used for the menu display in the site layout (to be discussed)
  7. styles - a list of styles to be included in the page. Used in site layout (to be discussed)
Most of these are not used regularly, but they'll be used just to introduce XSL functionality.

It is within the template that processing of XML occurs. If you are coming from a programming background (which you should be), the general workings should seem familiar, just in XML format.

xsl:if
The if does as an if should do... conditional processing. The following follows in tutorial.page:
<xsl:if test="params/pageName">
The pageName is: <xsl:value-of select="params/pageName">.<p/>
</xsl:if>
the test attribute tests the existence of a named element.
Addressing nodes by path
When nodes are addressed, such as in the xsl:if test=,the xsl:when test= or xsl:value-of select=, the nodes are addressed by path, with / as separators. For example, parent/child/grandchild would reference the element named grandchild which is a child of child which is a child of parent relative to the current element (as matched by the template, or by the xsl:for-each). Elements may also be referenced with / as a prefix, to select from the root. For example, /parent/child/grandchild would reference the top element named parent then the child and grandchild elements. To reference parent nodes, .. syntax may be used. For example, if the current element were grandchild then ../.. would reference parent. Attributes may also be referenced:
<xsl:if test="session/user">
The user id is: <xsl:value-of select="session/user/@key">.<p/>
</xsl:if>
The path of session/user/@key addresses the key attribute of the user element within the session element relative to the current element. As we're in the template matching index we do not need to prefix with /index.
xsl:for-each
The xsl:for-each provides iteration over elements. The select attribute contains the path of the items to iterate over. Within the body of the statement, each item can be adressed locally. For example:
<xsl:for-each select="menu/children/item/children/item">
<!--  show the name of each child -->
<xsl:value-of select="name"/><p/>
</xsl:for-each>
Functions
XSL contains build in operators which can be used within conditional testing, formatting of output, etc. The contains(path,string) returns true if the text of the attribute or element of the given path contains the given string. An example is:
<xsl:if test="contains(url,'localhost')">
You are connecting from localhost.<p/>
</xsl:if>
Another couple common functions are position() and count(path). The position() function returns the index position of the current element, ie what child of the parent is it, starting from 1. The count(path) function returns the number of nodes with the given path.
xsl:choose
The xsl:choose element is used when processing is to be different across one or more choices. The xsl:choose element contains one or more xsl:when elements and (possibly) a xsl:otherwise element. The xsl:when element has a test attribute which matches a condition and the xsl:otherwise is processed when none of the when conditions match. An example is:
<xsl:choose>
  <xsl:when test="postion()=1">
    <b><xsl:value-of select="name"/></b>
  </xsl:when>
  <xsl:when test="positions()=count(../item)">
    <i><xsl:value-of select="name"/></i>
  </xsl:when>
  <xsl:otherwise>
    <xsl:value-of select="name"/>
  </xsl:otherwise>
</xsl:choose>
This would bold the first item, italicize the last item and show the item name for all other items.

That's it for this brief introduction to XSL. As mentioned earlier, you would do well to study the Zvon.org XSL reference or the XSL specification itself.

The BeNOW tutorials are a work in progress. If you have any comments or suggestions, please email <andy@benow.ca>.