Push processing lets the document decide which template should be instantiated. This is also called pattern-driven control or the rule-based design pattern.
Push processing works best when the structure of the XML document is non-uniform or if the style sheet must process different forms of documents that contain the same elements.
<items>
<contact>
<name> Joe Smith </name>
<phone> (213) 555-1234
</phone>
</contact>
<appointment>
<date> June 21, 2003
</date>
<time> 1:00 PM </time>
<location> My office
</location>
<with> Joe Smith </with>
</appointment>
<contact>
<name> Bill Jones
</name>
<phone> (408) 555-9050
</phone>
<email> jones@cia.gov </email>
</contact>
<reminder>
Bill's birthday is on June 26!
</reminder>
<appointment>
<date> June 25, 2003
</date>
<time> 1:30 PM </time>
<location> My office
</location>
<with> Bill Jones
</with>
</appointment>
<reminder>
Don't forget to take your Prozac
</reminder>
</items>
In this example we want to extract only appointments and phone numbers:
Each template calls apply templates
<xsl:template match = "items">
<html>
<head><title> XSL Demo
</title></head>
<body>
Appointments:
<xsl:apply-templates select =
"//appointment"/>
</body>
</html>
</xsl:template>
<xsl:template match = "appointment">
<hr />
date and time:
<xsl:value-of select =
"date"/>@
<xsl:value-of select =
"time"/>
with: <br/>
<xsl:variable name = "who"
select = "with"/>
<xsl:value-of select =
"$who"/>
<br/>
<xsl:apply-templates select =
"//contact">
<xsl:with-param name =
"who" select = "$who"/>
</xsl:apply-templates>
<br/>
<hr />
</xsl:template>
<xsl:template match = "contact">
<xsl:param name =
"who"/>
<xsl:if test = "contains(name,
$who) or contains($who, name)">
phone:
<xsl:value-of select =
"phone"/>
</xsl:if>
</xsl:template>
In the pull variant, also called control-driven or navigational, it's the style sheet's job to explicitly pull data from the XML document. This can lead to nested iterations and style sheets that need to be re-written if and when the format of the XML document changes.
<xsl:template match = "items">
<html>
<head><title> XSL Demo
</title></head>
<body>
Appointments:
<xsl:for-each select =
"appointment">
<hr />
date and time:
<xsl:value-of select =
"date"/>@
<xsl:value-of select =
"time"/>
with: <br/>
<xsl:variable name =
"who" select = "with"/>
<xsl:value-of select =
"$who"/>
<xsl:for-each select =
"/items/contact[contains(name,
$who) or contains($who, name)]">
<br/>phone:
<xsl:value-of select =
"phone"/>
</xsl:for-each>
</xsl:for-each>
</body>
</html>
</xsl:template>
A Literal Result Element As Stylesheet or Simplified Style Sheet is a style sheet that does not have top level xsl:stylesheet or xsl:template elements. This makes it possible to embed XSL elements in other XML documents. This means, among other things, that XSL elements can be used in HTML documents.
<?xml version = "1.0"?>
<bio>
<name> Jon Pearce </name>
<education>
<school>
<name> UC Berkeley
</name>
<start> Fall 1967
</start>
<end> Winter 1972
</end>
<degree> BA </degree>
<major> Math </major>
</school>
</education>
<employment>
<job>
<employer> Stanford
University Hospital</employer>
<title> Orderly
</title>
<start> January 1968
</start>
<end> June 1972
</end>
<responsibilities>
Assist the doctors and nurses
in the emergency room.
</responsibilities>
</job>
<job>
<employer> Silma
Inc.</employer>
<title> Programmer
</title>
<start> September 1985
</start>
<end> December 1990
</end>
<responsibilities>
Implement and maintain the
development language for
Silma's factory simulation
software.
</responsibilities>
</job>
</employment>
<interests>
<interest category =
"travel">
I like to immerse myself in
foregin cultures. I have
lived in Africa and
</interest>
<interest category =
"philosophy">
I am interested in Cognitive
Psychology.
</interest>
<interest category =
"photography">
I have a digital camera, and I am
learning
to use Photo Shop.
</interest>
</interests>
</bio>
<html
xmlns:xsl =
"http://www.w3.org/1999/XSL/Transform"
xsl:version = "1.0">
<head><title> My Home Page </title></head>
<body bgcolor = "cyan">
<h1 align = "center">
Welcome to Jon Pearce's Home Page </h1>
<p align = "center">
<image
src="c:\pearce\spain\miro\DSCN0461.jpg"
height="300"
width="300"/> </p> <hr/>
<p> Here are some of the places I
have worked: <br/>
<ul>
<xsl:for-each select =
"/bio/employment/job">
<li>
<xsl:value-of select
= "title"/> at
<xsl:value-of select =
"employer"/>
</li>
</xsl:for-each>
</ul> </p>
<p> My interests include: <br
/>
<ul>
<xsl:for-each select =
"/bio/interests/interest">
<li><b><xsl:value-of
select = "@category"/>: </b>
<xsl:value-of select
= "."/>
</li>
</xsl:for-each>
</ul>
</p> <hr/> </body>
</html>
If we want to add top level elements such as variables and additional templates to our style sheet, then we must have a root-level xsl:stylesheet element. In the next version of our style sheet, data from the source tree is extracted by explicitly navigating to the node where the data is declared. We might call this type of style sheet procedural, because it is close to traditional procedural programming. However, the order nodes in the source tree are extracted is still not under our control, hence Kay refers to this as the navigational style.
<?xml version = "1.0"?>
<xsl:stylesheet
xmlns:xsl =
"http://www.w3.org/1999/XSL/Transform"
version = "1.0">
<xsl:template match = "/">
<html>
<head><title> My Home Page
</title></head>
<body bgcolor = "cyan">
<h1 align =
"center"> Welcome to Jon Pearce's Home Page </h1>
<p align = "center">
<image
src="c:\pearce\spain\miro\DSCN0461.jpg"
height="300"
width="300"/> </p> <hr/>
<xsl:call-template name =
"jobs"/>
<xsl:call-template name =
"interests"/>
<hr/> </body>
</html>
</xsl:template>
<xsl:template name = "jobs">
<p> Here are some of the places I
have worked: <br/>
<ul> <xsl:for-each select =
"/bio/employment/job">
<li>
<xsl:value-of select =
"title"/> at
<xsl:value-of select =
"employer"/>
</li>
</xsl:for-each> </ul>
</p>
</xsl:template>
<xsl:template name = "interests">
<p> My interests include: <br
/>
<ul> <xsl:for-each select =
"/bio/interests/interest">
<li><b><xsl:value-of
select = "@category"/>: </b>
<xsl:value-of select =
"."/>
</li>
</xsl:for-each> </ul>
</p>
</xsl:template>
</xsl:stylesheet>
A rule-based style sheet is a collection of templates, one matching each type of element in our source tree. If a template needs to invoke other templates, it does so by calling:
<xsl:apply-templates/>
Control is handed over to the processor and the structure of the source tree to decide which templates to instantiate. This type of style sheet can work with many different types of source trees, as long as they contain the same elements. It doesn't matter where the elements are located.
<?xml version = "1.0"?>
<xsl:stylesheet
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
version = "1.0">
<xsl:template match = "bio">
<html>
<head><title> My Home Page
</title></head>
<body bgcolor = "cyan">
<h1 align =
"center"> Welcome to Jon Pearce's Home Page </h1>
<p align = "center">
<image src="c:\pearce\spain\miro\DSCN0461.jpg"
height="300"
width="300"/>
</p> <hr/>
<xsl:apply-templates/>
<hr/> </body>
</html>
</xsl:template>
<xsl:template match = "employment">
<p> Here are some of the places I
have worked: <br/> <ul>
<xsl:apply-templates/>
</ul> </p>
</xsl:template>
<xsl:template match = "interests">
<p> My interests include: <br
/> <ul>
<xsl:apply-templates/>
</ul> </p>
</xsl:template>
<xsl:template match = "job">
<li>
<xsl:value-of select =
"title"/> at
<xsl:value-of select =
"employer"/>
</li>
</xsl:template>
<xsl:template match = "interest">
<li><b><xsl:value-of
select = "@category"/>: </b>
<xsl:value-of select =
"."/>
</li>
</xsl:template>
<xsl:template match = "name | education">
<!-- do nothing at this point -->
</xsl:template>
</xsl:stylesheet>
Computational style sheets need to output nodes that aren't explicitly in the source tree. This includes computing values that aren't explicitly in the source tree.
As such, this isn't really a design pattern. Instead, it's a collection of techniques such as using recursion to preserve state.
<?xml version = "1.0"?>
<!-- scores from midterm #1 -->
<scores>
<score> 92 </score>
<score> 35 </score>
<score> 95 </score>
<score> 75 </score>
<score> 76 </score>
<score> 75 </score>
<score> 67 </score>
<score> 69 </score>
<score> 35 </score>
<score> 75 </score>
<score> 92 </score>
<score> 65 </score>
<score> 95 </score>
<score> 75 </score>
<score> 76 </score>
</scores>
We need to compute the min, max, and average of a list of scores. This would be done with a single iteration in traditional languages, but in XSLT, we need to compute these values separately, and we need to do it using recursion, because we can't maintain the current minimum or maximum in variables that can be updated.
<xsl:template match = "/">
<html>
<head><title> Midterm 1
</title></head>
<body>
<h1 align = "center">
Results from Midterm 1 </h1>
<p>
The maximum score was
<xsl:call-template name =
"getmax">
<xsl:with-param name =
"nums" select = "//score"/>
<xsl:with-param name =
"result" select = "0"/>
</xsl:call-template>
</p>
<p>
The minimum score was
<xsl:call-template name =
"getmin">
<xsl:with-param name =
"nums" select = "//score"/>
<xsl:with-param name =
"result" select = "100"/>
</xsl:call-template>
</p>
<p>
The average score was
<xsl:value-of select =
"sum(//score) div count(//score)"/>
</p>
<xsl:variable name =
"scores2"
select = "/scores/score[not(.=preceding-sibling::score)]"/>
<p> Here's the distribution:
<br/>
<xsl:for-each select =
"$scores2">
<xsl:sort order =
"descending"/>
<xsl:value-of select =
"."/>
<xsl:call-template name =
"getcount">
<xsl:with-param name =
"nums" select = "//score"/>
<xsl:with-param name =
"result" select = "''"/>
<xsl:with-param name =
"target" select = "."/>
</xsl:call-template>
<br/>
</xsl:for-each>
</p> </body> </html>
</xsl:template>
The getmax template demonstrates recursion and conditional initialization of the temp variable.
<xsl:template name = "getmax">
<xsl:param name =
"nums"/>
<xsl:param name =
"result"/>
<xsl:choose>
<xsl:when test =
"$nums">
<xsl:variable name =
"temp">
<xsl:choose>
<xsl:when test =
"$nums[1] > $result">
<xsl:value-of select
= "$nums[1]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select
= "$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name =
"getmax">
<xsl:with-param name =
"nums"
select =
"$nums[position() != 1]"/>
<xsl:with-param name =
"result" select = "$temp"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select =
"$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The getmin and getcount templates are nearly identical to the getmax template and are left as exercises. Is there some way the same template could be used for all three calculations?