Section 3 Notes

by Blake Meike, former teaching fellow

Menu

Using Xalan

You've probably noticed that this project doesn't involve Java at all. Since there is no need to compile anything, there's really no need for ant. We've provided it as a handy way to run Xalan, but, underneath the covers, it is doing this:
java -cp whereever/you/put/xalan.jar org.apache.xalan.xslt.Process
The static main method, in the Process class, looks for the following arguments For instance, you would transform the blockbuster.xml file, as required in the first part of the assignment, from the project directory, with the following (on a single line):
java -cp whereever/you/put/xalan.jar org.apache.xalan.xslt.Process
 	-IN myblockbuster/xml/myblockbuster.xml
	-XSL myblockbuster/xsl/myblockbuster.xsl
	-OUT myblockbuster/xhtml/myblockbuster.html

Template Matching

A template matches what you say it matches, in the "match" attribute. Refering to the document below, e.g., a template with the match clause: match="/condo/owner" will be instantiated three times, once for each of the owner elements. There are a three of places that might need further discussion. The first is a template that doesn't have a match attribute. Such a template must have a name attribute (e.g., name="fmtTable"), and must be instantiated, explicitly, with an "xsl:call-template" element. The second place that the match behaviour is interesting, is in context. Suppose some template, with the match attribute: match="/condo/unit" contained the following code:
<xsl:apply-templates select="rooms/room">
The templates that will be applied are those that have a match attribute for "room", not those that match "/condo/unit/rooms/room" or even those that match "rooms/room". Makes sense, huh?

Finally, there are the default templates. A good way to think about these is to consider, "how does an XSL engine get started?" Notice that, if you run Xalan on some XML document, it prints the document. Why?

The answer is that XSL engines have default templates, and some rules for applying them. The "rules" part says, essentially, that the most specific rule that applies is the one that is used. Here are the templates:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

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

These are fiendishly clever. The first one applies to the root element, and then applies itself to every sub-element of the root, and so on, except when the element is text or an attribute. In the latter case, the second rule is more specific (text() is more more specific that *), so it applies.

Of equal importance is that match="/" is more specific than the first default rule. If you put a template with a match="/" attribute in it, into your stylesheet, it will apply, instead of the default template, because it is more specific than "*|/".

Recursive Iteration

Suppose that you you wanted to select all of the rooms in the sample document below, and put them in an html table with three columns. Selecting the rooms is easy: select="/condo/unit/rooms/room". But how would we format them like so:
<table>
  <tr>
    <td>Kitchen</td>
    <td>Bath</td>
    <td>Living Room</td>
  </tr>
  <tr>
    <td>Study</td>
    <td>Bed Room</td>
    <td>Kitchen</td>
  </tr>
  .
  .
  .
You might want to stop here and have a shot at this, before you go on. It turns out to be a bit tricky. If you are like most of us you'll find yourself trying to use count() or position() in ways that, since they are part of XPath, they cannot be used.

One solution will look familiar to anyone who has worked with Lisp (or another functional programming language. Here's most of it:

<xsl:template match="/">
  <table>
    <xsl:call-templates name="fmtTable">
      <xsl:with-param name="rooms" select="condo/unit/rooms/room" />
    </xsl:call-templates>
  </table>
</xsl:template>


<xsl:template name="fmtTable">
  <xsl:param name=rooms"/>
  <tr>
    <xsl:for-each select="$rooms[position < 3]">
      <td><value-of select="." /></td>
    </xsl:for-each>
  </tr>
  <xsl:call-template name="fmtTable">
    <xsl:with-param name="rooms" select=$rooms[position >= 3] />
  </xsl:call-template>
</xsl:template>
Now, this fragment has the unfortunate problem that it will recur until it runs out of space. But you can see how to fix that, right?

XPath Exercises

You should be able to write a couple of different expressions to obtain each of the following:
  1. All of the owners that have Dining Rooms
  2. All of the rooms on the first floor
  3. The floor on which each of the Kitchens is situated
  4. Owners that have more than one Bath
  5. A list of unique room names (no duplcates!)

One solution for the first exercise is:

/condo/unit/rooms/room[.="Dining Room"]/../../owner
But, you, of course, can do much better...

The last one is also pretty tricky. To solve it, you might consider what it means to be "unique". Perhaps, one way to get the unique items from the list would be to order the list of all rooms -- in fact, they already have a "natural" order -- and then you might walk the list. As you reach each new node, you might keep it, only if it was different from everything you'd seen already.

Sample XML

<condo>
  <unit id="1">
    <owner>Rachel</owner>
    <rooms>
      <room floor="1">Kitchen</room>
      <room floor="1">Bath</room>
      <room floor="1">Living Room</room>
      <room floor="1">Study</room>
      <room floor="1">Bed Room</room>
    </rooms>
  </unit>
  <unit id="2">
    <owner>Lou</owner>
    <rooms>
      <room floor="2">Kitchen</room>
      <room floor="2">Bath</room>
      <room floor="2">Living Room</room>
      <room floor="2">Study</room>
      <room floor="2">Bed Room</room>
    </rooms>
  </unit>
  <unit id="3">
    <owner>Blake & Cathy</owner>
    <rooms>
      <room floor="1">Kitchen</room>
      <room floor="1">Bath</room>
      <room floor="1">Dining Room</room>
      <room floor="1">Living Room</room>
      <room floor="2">Study</room>
      <room floor="2">Study</room>
      <room floor="2">Bath</room>
      <room floor="2">Bed Room</room>
    </rooms>
  </unit>
</condo>