3

I'm trying to display a university courses timetable using XSLT. My DTS looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT timetable (day,day,day,day,day,day,day)>
<!ELEMENT day (session)*>
<!ELEMENT session (begin,end,(course?))>
<!ELEMENT course (#PCDATA)>
<!ELEMENT begin (#PCDATA)>
<!ELEMENT end (#PCDATA)>

I want to display all the courses in a Day/Hour table that looks something like this (excuse the horrible design):

timetable

Trouble is, I want to do a for each clause, but just on regular numbers, not on parts of the xml. Is that possible with XSLT? For example, it would probably look something like this:

/* for each time = 8..17, do: */
    <xsl:for-each select="timetable/day">
        <xsl:value-of select="session[[begin&lt;/*time*/ or begin=/*time*/]/course" />
    </xsl:for-each>
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
Amir Rachum
  • 76,817
  • 74
  • 166
  • 248

4 Answers4

2

You can use recursion

<xsl:template name="for_i_from_8_to_17">
    <xsl:param name="i">8</xsl:param> <!-- initial value -->
    <!-- do what you have to do -->
    <xsl:if test="not($i = 17)">
        <xsl:call-template name="for_i_from_8_to_17">
            <xsl:with-param name="i">
        <xsl:value-of select="$i + 1">
        </xsl:with-param>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

(slightly adapted from xsl-list@mulberrytech.com)

marc
  • 6,103
  • 1
  • 28
  • 33
2

in XSLT 2.0:

<xsl:variable name="timetable" select="timetable">
<table>
  <thead>
     .. output the table heading ..
  </thead>
  <tbody>
    <xsl:for-each select="8 to 17">
    <tr>
      <xsl:variable name="hour" select="."/>
      <td><xsl:value-of select="$hour, '-', $hour+1"/></td>          
      <xsl:for-each select="$timetable/day">
        <td><xsl:value-of 
            select="session[begin lt $hour+1 and end gt $hour]/course"/>
        </td>
      </xsl:for-each>
    </xsl:for-each>
  </tbody>
</table> 

plus a bit of work on the formatting.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
0

You can use recursion:

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

  <xsl:template match="/">
    <html>

      <head>
        <style type="text/css">td{border:solid 1px black} table{border-collapse:collapse}</style>
      </head>

      <table>
        <xsl:call-template name="for">
          <xsl:with-param name="count" select="10"/>
        </xsl:call-template>
      </table>
    </html>
  </xsl:template>

  <xsl:template name="for">
    <xsl:param name="i" select="0"/>
    <xsl:param name="count"/>

    <xsl:if test="$i &lt; $count">
      <tr>
        <td>
          <xsl:value-of select="concat($i + 8, ':00 - ', $i + 9, ':00')"/>
        </td>
      </tr>

      <xsl:call-template name="for">
        <xsl:with-param name="i" select="$i + 1"/>
        <xsl:with-param name="count" select="$count"/>
      </xsl:call-template>
    </xsl:if>

  </xsl:template>

</xsl:stylesheet>

Output:

enter image description here

Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
0

You would require to have 2 for each loops. one to iterate over the days of week and one for the hours of the day. The hours of the day could be solved with XSLT 2.0 easily like this:

<xsl:for-each select="8 to 17">
   <!-- do your stuff -->
   <xsl:value-of select="." /> <!-- dot represents a number from the range -->
</xsl:fo-each>

See this for a complete coverage of sequences and ranges.

Michael-O
  • 18,123
  • 6
  • 55
  • 121
  • I didn't quite understand how to access the iteration variable. Can you demonstrate? – Amir Rachum Jul 23 '11 at 16:32
  • The issue is that the rendering of the table and your data are disjoint. Assuming that you cannot fill every single cell in the table of have to build the entire table and then lookup the data from the XML file with the day and time. – Michael-O Jul 23 '11 at 16:52