|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionXSL is a declarative programming language. Variables that are once declared cannot be reassigned. It is often difficult for programmers coming from procedural languages background to do advanced tasks. Using XSL, we can solve complex problems, which at first glance often seem to be difficult or impossible. In this brief article, I will demonstrate the recursive nature of templates. In a typical case of a product catalog display, our requirement is to display the product details in each cell, and number of columns should be selectable by the user. The sample XML file is listed below: <data>
<product name="Heine HKL with trolleystand"
price="230.45" weight="34.4kg" />
<product name="Universal clamp and Projector"
price="670.45" weight="10.64kg" />
<product name="Examination Lamp, Universal Mount"
price="25.45" weight="1.08kg" />
<product name="Provita Examination Lamp, Mobile Base"
price="215.45" weight="1.4kg" />
<product name="35Watt Flexible Arm Light to fit Rail system"
price="130.45" weight="11.67kg" />
.
.
.
.
.
.
</data>
Assuming we are getting the above from a business component, each element row corresponds to a product, whose attributes comprises of product specific data. Each product along with its details will be rendered in a single cell. And the number of columns should be definable at runtime. Following is the brief XSL which does the rendering: <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:param name="numCols" select="3"/>
<table cellpadding="0" cellspacing="5" border="0">
<xsl:call-template name="renderColumns">
<xsl:with-param name="listrows" select="//product"/>
<xsl:with-param name="startindex" select="1"/>
<xsl:with-param name="numofCols" select="$numCols"/>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="renderColumns">
<xsl:param name="listrows" />
<xsl:param name="startindex"/>
<xsl:param name="numofCols"/>
<xsl:if test="count($listrows) > 0">
<tr>
<xsl:apply-templates
select="$listrows[position() >=
$startindex and position() <
($startindex+$numofCols)]" mode="rows">
</xsl:apply-templates>
</tr>
<xsl:call-template name="renderColumns">
<xsl:with-param name="listrows"
select="$listrows[position() >= $startindex+$numofCols]"/>
<xsl:with-param name="startindex" select="$startindex"/>
<xsl:with-param name="numofCols" select="$numofCols"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="node()" mode="rows">
<td nowrap="true">
<table style="width:100%;border-right: thin solid;
border-top: thin solid; border-left:
thin solid; border-bottom: thin solid;">
<xsl:apply-templates select="@*"/>
</table>
</td>
</xsl:template>
<xsl:template match="@*">
<tr>
<td style="font-size: larger; text-transform:
uppercase; background-color: gainsboro">
<xsl:value-of select="name()"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Explanation<xsl:template match="/">
<xsl:param name="numCols" select="3"/>
As would a C or C++ programmer organize and reuse the code using functions or object methods, in XSL, we can organize code using templates. The above code is the root template which will be invoked by the XSLT processor. We are declaring a parameter using <xsl:call-template name="renderColumns">
<xsl:with-param name="listrows" select="//product"/>
<xsl:with-param name="startindex" select="1"/>
<xsl:with-param name="numofCols" select="$numCols"/>
</xsl:call-template>
We are calling the <xsl:template name="renderColumns">
<xsl:param name="listrows" />
<xsl:param name="startindex"/>
<xsl:param name="numofCols"/>
<xsl:if test="count($listrows) > 0">
<tr>
<xsl:apply-templates
select="$listrows[position() >=
$startindex and position() <
($startindex+$numofCols)]"
mode="rows">
</xsl:apply-templates>
</tr>
<xsl:call-template name="renderColumns">
<xsl:with-param name="listrows"
select="$listrows[position() >= $startindex+$numofCols]"/>
<xsl:with-param name="startindex" select="$startindex"/>
<xsl:with-param name="numofCols" select="$numofCols"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
In the XSL template <xsl:template match="node()" mode="rows">
<td nowrap="true">
<table style="width:100%;border-right: thin solid;
border-top: thin solid; border-left:
thin solid; border-bottom: thin solid;">
<xsl:apply-templates select="@*"/>
</table>
</td>
</xsl:template>
in which we are converting the attribute nodes into elements and calling another template: <xsl:template match="@*">
<tr>
<td style="font-size: larger; text-transform:
uppercase; background-color: gainsboro">
<xsl:value-of select="name()"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
which does the job of rendering the product details in a cell. ConclusionEven though variables are constants through out the life time of a variable in XSL, we can achieve things which, at first glance, look impossible due to the declarative nature of XSL. Upon close look and thinking in a declarative manner, we can solve the problem.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||