★ wanayoo — archive 1999 http://developer.iplanet.com/viewsource/marchal_xml2/marchal_xml2.htmlNouvelle recherche | Portail wanayoo
iPlanet

You are here:  Home > Developers > View Source Articles > Java View Source Article
Java View Source Article
 iPlanet Developers


Developer Program
  Membership
  One-to-One Support
  Newsgroups

Developer Publications
  View Source
  Developer News

Documentation
  Technical Manuals
  White Papers
  TechNotes
  Sample Code
  FAQs
  Books

Technologies
  CORBA
  Directory & LDAP
  Dynamic HTML
  Java
  JavaScript
  Linux
  Security
  SSJS
  XML

Developer Downloads
  Tools & SDKs
  Patches

iPlanet Products
  Technical Resources










spacer
More Servlet Programming for Teams
Style Sheet Changes and Advanced Techniques

By Benoît Marchal


Send comments and questions about this article to View Source.

In my article Servlet Programming for Teams: How Java Programmers and HTML Designers Can Collaborate Using XML, I introduced a technique for cleanly separating the presentation aspect from the application logic in Java servlets - a separation that matches the division of work between HTML designers and Java developers. The technique is based on XML (Extensible Markup Language) and XSL (XML Stylesheet Language). This article expands on that one, introducing recent changes to XSL and some advanced techniques for style sheets. I'll show you how to use XSL to create compelling web pages.

I'll assume you've installed the software introduced in the earlier article. Although it's not strictly required, I'd advise you to read that article if you haven't done so already.

XSL UPDATE

XML and XSL were developed by the W3C (World Wide Web Consortium), the organization that brought us HTML and the web. XML was formally approved in 1998, whereas XSL was only recently approved (in late November). While developing XSL, the W3C regularly amended the specifications, sometimes making changes that weren't compatible with older versions. This means that you need to revisit your style sheets and upgrade them as necessary. The good news is that most changes have been minor.

This article integrates the changes made to the XSL standard since I wrote my last article, mainly by using the following new format for the xsl:stylesheet element:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/TR/REC-html40">

The changes to this element are minimal: the value of the xmlns:xsl attribute has changed, result-ns has been removed, and a new attribute, version, has been introduced. If you download the latest version of XT from James Clark's site, you'll need to update your existing style sheets, because XT no longer recognizes the old format.

Also, contrary to what I noted in my earlier article, the xsl:output element is now part of XSL.

A DIFFERENT APPROACH FOR STYLE SHEETS

In my earlier article, I presented style sheets that looked a lot like a mail merge, with special fields being replaced by values. Those style sheets consisted essentially of HTML documents containing xsl:value-of elements where values from the XML document were to be inserted. Here I'll use a different approach to writing style sheets, based on the xsl:apply-templates element. Listing 1 demonstrates this approach, with a style sheet for the product list created in the earlier article. You can copy this new style sheet into the document directory (along with the other style sheets) and select it with the xsl parameter in the URL. For example:

http://localhost/productlist?xsl=/simple_productlist_en.xsl.


Listing 1
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/TR/REC-html40">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>Shop</TITLE>
   </HEAD>
   <BODY>
      <P>Select a product:</P>
      <xsl:apply-templates/>
      <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bshoppingcart%26quot%3B%26gt%3B&y=1999
         <INPUT TYPE="SUBMIT" VALUE="Shopping cart"/>
      </FORM>
      EN | <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bproductlist%3Fxsl%3D%2Fproductlist_fr.xsl%26quot%3B%26gt%3BFR%26lt%3B%2FA%26gt%3B&y=1999
   </BODY>
   </HTML>
</xsl:template>

<xsl:template match="product">
   <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
      <INPUT TYPE="HIDDEN" NAME="add">
         <xsl:attribute name="value">
            <xsl:value-of select="@id"/>
         </xsl:attribute>
      </INPUT>
      <xsl:apply-templates/>
      <INPUT TYPE="SUBMIT" VALUE="Buy"/>
   </FORM>
</xsl:template>

<xsl:template match="name">
   <B><xsl:apply-templates/></B><BR/>
</xsl:template>

<xsl:template match="description[@xml:lang='EN']">
   <xsl:apply-templates/><BR/>
</xsl:template>

<xsl:template match="description[not(@xml:lang='EN')]"/>

<xsl:template match="price">
   <I><xsl:apply-templates/></I><BR/>
</xsl:template>

<xsl:template match="image">
   <IMG>
      <xsl:attribute name="SRC"><xsl:apply-templates/></xsl:attribute>
   </IMG><BR/>
</xsl:template>

</xsl:stylesheet>

If you compare this style sheet with the ones in the earlier article, you'll notice that it has several xsl:template elements. Each of these elements will match an element in the XML document; for example, <xsl:template match="name"> matches the name element.

When the XSL processor encounters an element that matches a template, it replaces the element with the content of the template. Thus, when the processor encounters a name element, it replaces it with

<B><xsl:apply-templates/></B><BR/>

The xsl:apply-templates element is essentially a recursive call to the style sheet: when the XSL processor encounters xsl:apply-templates, it goes one level down in the source document and applies the style sheet again.

Following the Processor

As an illustration, let's follow the XSL processor when it formats the document in Listing 2. This is the list of products, in XML, generated by the servlet written in the earlier article.


Listing 2
<?xml version="1.0" encoding="ISO-8859-1"?>
<products>
   <product id="0">
      <name>WhizBang Ultra Word Processor</name>
      <description xml:lang="EN">More words per minute than the competition.</description>
      <description xml:lang="FR">Plus de mots à la minute que la concurrence.</description>
      <image>wordprocessor.jpg</image>
      <price>$799.99</price>
   </product>
   <product id="1">
      <name>Super WhizBang Calculator</name>
      <description xml:lang="EN">Cheap and reliable with power saving.</description>
      <description xml:lang="FR">Economique et fiable avec économie d'énerge.</description>
      <image>calculator.jpg</image>
      <price>$5.99</price>
   </product>
   <product id="2">
      <name>WhizBang Safest Safe</name>
      <description xml:lang="EN">Choose the authentic WhizBang Safest Safe.</description>
      <description xml:lang="FR">Exigez l'original!</description>
      <image>safe.jpg</image>
      <price>$1,999.00</price>
   </product>
</products>

Here's what happens:

  1. The processor selects the <xsl:template match="/"> template in the style sheet (Listing 1), because it matches the root of the XML document. (XML paths in style sheets work a lot like paths in a file system, and / is the root of the document.) Since the processor first matches the root, that template is the ideal place to create the HTML element as well as the HEAD and BODY elements.

    The template contains the xsl:apply-templates element to force the processor to move one level down in the document and recursively call the style sheet.

  2. The processor is positioned on the products element, so it looks for a template that matches products. The style sheet doesn't define any templates for products, so the processor needs to match against a built-in template. The built-in template isn't defined in the style sheet but is predefined by the processor, and it looks like this (where * means any element and | means "or"):

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

    The built-in template exists solely to call the style sheet recursively. It doesn't create HTML code, but it does make sure the processor doesn't stop dead in its tracks. The xsl:apply-templates element in this template moves the processor one level down.

  3. The processor is positioned on the first product element; it searches the style sheet for a template that matches product and finds one. The product template creates an HTML form. You should recognize the familiar xsl:value-of element for inserting the value of the id attribute. This template also contains an xsl:apply-templates element, which continues the recursive processing.

  4. The processor is positioned on the name element, and it searches for the corresponding template. The template for name creates a bold HTML element and recursively calls the style sheet through xsl:apply-templates.

  5. The processor is positioned on the text content of the name element (WhizBang Ultra Word Processor). The style sheet defines no template for text, so the processor turns to another built-in template, which simply copies the text into the HTML file:

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

    The processor has reached a leaf in the document, so it goes one level up in the source document and applies the style sheet again.

  6. The processor is positioned on the description element. There are two templates for description; a test in square brackets selects which element should match:

    • description[@xml:lang='EN'] matches the English descriptions.
    • description[not(@xml:lang='EN')] matches the other (non-English) descriptions. Note that this template doesn't contain xsl:apply-templates, so the processor doesn't go further in this branch. This is how we filter out the non-English descriptions.

And so on. You've probably recognized the algorithm for a deep-first search: the processor walks through the XML document; at each step, it selects a template in the style sheet and applies it.

Comparing the Two Approaches

The major difference between this style sheet and the ones used in the earlier article is that those were built around the resulting HTML document, whereas this style sheet is built around the input XML document.

The earlier style sheets were similar to the mail merge feature of word processors - again, because special fields are replaced by actual values. In contrast, this style sheet specifies how to format each element in an XML document.

In practice, you'll find that the earlier style sheets are ideal for prototypes because they're so close to the final HTML document. However, because it's organized by element, the type of style sheet used in this article is easier to maintain. In particular, changes to the presentation of one element do not affect other elements.

MORE FUN WITH XSL

Listing 3 is an advanced style sheet for the product list that demonstrates several new XSL features. You apply this style sheet using the xsl parameter, as we've seen before.


Listing 3
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/TR/REC-html40">

<xsl:output method="html"/>

<xsl:template match="/">
   <HTML>
   <HEAD>
      <TITLE>Shop</TITLE>
   </HEAD>
   <BODY>
      <P>Quick access:</P>
         <xsl:apply-templates mode="toc"/>
      <P>Select a product:</P>
      <xsl:apply-templates/>
      <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bshoppingcart%26quot%3B%26gt%3B&y=1999
         <INPUT TYPE="SUBMIT" VALUE="Shopping cart"/>
      </FORM>
      EN | <A HREF=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bproductlist%3Fxsl%3D%2Fproductlist_fr.xsl%26quot%3B%26gt%3BFR%26lt%3B%2FA%26gt%3B&y=1999
   </BODY>
   </HTML>
</xsl:template>

<xsl:template match="product" mode="toc">
   <xsl:apply-templates select="name" mode="toc"/>
   <!-- -1 is to account for the "newline" text node -->
   <xsl:if test="not(position()=last()-1)"> | </xsl:if>
</xsl:template>

<xsl:template match="name" mode="toc">
   <A>
      <xsl:attribute name="HREF">
         <xsl:text>#</xsl:text>
         <xsl:value-of select="generate-id(..)"/>
      </xsl:attribute>
      <xsl:apply-templates/>
   </A>
</xsl:template>

<xsl:template match="product">
   <FORM ACTION=/old?u=http%3A%2F%2Fdeveloper.iplanet.com%2Fviewsource%2Fmarchal_xml2%2F%26quot%3Bshoppingcart%26quot%3B&y=1999 METHOD="POST">
      <INPUT TYPE="HIDDEN" NAME="add">
         <xsl:attribute name="value">
            <xsl:apply-templates select="@id"/>
         </xsl:attribute>
      </INPUT>
      <xsl:apply-templates select="image"/>
      <xsl:apply-templates select="name"/><xsl:text> </xsl:text>
      <xsl:apply-templates select="price"/><BR/>
      <xsl:apply-templates select="description"/><BR/>
      <INPUT TYPE="SUBMIT" VALUE="Buy"/>
      <BR CLEAR="LEFT"/>
   </FORM><BR/>
</xsl:template>

<xsl:template match="name">
   <B><xsl:apply-templates/></B>
</xsl:template>

<xsl:template match="description[not(@xml:lang='EN')]"/>

<xsl:template match="image">
   <A>
      <xsl:attribute name="NAME">
         <xsl:value-of select="generate-id(..)"/>
      </xsl:attribute>
      <IMG ALIGN="LEFT">
         <xsl:attribute name="SRC">
            <xsl:apply-templates/>
         </xsl:attribute>
      </IMG>
   </A>
</xsl:template>

</xsl:stylesheet>

Attributes

Notice that some xsl:apply-templates and xsl:template elements have a new attribute named mode. Modes are used for walking through the XML document several times while applying different templates. <xsl:apply-templates mode="toc"/> is like a regular xsl:apply-templates element except that the processor applies only templates that have the corresponding mode attribute. This is useful for generating a "table of contents" (in this case, a list of hyperlinks to all the products).

In addition, the xsl:apply-templates element can take a select attribute, causing the processor to limit itself to elements whose names match the select parameter. The select attribute is commonly used to reorganize elements. In the product template, for example, it's used to move the image element before the name element (and price before description).

Elements

This style sheet uses two new elements:

  • xsl:text, for delimiting text
  • xsl:if, for testing a condition

In most cases, to insert text into the HTML document you simply type it in the style sheet. However, because newline characters and spaces are also used to improve the readability of the style sheet, you might not always get the results you want. You can use the xsl:text element to unambiguously delimit text. Here's how it's used in this style sheet:

  • In the template for name, xsl:text is used to separate the # character from the newline character and spaces that have been added for readability. If xsl:text had not been used here, the spaces preceding the # character would have been included in the HTML document.
  • In the template for products, xsl:text is used to insert a single space between the name and the price. Otherwise, the processor would have ignored the space, incorrectly assuming it was meant for readability, and the name and price would have been run together.

The xsl:if element is used to conditionally generate the output. Its test attribute is a condition that's evaluated; if the condition is true, the content of the xsl:if element is inserted in the result, otherwise nothing is produced.

Functions

XSL supports several functions. We've already seen the text() and not() functions: text() matches the text content of an element; not() returns true if its argument was false, and vice versa. This style sheet also uses the position() and last() functions to test for the last product: position() is the position of the current element or text in the context of its parent (for example, 1 if it's the first product element or text in the products element, or 2 if the second), and last() is the position of the last such element or text.

You might wonder why position() is tested against last()-1. Notice that in the XML document in Listing 2, each element is on a different line - that is, there's a newline character after each element. The newline character is counted as text by the position() and last() functions. Therefore last() points to a newline character, not a product element; that's why 1 has to be subtracted from it.

The style sheet also uses the generate-id() function, which returns a unique identifier for an element. This function is particularly useful when you're creating links within the document.

YOUR TURN

This article and my earlier one have introduced you to some practical applications of XML and XSL. As you gain familiarity with XML and XSL, you'll find that they're very flexible and can solve many problems.

I couldn't cover all the elements and functions of XSL, but I hope I've given you the tools and willingness to learn more. If you're interested, I invite you to read a good tutorial on XML - and, if you'll forgive the shameless plug, I'd recommend my own book, XML by Example.

I'm very interested in hearing about your experiences with XML and XSL. Please write to me and tell me about them.


View Source wants your feedback!
Write to us and let us know
what you think of this article.

Many thanks to the readers who commented on my earlier article.

Benoît Marchal is a software engineer and consultant based in Namur, Belgium, who has been working extensively in Java and XML. He runs his own software company, Pineapplesoft. He also likes teaching and writing; his book XML by Example has been published by Que.

(12.99)

Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use

spacer spacer


                                                       
iPlanet International | Year 2000 | Site Map | Feedback
Products | Solutions | Support | Services | Download | About Us | Developer
© 2000 Sun-Netscape Alliance. All Rights Reserved  Privacy Policy