JMU
eXtensible Stylesheet Language Transforms (XSLT)
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


A Brief History of XSL
Overview
Overview (cont.)
A Simple Example

The Input

xsltexamples/ilovejmu/jmu.xml
<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" href="simple.xsl" ?>
<text>I Love JMU</text>        
A Simple Example (cont.)

When the conceptual (as opposed to actual) root element is encountered, we want to build the skeleton of the HTML document.

xsltexamples/ilovejmu/simple.xsl (Fragment: 0)
<xsl:template match="/">
  <HTML>
    <BODY>
      <xsl:apply-templates />
    </BODY>
  </HTML>
</xsl:template>
        
A Simple Example (cont.)

When a text (i.e., actual root) element is encountered, we want to insert a P element into the HTML skeleton, and insert the contents of the text element in the P.

xsltexamples/ilovejmu/simple.xsl (Fragment: 1)
<xsl:template match="text">
  <P>
    <xsl:value-of select="." />
  </P>
</xsl:template>
        
A Simple Example (cont.)

The Complete Program Click here for a demonstration.

xsltexamples/ilovejmu/simple.xsl
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

<xsl:template match="/">
  <HTML>
    <BODY>
      <xsl:apply-templates />
    </BODY>
  </HTML>
</xsl:template>



<xsl:template match="text">
  <P>
    <xsl:value-of select="." />
  </P>
</xsl:template>



</xsl:stylesheet>
        
The xsl:stylesheet Element
The xsl:template Element
The xsl:apply-templates Element
The xsl:value-of Element
An Example

The Input

xsltexamples/bibliography/version1/bibliography.xml
<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" href="reference.xsl" ?>

<bibliography>

<reference>
  <author>
    <firstname>C.E.</firstname>
    <lastname>White</lastname>
  </author>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <author>
    <firstname>A.L.</firstname>
    <lastname>Kornhauser</lastname>
  </author>
  <year>2000</year>
  <title>Some Map-Matching Algorithms for Personal Navigation Assistants</title>
  <journal>
  Transportation Research C
  </journal>
</reference>


<reference>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <author>
    <firstname>I.</firstname>
    <lastname>El Sanhouri</lastname>
  </author>
  <year>1999</year>
  <title>The Roles of Driver Information and Congestion Pricing Systems</title>
  <book>
    Behavioral and Network Impacts of Driver Information Systems
    <editor>
      <firstname>P.</firstname>
      <lastname> Nijkamp</lastname>
    </editor>
    <editor>
      <firstname>R.</firstname>
      <lastname>Emerink</lastname>
    </editor>
    <publisher>John Wiley and Sons</publisher>
    <pages>371-392</pages>
  </book>
</reference>



<reference>
  <author>
    <firstname>J.</firstname>
    <lastname>Padmos</lastname>
  </author>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <year>1997</year>
  <title>Personal Travel Assistants and the World Wide Web</title>
  <journal>
    Transportation Research Record
    <volume>1573</volume>
    <pages>52-56</pages>
  </journal>
</reference>


<reference>
  <author>
    <firstname>R.</firstname>
    <lastname>Guensler</lastname>
  </author>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <year>1996</year>
  <title>Transportation Resources on the Internet</title>
  <journal>
    ITE Journal
    <volume>66</volume>
    <pages>42-47</pages>
  </journal>
</reference>



<reference>
  <author>
    <firstname>I.</firstname>
    <lastname>El Sanhouri</lastname>
  </author>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <year>1994</year>
  <title>Integrating Driver Information Systems with Facility-Based Congestion Pricing</title>
  <journal>
    Transportation Research Record
    <volume> 1450</volume>
    <pages>44-52</pages>
  </journal>
</reference>


<reference>
  <author>
    <firstname>D.</firstname>
    <lastname>Bernstein</lastname>
  </author>
  <author>
    <firstname>A.</firstname>
    <lastname>Kanaan</lastname>
  </author>
  <year>1993</year>
  <title>Automatic Vehicle Identification Technologies and Functionalities</title>
  <journal>
    IVHS Journal
    <volume> 1</volume>
    <pages>191-204</pages>
  </journal>
</reference>

</bibliography>



        
An Example (cont.)

The Program Click here for a demonstration.

xsltexamples/bibliography/version1/reference.xsl
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

<!-- reference.xsl v1.0 -->



<!-- root element -->
<xsl:template match="/">
  <xsl:apply-templates />
</xsl:template>




<!-- author -->
<xsl:template match="author">
  <xsl:apply-templates select="firstname"/>
  <xsl:apply-templates select="lastname"/>,
</xsl:template>



<!-- bibliography -->
<xsl:template match="bibliography">
  <xsl:apply-templates />
</xsl:template>




<!-- book -->
<!-- Note the use of the built-in text() function -->
<!-- that prevents the children from being selected -->
<xsl:template match="book">
  <xsl:value-of select="text()" />,
  <xsl:apply-templates select="editor"/>
  <xsl:apply-templates select="publisher"/>
  <xsl:apply-templates select="pages"/>.
</xsl:template>



<!-- editor -->
<xsl:template match="editor">
  <xsl:apply-templates select="firstname"/>
  <xsl:apply-templates select="lastname"/>,
</xsl:template>




<!-- firstname -->
<xsl:template match="firstname">
  <xsl:value-of select="." />
</xsl:template>






<!-- journal -->
<!-- Note the use of the built-in text() function -->
<!-- that prevents the children from being selected -->
<xsl:template match="journal">
  <xsl:value-of select="text()" />,
  <xsl:apply-templates select="volume"/>
  <xsl:apply-templates select="pages"/>.
</xsl:template>




<!-- lastname -->
<xsl:template match="lastname">
  <xsl:value-of select="." />
</xsl:template>





<!-- pages -->
<xsl:template match="pages">
  pp. <xsl:value-of select="." />
</xsl:template>



<!-- publisher -->
<xsl:template match="publisher">
  <xsl:value-of select="." />,
</xsl:template>




<!-- reference -->
<xsl:template match="reference">

    <xsl:apply-templates select="author"/>
    <xsl:apply-templates select="year"/>
    <xsl:apply-templates select="title"/>
    <xsl:apply-templates select="journal"/>
    <xsl:apply-templates select="book"/>

</xsl:template>

    



<!-- title -->
<xsl:template match="title">
  &quot;<xsl:value-of select="." />&quot;,
</xsl:template>





<!-- volume -->
<xsl:template match="volume">
  Vol. <xsl:value-of select="." />,
</xsl:template>




<!-- year -->
<xsl:template match="year">
  (<xsl:value-of select="." />)
</xsl:template>




</xsl:stylesheet>
        
Path Expressions
Path Expressions: An Example

The Input

xsltexamples/timetable/v1/amtrak.xml
<?xml version="1.0"?>

<?xml-stylesheet type="text/xsl" href="timetable.xsl"?>

<timetable title="Boston...Providence...New London...New York"
     subtitle="Through services to Philadelphia...Washington...Newport News">
  <train number="95" normaldays="Mo-Fr" reservations="YES" businessclass="YES">
    <stop>
      <station>Boston, MA-South Station</station>
      <time>6:20AM</time>
    </stop>

    <stop>
      <station>Boston, MA-Back Bay</station>
      <time>6:25AM</time>
    </stop>

    <stop>
      <station>Route 128, MA</station>
      <time>7:07AM</time>
    </stop>

    <stop>
      <station>Providence, RI</station>
      <time>7:31AM</time>
    </stop>

    <stop>
      <station>Kingston, RI</station>
      <time></time>
    </stop>

    <stop>
      <station>Westerly, RI</station>
      <time></time>
    </stop>

    <stop>
      <station>Mystic, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New London, CT</station>
      <time>8:06AM</time>
    </stop>

    <stop>
      <station>Old Saybrook, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New Haven, CT</station>
      <time status="Ar">9:00AM</time>
      <time status="Dp">9:15AM</time>
    </stop>

    <stop>
      <station>Bridgeport, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>Stamford, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New Rochelle, NY</station>
      <time></time>
    </stop>

    <stop>
      <station>New York, NY</station>
      <time status="Ar">10:50AM</time>
    </stop>

    <stop>
      <station>Newark, NJ</station>
      <time>11:26AM</time>
    </stop>

    <stop>
      <station>Metropark, NJ</station>
      <time>11:40AM</time>
    </stop>

    <stop>
      <station>Trenton, NJ</station>
      <time>12:04AM</time>
    </stop>

    <stop>
      <station>Philadelphia, PA</station>
      <time>12:32PM</time>
    </stop>

    <stop>
      <station>Wilmington, DE</station>
      <time>12:58PM</time>
    </stop>

    <stop>
      <station>Baltimore, MD</station>
      <time>1:49PM</time>
    </stop>

    <stop>
      <station>BWI Airport Rail Sta., MD</station>
      <time>2:02PM</time>
    </stop>

    <stop>
      <station>New Carrollton, MD</station>
      <time>2:17PM</time>
    </stop>

    <stop>
      <station>Washington, DC</station>
      <time>2:35PM</time>
    </stop>

    <stop>
      <station>Richmond, VA</station>
      <time>5:19PM</time>
    </stop>

    <stop>
      <station>Newport News, VA</station>
      <time status="Ar">7:07PM</time>
    </stop>
  </train>




  <train number="191" normaldays="Sa"  reservations="YES" businessclass="YES">
    <stop>
      <station>Boston, MA-South Station</station>
      <time>6:20AM</time>
    </stop>

    <stop>
      <station>Boston, MA-Back Bay</station>
      <time>6:25AM</time>
    </stop>

    <stop>
      <station>Route 128, MA</station>
      <time>7:07AM</time>
    </stop>

    <stop>
      <station>Providence, RI</station>
      <time>7:31AM</time>
    </stop>

    <stop>
      <station>Kingston, RI</station>
      <time></time>
    </stop>

    <stop>
      <station>Westerly, RI</station>
      <time></time>
    </stop>

    <stop>
      <station>Mystic, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New London, CT</station>
      <time>8:06AM</time>
    </stop>

    <stop>
      <station>Old Saybrook, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New Haven, CT</station>
      <time status="Ar">9:00AM</time>
      <time status="Dp">9:15AM</time>
    </stop>

    <stop>
      <station>Bridgeport, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>Stamford, CT</station>
      <time></time>
    </stop>

    <stop>
      <station>New Rochelle, NY</station>
      <time></time>
    </stop>

    <stop>
      <station>New York, NY</station>
      <time status="Ar">10:50AM</time>
    </stop>

    <stop>
      <station>Newark, NJ</station>
      <time>11:26AM</time>
    </stop>

    <stop>
      <station>Metropark, NJ</station>
      <time>11:40AM</time>
    </stop>

    <stop>
      <station>Trenton, NJ</station>
      <time>12:04AM</time>
    </stop>

    <stop>
      <station>Philadelphia, PA</station>
      <time>12:32PM</time>
    </stop>

    <stop>
      <station>Wilmington, DE</station>
      <time>12:58PM</time>
    </stop>

    <stop>
      <station>Baltimore, MD</station>
      <time>1:49PM</time>
    </stop>

    <stop>
      <station>BWI Airport Rail Sta., MD</station>
      <time>2:02PM</time>
    </stop>

    <stop>
      <station>New Carrollton, MD</station>
      <time>2:17PM</time>
    </stop>

    <stop>
      <station>Washington, DC</station>
      <time>2:35PM</time>
    </stop>

    <stop>
      <station>Richmond, VA</station>
      <time></time>
    </stop>

    <stop>
      <station>Newport News, VA</station>
      <time></time>
    </stop>
  </train>




  <train number="171" normaldays="Daily" businessclass="YES">
    <stop>
      <station>Boston, MA-South Station</station>
      <time>7:22AM</time>
    </stop>

    <stop>
      <station>Boston, MA-Back Bay</station>
      <time>7:27AM</time>
    </stop>

    <stop>
      <station>Route 128, MA</station>
      <time>7:42AM</time>
    </stop>

    <stop>
      <station>Providence, RI</station>
      <time>8:17AM</time>
    </stop>

    <stop>
      <station>Kingston, RI</station>
      <time>8:43AM</time>
    </stop>

    <stop>
      <station>Westerly, RI</station>
      <time>8:59AM</time>
    </stop>

    <stop>
      <station>Mystic, CT</station>
      <time>9:10AM</time>
    </stop>

    <stop>
      <station>New London, CT</station>
      <time>9:25AM</time>
    </stop>

    <stop>
      <station>Old Saybrook, CT</station>
      <time>9:47AM</time>
    </stop>

    <stop>
      <station>New Haven, CT</station>
      <time status="Ar">10:25AM</time>
      <time status="Dp">10:35AM</time>
    </stop>

    <stop>
      <station>Bridgeport, CT</station>
      <time>10:57AM</time>
    </stop>

    <stop>
      <station>Stamford, CT</station>
      <time>11:23AM</time>
    </stop>

    <stop>
      <station>New Rochelle, NY</station>
      <time>11:43AM</time>
    </stop>

    <stop>
      <station>New York, NY</station>
      <time status="Ar">12:20PM</time>
    </stop>

    <stop>
      <station>Newark, NJ</station>
      <time>12:56PM</time>
    </stop>

    <stop>
      <station>Metropark, NJ</station>
      <time></time>
    </stop>

    <stop>
      <station>Trenton, NJ</station>
      <time>1:34PM</time>
    </stop>

    <stop>
      <station>Philadelphia, PA</station>
      <time>2:02PM</time>
    </stop>

    <stop>
      <station>Wilmington, DE</station>
      <time>2:43PM</time>
    </stop>

    <stop>
      <station>Baltimore, MD</station>
      <time>3:42PM</time>
    </stop>

    <stop>
      <station>BWI Airport Rail Sta., MD</station>
      <time>3:55PM</time>
    </stop>

    <stop>
      <station>New Carrollton, MD</station>
      <time>4:10PM</time>
    </stop>

    <stop>
      <station>Washington, DC</station>
      <time>4:30PM</time>
    </stop>

    <stop>
      <station>Richmond, VA</station>
      <time></time>
    </stop>

    <stop>
      <station>Newport News, VA</station>
      <time></time>
    </stop>
  </train>



</timetable>
        
Path Expressions: An Example (cont.)

The Program Click here for a demonstration.

xsltexamples/timetable/v1/timetable.xsl
<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>


  <!-- Root -->
  <xsl:template match="/">
    <HTML>
      <HEAD>
      </HEAD>

      <BODY BGCOLOR="#FFFFFF">
        <xsl:apply-templates select="timetable"/>
      </BODY>
    </HTML>
  </xsl:template>



  <!-- Station -->
  <xsl:template match="station">
    <B>
      <xsl:value-of select="text()" />
    </B>
  </xsl:template>



  <!-- Stop -->
  <xsl:template match="stop">
    <TD>
      <xsl:apply-templates select="station" />
    </TD>
  </xsl:template>




  <!-- Time -->
  <xsl:template match="time">
    <TD>
      <xsl:value-of select="text()" />
    </TD>
  </xsl:template>


  <!-- Timetable -->
  <!-- Note the use of the attribute operator -->
  <xsl:template match="timetable">
    <H2>
      <xsl:value-of select="@title"/>
    </H2>
    <H3>
      <xsl:value-of select="@subtitle"/>
    </H3>
    <TABLE>
      <xsl:apply-templates select="train"/>
    </TABLE>
  </xsl:template>




  <!-- Train -->
  <!-- Note the use of the attribute and child operators -->
  <xsl:template match="train">
      <TR>
        <TD><xsl:value-of select="@number" /></TD>
        <TD><xsl:value-of select="@normaldays" /></TD>
        <xsl:apply-templates select="stop/time" />
      </TR>
  </xsl:template>

  <!-- Note the use of the index, attribute, and child operators -->
  <xsl:template match="train[1]">
      <TR>
        <TD><B>Train Number</B></TD>
        <TD><B>Normal Days</B></TD>
        <xsl:apply-templates select="stop" />
      </TR>
      <TR>
        <TD><xsl:value-of select="@number" /></TD>
        <TD><xsl:value-of select="@normaldays" /></TD>
        <xsl:apply-templates select="stop/time" />
      </TR>
  </xsl:template>





</xsl:stylesheet>




        
Filter Operators
Filter Operators (cont.)
= The equal value operator. For example, time[@status = 'Ar'] refers to a time element with a status attribute equal to "Ar".
> The greater than value operator.
< The less than value operator.
>= The greater than or equal to value operator.
<= The less than or equal to value operator.
!= The not equal to value operator.
The xsl:if Element
The xsl:if Element (cont.)

An Example

xsltexamples/bibliography/version2/reference.xsl (Fragment: 0)
  <xsl:if test="pages">
    ,<xsl:apply-templates select="pages"/>
  </xsl:if>
        
The xsl:choose Element
The xsl:choose Element (cont.)

An Example

xsltexamples/bibliography/version2/reference.xsl (Fragment: 1)
<!-- author -->
<!-- v2.0 Fixes first author and last author -->
<xsl:template match="author">
  <xsl:choose>
    <xsl:when test="position()=1">
      <xsl:apply-templates select="lastname"/>,
      <xsl:apply-templates select="firstname"/>
    </xsl:when>
    <xsl:when test="position()=last()">
      and
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:when>
    <xsl:otherwise>
      ,
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
        
Default Rules
A Complete Example

The Program Click here for a demonstration.

xsltexamples/bibliography/version2/reference.xsl
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

<!-- reference.xsl v2.0 -->



<!-- root element -->
<xsl:template match="/">
  <HTML>
    <BODY>
      <xsl:apply-templates />
    </BODY>
  </HTML>
</xsl:template>



<!-- author -->
<!-- v2.0 Fixes first author and last author -->
<xsl:template match="author">
  <xsl:choose>
    <xsl:when test="position()=1">
      <xsl:apply-templates select="lastname"/>,
      <xsl:apply-templates select="firstname"/>
    </xsl:when>
    <xsl:when test="position()=last()">
      and
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:when>
    <xsl:otherwise>
      ,
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>



<!-- bibliography -->
<xsl:template match="bibliography">
  <xsl:apply-templates />
</xsl:template>




<!-- book -->
<!-- Note the use of the built-in text() function -->
<!-- that prevents the children from being selected -->
<xsl:template match="book">
  <EM><xsl:value-of select="text()" /></EM>
  , Edited by
  <xsl:apply-templates select="editor"/>
  <xsl:apply-templates select="publisher"/>
  <xsl:apply-templates select="pages"/>.
</xsl:template>



<!-- editor -->
<!-- v2.0 Fixes first editor and last editor -->
<xsl:template match="editor">
  <xsl:choose>
    <xsl:when test="position()=1">
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:when>
    <xsl:when test="position()=last()">
      and
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:when>
    <xsl:otherwise>
      ,
      <xsl:apply-templates select="firstname"/>
      <xsl:apply-templates select="lastname"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>




<!-- firstname -->
<xsl:template match="firstname">
  <xsl:value-of select="." />
</xsl:template>






<!-- journal -->
<!-- Note the use of the built-in text() function -->
<!-- that prevents the children from being selected -->
<!-- -->
<!-- v2.0 fixed problem with missing elements -->
<xsl:template match="journal">
  <EM><xsl:value-of select="text()" /></EM>
  <xsl:if test="volume">
    ,<xsl:apply-templates select="volume"/>
  </xsl:if>
  <xsl:if test="pages">
    ,<xsl:apply-templates select="pages"/>
  </xsl:if>
  .
</xsl:template>




<!-- lastname -->
<xsl:template match="lastname">
  <xsl:value-of select="." />
</xsl:template>





<!-- pages -->
<xsl:template match="pages">
  pp. <xsl:value-of select="." />
</xsl:template>



<!-- publisher -->
<xsl:template match="publisher">
  <xsl:value-of select="." />,
</xsl:template>




<!-- reference -->
<xsl:template match="reference">
  <P>
    <xsl:apply-templates select="author"/>
    <xsl:apply-templates select="year"/>
    <xsl:apply-templates select="title"/>
    <xsl:apply-templates select="journal"/>
    <xsl:apply-templates select="book"/>
  </P>
</xsl:template>

    



<!-- title -->
<xsl:template match="title">
  &quot;<xsl:value-of select="." />&quot;,
</xsl:template>





<!-- volume -->
<xsl:template match="volume">
  Vol. <xsl:value-of select="." />
</xsl:template>




<!-- year -->
<xsl:template match="year">
  (<xsl:value-of select="." />)
</xsl:template>




</xsl:stylesheet>
        
The Timetable Example with CSS Formatting

For Those Who Are Interested Click here for a demonstration.

xsltexamples/timetable/v2/timetable.xsl
<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>


  <!-- Root -->

  <xsl:template match="/">
    <HTML>
      <HEAD>
      <STYLE>

        SPAN.title   {font-family: Arial; font-size: 18pt;
                          font-style: bold}

        SPAN.subtitle {font-family: Arial; font-size: 14pt; font-style: bold; 
                       border-bottom-style: solid; border-width:4px;}


        SPAN.trainnumber {font-family: Arial; font-size: 12pt;
                          font-style: bold}

        TABLE    {font-family: Arial; font-size: 8pt;}

        TABLE.inline {display:inline;}

        TD.head  {border-bottom-style: solid; border-width: 2px;}

        TR       {height: 14pt;}
        TR.trainnumber {height: 14pt;}

        .orange  {background-color: orange;}

      </STYLE>
      </HEAD>

      <BODY BGCOLOR="#FFFFFF">
        <xsl:apply-templates />
      </BODY>
    </HTML>
  </xsl:template>



  <!-- Station -->

  <xsl:template match="station">
    <B>
      <xsl:apply-templates />&#160;
    </B>
  </xsl:template>





  <!-- Stop -->

  <xsl:template match="stop">
    <xsl:param name="position"/>
    <TR>
      <xsl:if test="$position=2">
      <TD ALIGN="LEFT" VALIGN="TOP">
        <xsl:apply-templates select="station"/>
      </TD>
      </xsl:if>
      <TD ALIGN="RIGHT" VALIGN="TOP">
        <xsl:apply-templates select="time"/>
      </TD>
    </TR>
  </xsl:template>




  <!-- Text  -->

  <xsl:template match="text()">
    <xsl:value-of select="." />
  </xsl:template>



  <!-- Time -->

  <xsl:template match="time">
    <xsl:apply-templates />&#160;<BR />
  </xsl:template>



  <!-- Timetable -->

  <xsl:template match="timetable">
    <SPAN CLASS="title">
      <xsl:value-of select="./@title"/>
    </SPAN>
    <BR />
    <SPAN CLASS="subtitle">
      <xsl:value-of select="./@subtitle"/>
    </SPAN>
    <BR />
    <xsl:apply-templates />
  </xsl:template>




  <!-- Train -->

  <xsl:template match="train">
    <TABLE CLASS="inline"  CELLPADDING="0" CELLSPACING="0">
      <xsl:if test="position()=2">
        <COLGROUP>
        </COLGROUP>
      </xsl:if>
      <COLGROUP>
        <xsl:if test="@reservations">
          <xsl:attribute name="CLASS">orange</xsl:attribute>
        </xsl:if>
      </COLGROUP>

      <TR CLASS="trainnumber">
        <xsl:if test="position()=2">
          <TD CLASS="head" ALIGN="LEFT">
            <B>Train Number</B>
          </TD>
        </xsl:if>
        <TD CLASS="head" ALIGN="CENTER">
          <SPAN CLASS="trainnumber">
            <xsl:value-of select="./@number" />
          </SPAN>
        </TD>
      </TR>

      <TR>
        <xsl:if test="position()=2">
          <TD CLASS="head" ALIGN="LEFT">
            <B>Normal Days of Operation</B>
          </TD>
        </xsl:if>
        <TD CLASS="head" ALIGN="CENTER">
          <xsl:value-of select="./@normaldays" />
        </TD>
      </TR>

      <TR>
        <xsl:if test="position()=2">
          <TD CLASS="head" ALIGN="LEFT">
            <B>&#160;&#160;&#160;Will Also Operate</B>
          </TD>
        </xsl:if>
        <TD CLASS="head" ALIGN="CENTER">
          <xsl:value-of select="./@willalsooperate" />&#160;
        </TD>
      </TR>

      <TR>
        <xsl:if test="position()=2">
          <TD CLASS="head" ALIGN="LEFT">
            <B>&#160;&#160;&#160;Will Not Operate</B>
          </TD>
        </xsl:if>
        <TD CLASS="head" ALIGN="CENTER">
          <xsl:value-of select="./@willnotoperate" />&#160;
        </TD>
      </TR>

      <TR>
        <xsl:if test="position()=2">
          <TD CLASS="head" ALIGN="LEFT">
            <B>On Board Service</B>
          </TD>
        </xsl:if>
        <TD CLASS="head" ALIGN="CENTER">
          <xsl:if test="@businessclass">
            <IMG SRC="businessclass.gif" />
          </xsl:if>
          <xsl:if test="@reservations">
            <IMG SRC="reservations.gif" />
          </xsl:if>
          <xsl:if test="@sleepingcar">
            <IMG SRC="sleepingcar.gif" />
          </xsl:if>
        </TD>
      </TR>


      <xsl:apply-templates select="stop">
        <xsl:with-param name="position">
          <xsl:value-of select="position()"/>
        </xsl:with-param>
      </xsl:apply-templates>
    </TABLE>
  </xsl:template>

</xsl:stylesheet>