This material is adapted from the excellent tutorials at: http://www.tei-c.org.uk/Talks/OUCS/2005-02/talk-transform.pdf (2005-07-05)
Take this
<text> <title>My Story</title> <para>As the day began...</para> </text>
and make this
<html> <h1>My Story</h1> <p>As the day began...</p> </html>
<xsl:stylesheet version = '1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match="/">
<html>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="title">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="para">
<p><xsl:value-of select="."/> </p>
</xsl:template>
</xsl:stylesheet>
<!-- the opening stylesheet element -->
<xsl:stylesheet version = '1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match="[xpath expression]">
<!-- do something with matched elements -->
</xsl:template>
<!-- the closing stylesheet element -->
</xsl:stylesheet>
Process everything in the document and make an HTML document:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- match the whole TEI document (i.e. '/')
and process the tags inside-->
<xsl:template match="/">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
<!-- but ignore the <teiHeader>
(only process the <text> element) -->
<xsl:template match="TEI.2">
<xsl:apply-templates select="text"/>
</xsl:template>
<!-- and do the <front> and <body> separately -->
<xsl:template match="text">
<h1>FRONT MATTER</h1>
<xsl:apply-templates select="front"/>
<h1>BODY MATTER</h1>
<xsl:apply-templates select="body"/>
</xsl:template>
</xsl:stylesheet>
Add templates for paragraphs and headings:
<!-- add template for paragraphs, divs, and headers -->
<xsl:template match="p">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="div">
<div>
<h2><xsl:value-of select="head"/></h2>
<xsl:apply-templates/>
</div>
</xsl:template>
<!-- suppress a <head> inside a <div> Why? -->
<xsl:template match="div/head">
</xsl:template>
Notice how we avoid getting the heading text twice. Why did we need to qualify it to deal with just <head> inside <div>?
Now for TEI lists:we'll need to look at the �type� attribute to decide what sort of HTML list to produce:
<!-- output lists -->
<xsl:template match="list">
<xsl:choose>
<xsl:when test="@type='ordered'">
<ol><xsl:apply-templates/></ol>
</xsl:when>
<xsl:when test="@type='unordered'">
<ul><xsl:apply-templates/></ul>
</xsl:when>
<xsl:when test="@type='gloss'">
<dl><xsl:apply-templates/></dl>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- list items are simple -->
<xsl:template match="item">
<li><xsl:apply-templates/></li>
</xsl:template>
It would be nice to number the paragraphs, so let's change the template and let XSLT do it for us:
<!-- paragraphs, with a running count -->
<xsl:template match="p">
<p>
<xsl:number level="any" count="p" /><xsl:text>:
</xsl:text>
<xsl:apply-templates/>
</p>
</xsl:template>
</xsl:template>
Is the count correct? What happens if you change drop the level attribute to <xsl:number>? If your paragraph count is not correct, why not? How can we correct it (hint: use an XPath expression).
At the bottom, let�s sum up that paragraph count.
<xsl:template match="/">
<html>
<xsl:apply-templates/>
<p>Num para: <xsl:value-of select="count(//p)"/></p>
</html>
</xsl:template>
We have met the following XSL basic controls:
<xsl:stylesheet> <xsl:template match="..."> <xsl:apply-templates select="..."> <xsl:value-of select="..."> <xsl:text> <xsl:choose>
and the following extras:
<xsl:number> count()
Note that count() does not use <>s. It is an XSLT function, and there are many of these defined that perform specific operations.
concat: (string, string) substring-before: (string, string) substring-after: (string, string) string-length: (string) normalize-space: (string)
Modes
You can process the same elements in different ways using modes. Let's add a table of contents by using a mode:
<xsl:template match="/">
<xsl:apply-templates select=".//tei:div" mode="toc"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="tei:div" mode="toc">
Heading <xsl:value-of select="tei:head"/>
</xsl:template>
This is a very useful technique when the same information is processed in different ways in different places.
<xsl:template name="...">: define a named template <xsl:call-template>: call a named template <xsl:param>: specify a parameter in a template definition <xsl:with-param>: specify a parameter when calling a template <xsl:variable name="...">: define a variable
Example:
<xsl:template match="/div">
<html>
<xsl:call-template name="header">
<xsl:with-param name="title" select="head"/>
</xsl:call-template>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template name="header">
<xsl:param name="title"/>
<head>
<title><xsl:value-of select="$title"/></title>
</head>
</xsl:template>
In XSLT, you can assign and reference the value of variables. In the example below, the variable "n" is created and assigned a value. Then it is output by using the <xsl:value-of> tag as well as output directly by embedding it in {} s. Note that variables are preceeded with a $ when they are referenced (but NOT when they are assigned).
<xsl:template match="p">
<xsl:variable name="n">
<xsl:number/>
</xsl:variable>
Paragraph <xsl:value-of select="$n"/>
<a name="P{$n}"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:import href="...">: include a file of XSLT templates, overriding them as needed <xsl:include href="...">: include a file of XSLT templates, but do not override them <xsl:output>: specify output characteristics of this job
Very often, we will sit on the root element and process all the occurrences of a specific element by using the descendant axis. Here, we just return a page count:
<xsl:template match="/">
<html>
<body>
Pages: <xsl:value-of select="count(descendant::pb)"/>
</body>
</html>
</xsl:template>
Program your own XSLT stylesheet for TEI or other XML-based language.