home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book HomeJava and XSLTSearch this book

7.2. Prototyping the XML

Once the requirements and screen flow are well understood, it is possible to move on to a more rigorous design process. Web sites based on XSLT are highly modular, facilitating a design and implementation process that can be farmed out to several members of a development team. Each piece can be developed and tested independently before finally bringing everything together into the completed web application.

7.2.1. XHTML Prototypes

Creating user interface prototypes is an early task that can be handed off to less experienced programmers or perhaps to a dedicated web page designer. At this stage in the game, an overly complex and graphical web interface is not required. The bells and whistles can be added later by merely updating XSLT stylesheets. In fact, too much effort at this early stage can make it more difficult to figure out what the XML and XSLT should look like.

Since the front end will be created using XHTML Strict, a separate cascading style sheet (CSS) will be required to make the pages look presentable.[25] The strict variant of XHTML does not allow most of the HTML 4.0 formatting tags, but instead encourages the use of CSS. Example 7-1 contains the complete CSS file used by the discussion forum.

[25] See http://www.w3.org/TR/xhtml1 for more information on XHTML Strict.

Example 7-1. forum.css

body {
  font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
}

.box1 { 
  border: 3px solid Navy;
  text-align: center;
  padding: 4px;
  margin : 2px;
  background-color: #c0c0c0;
}

.box2 { 
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

h1 { 
  font-size: 22pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h2 {
  font-size: 18pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h3 {
  font-size: 14pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

ul { 
  margin-top: 0px;
}

.msgSummaryLine { 
  font-size: smaller;
  font-weight: normal;
}

a:hover { 
  background-color:yellow;
}
 
.error { 
  font-weight: bold;
  color: red;
}

Each of the XHTML web pages refers to this CSS file using the following syntax:

<link type="text/css" rel="stylesheet" href="/forum/forum.css" />

This is a great technique because it keeps the size of the XSLT stylesheets and each XHTML page much smaller. Changes to fonts and colors can be made in the single CSS file and are immediately reflected throughout the web application. The primary obstacle at this time is noncompliant web browsers. Although support for CSS is gradually improving, web pages must be tested on a variety of browsers to identify formatting problems.

NOTE: A common theme presented throughout this book is the separation of data and presentation that XSLT supports. CSS expands upon this theme by separating XHTML content from many aspects of its visual presentation. CSS and XSLT are very different technologies that complement one another nicely.

Most of the code in a CSS file is fairly self-explanatory. For example, the h2 style applies to <h2> elements in the XHTML. One style element that many programmers may not be familiar with is:

.box2 { 
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

The dot in .box2 indicates a style class definition. Here is how the box2 style class is used in the XHTML:

<div class="box2">Messages for March, 2001</div>

The advantage of a style class is that it can be applied to any element in the XHTML. In this case, a thin border and yellow background are applied to any element that has the box2 class.

The web page designers should create basic representations of every page in the application at this point. The home page is shown in Figure 7-2.

Figure 7-2

Figure 7-2. Home page prototype

The complete XHTML source code for the home page is shown in Example 7-2. As shown, the actual hyperlinks are not valid because the design for the servlets has not been completed, and the final URLs are probably unknown at this point. At any rate, this is only prototype code, because the actual XHTML web pages are dynamic and will be produced as the result of an XSLT transformation from XML data.

Example 7-2. Home page XHTML source

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Discussion Forum Home</title>
    <link href="../docroot/forum.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div class="box1">
      <h1>Discussion Forum Home</h1>
    </div>
    <div class="box2">
      <h2>Java Programming</h2>
      <div>General programming questions about Java.</div>
      <div style="margin-left: 2em;">
        <p>
          <a href="link_to_post_message">Post Message</a>
        to Java Programming</p>
        <a href="link_to_feb_messages">Feb, 2001</a> | 
        <a href="link_to_mar_messages">Mar, 2001</a> | 
        <a href="link_to_apr_messages">Apr, 2001</a>
      </div>
    </div>
    <div class="box2">
      <h2>XSLT Basics</h2>
      <div>Writing effective XSLT stylesheets.</div>
      <div style="margin-left: 2em;">
        <p>
        <a href="link_to_post_message">Post Message</a> to XSLT Basics</p>
        <a href="link_to_feb_messages">Feb, 2001</a> | 
        <a href="link_to_mar_messages">Mar, 2001</a> | 
        <a href="link_to_apr_messages">Apr, 2001</a>
      </div>
    </div>
    <div class="box2">
      <h2>Sample empty board</h2>
      <div>This board has no messages.</div>
      <div style="margin-left: 2em;">
        <p>
          <a href="link_to_post_msg">Post Message</a>
        to Sample empty board</p>
      </div>
    </div>
  </body>
</html>

<div> and <span> tags may be unfamiliar because they were ignored by many HTML authors until CSS became more prevalent. Basically, a <div> tag is wrapped around any number of other elements, turning them into a block-level element group. The <span> tag is similar, but it is an inline element. This means that <span> tags will be embedded into the current line, while <div> tags will wrap to a new line much like <p> or <h1> tags do. The ability to define style classes make <div> and <span> particularly useful for XHTML Strict, which disallows deprecated HTML 4.0 elements such as <font>. Although <span> is not used in this particular example, <div> is used frequently to introduce line breaks and to apply styles using CSS.

The next prototype, shown in Figure 7-3, shows what a message board looks like. XHTML source code for the remaining screens is not listed here.

Figure 7-3

Figure 7-3. View Month prototype

Messages that are replies to other messages are indented a few spaces. Later, a simple change to the XSLT stylesheet can be employed to show graphical folders or other icons in front of each message. The next screen, shown in Figure 7-4, shows how users can post new messages to the discussion forum.

Figure 7-4

Figure 7-4. Post Message prototype

This page is also used to reply to an existing message. Although not shown here, the title changes to "Reply to Message," and the subject and message text are pre-filled with text from the original message. If the user submits this form without filling in all values, the web page is redisplayed with an error message.

The final screen prototype is shown in Figure 7-5. This screen allows users to view existing messages.

Figure 7-5

Figure 7-5. View Message prototype

7.2.2. XML Samples

While page designers are hard at work on the XHTML prototype screens, someone else can be working on sample XML data for each web page. Although different people may work on these tasks, a certain degree of coordination is critical at this point. The prototype XHTML pages may look great, but the XML must provide the data to enable those pages to be created. The XML designer will also have to work with the people who are designing the back-end data sources to determine if the desired data is even available.

When designing XML, the focus should be on data rather than presentation. All of the fonts and colors that are part of the CSS should have absolutely zero impact on the design of the XML. The XML will contain additional data that is not displayed, however. For example, creating hyperlinks requires some sort of identifier for each object. This allows the servlet to figure out which message the user clicked on. The XML data contains the identifier for the message, but the actual XHTML markup for the hyperlink comes from an XSLT stylesheet.

The XML data for the home page is shown in Example 7-3. Because the XML does not contain presentation information, it is smaller than the XHTML markup.

Example 7-3. home.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/home.xslt"?>
<home>
  <board id="0">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="1">
    <name>XSLT Basics</name>
    <description>Writing effective XSLT stylesheets</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="3">
    <name>Sample empty board</name>
    <description>This board has no messages.</description>
  </board>
</home>

Do not forget that this is still just a prototype XML file. The actual XML data will be dynamically generated by JDOM once the application is finished; this XML prototype code is used only for testing and development purposes.

In this XML data, each message board is represented by a <board> element that has an id attribute. When the user clicks on the "Post Message" web page hyperlink, this id is used to figure out which message board he or she wants to post to. The list of <messages> elements indicates months that have messages in them. These do not need id attributes because the month and year are used in the hyperlink.

The second line of the XML links to the XSLT stylesheet:

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

This is not used in the final application but is very useful during the prototyping and development process. By linking to the stylesheet, the transformation can be quickly viewed in an XSLT-compatible web browser by simply loading the XML page.

Iterative Design

 

The examples shown in this chapter are the result of several attempts to get the design "right." As in other areas of software design, figuring out what to place in the XHTML, XML, and XSLT is an iterative process that requires several attempts before the design can be finalized.

 

In a nutshell, the process works something like this:

 
  • Prototype the web pages using HTML or XHTML.

  • Create the XML datafile prototypes and optionally create DTDs.

  • Create XSLT stylesheets that transform the XML into XHTML.

  • Design and create back-end data sources and classes that know how to produce the required XML data.

  • Create servlets that tie everything together.

As each piece of the application is implemented, missing or redundant features will manifest themselves in other areas. This is where the iterative process comes into effect. If some features are not right the first time, simply refine the prototypes and repeat various steps in the process until all the pieces fit together.

The next XML file, shown in Example 7-4, contains data for the "View Month" page.

Example 7-4. viewMonth.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMonth.xslt"?>
<viewMonth month="1" year="2001">
  <board id="1">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
  </board>
  <message id="1" day="1">
    <subject>First test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
    <message id="2" day="2">
      <subject>Re: First test message</subject>
      <authorEmail>aidan@nowhere.com</authorEmail>
    </message>
  </message>
  <message id="3" day="4">
    <subject>Another test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
  </message>
</viewMonth>

Moving on to Example 7-5, we have the XML for the "Post/Reply Message" page.

Example 7-5. postMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/postMsg.xslt"?>
<postMsg>
  <board id="1">
    <name>Java Programming</name>
    <description>The board description...</description>
  </board>
  <inResponseTo id="4">
    <subject>Test Subject</subject>
  </inResponseTo>
  <error code="ALL_FIELDS_REQUIRED"/>
  <prefill>
    <subject>Test Subject</subject>
    <authorEmail></authorEmail>
    <message>My Message</message>
  </prefill>
</postMsg>

This XML is used for both posting new messages and replying to existing messages because the web pages are virtually identical, and the data is the same in both cases. The <error> and <prefill> elements were not part of the original prototype, but it was quickly determined that these were needed if the user did not provide information for all required fields. When the "Post New Message" page is first displayed, these XML elements are not present. After the user clicks on the Submit button, however, these elements are inserted into the XML if a field is missing and the page needs to be redisplayed.

And finally, the XML for the "View Message" page is shown in Example 7-6.

Example 7-6. viewMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMsg.xslt"?>
<message id="5" month="1" day="4" year="2001">
  <board id="1">
    <name>Java Programming</name>
  </board>
  <inResponseTo id="4">
    <subject>Test Subject</subject>
  </inResponseTo>
  <subject>Re: Test Subject</subject>
  <authorEmail>burke_e@yahoo.com</authorEmail>
  <text>This is a test of the message
   text.</text>
</message>

A quick study of this data reveals that postMsg.xml and viewMsg.xml have many similarities. A few modifications to either XML file will enable us to reuse the same JDOM code later when producing these pages. The alternative is to keep these pages separate, which results in at least one additional Java class later on. The advantage of keeping these files separate is so that the XML generation code does not have to be cluttered up with a lot of if/else statements to figure out the mode of operation it is in.

7.2.3. XSLT Stylesheets

Yet another member of the development team can be assigned to the task of creating XSLT stylesheets, although he or she will have to wait until the XML and XHTML prototypes are complete. More often than not, the person designing the XML will be the one creating the initial XSLT stylesheets.

At this point in the process, a tool such as XML Spy can be invaluable.[26] The ability to edit the XSLT stylesheet and click on the Refresh button in an IDE makes development a snap. Alternately, an XSLT-compatible web browser can quickly display changes as stylesheets are edited. As explained in Chapter 1, "Introduction ", Microsoft's Internet Explorer 5.x supports XSLT, provided that the updated msxml parser is installed using the xmlinst utility.[27]

[26] XML Spy is a commercial XML editor that works nicely for XSLT development. It is available at http://www.xmlspy.com.

[27] As this is written, IE 6.0 is in beta testing. It supports the latest XSLT specification. The Mozilla browser will also support XSLT at some point.

Example 7-7 shows the XSLT for the discussion forum home page.

Example 7-7. XSLT for the home page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** home.xslt
**
** Transforms the home page into XHTML
***********************************************************
-->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  <xsl:param name="rootDir" select="'../docroot/'"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" 
    indent="yes" 
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" 
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  
  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Discussion Forum Home</title>
        <link href="{$rootDir}forum.css" 
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <div class="box1">
          <h1>Discussion Forum Home</h1>
        </div>
        <xsl:apply-templates select="home/board"/>
      </body>
    </html>
  </xsl:template>
  
  <!--
  **********************************************************
  ** Output a box for each board in the discussion forum
  **********************************************************
  -->
  <xsl:template match="board">
    <xsl:variable name="boardID" select="@id"/>
    
    <div class="box2">
      <h2><xsl:value-of select="name"/></h2>
      <div><xsl:value-of select="description"/></div>
      <div style="margin-left: 2em;">
        <!-- create a link so the user can post a new message
             to this board -->
        <p>
        <a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Post Message</a>
        to <xsl:value-of select="name"/>
        </p>
        
        <!-- For each month that has messages, show the 
             month name and year number as a link -->
        <xsl:for-each select="messages">
         <ahref="forum/main/viewMonth?boardID={$boardID}&amp;month={@month}&amp;year=
         {@year}">
            <xsl:call-template name="utils.printShortMonthName">
              <xsl:with-param name="monthNumber" select="@month"/>
            </xsl:call-template>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="@year"/>
          </a>
          <!-- put a pipe character after 
               all but the last month -->
          <xsl:if test="position() != last( )">
            <xsl:text> | </xsl:text>
          </xsl:if>
        </xsl:for-each>
      </div>
    </div>
  </xsl:template>
</xsl:stylesheet>

This stylesheet opens with the usual <xsl:stylesheet> tag and then proceeds to import utils.xslt. This is a stylesheet that contains common templates for formatting dates. Since these utilities are needed on just about every page, they are defined a common file that is imported, as shown here. This stylesheet also takes a parameter named rootDir, allowing the web application to specify the location of the document root directory:

<xsl:param name="rootDir" select="'../docroot/'"/>

The select attribute defines a default value for this parameter if none was specified. During the stylesheet development process, the XSLT is tested using a static XML file. This is done outside of a web application, so the parameter is not specified and the root directory defaults to ../docroot/. This makes it possible to locate the CSS file during development, when developers are working from a static directory structure on their file systems. Later, when the XSLT stylesheet is deployed to a web application and the servlet is running, the servlet can specify a different value for this parameter that indicates a directory relative to the web application context. This is a useful technique whenever a stylesheet has to reference external resources such as CSS files, JavaScript files, or images.

Next, the <xsl:output> element is used to set up XHTML output. The XHTML 1.0 Strict DTD is used, which eliminates many deprecated HTML 4.0 features. Because the strict DTD does away with many formatting tags, a CSS file is required to make the pages look presentable. All the XSLT needs to do is produce HTML code that references the external stylesheet, as shown here:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Discussion Forum Home</title>
    <link href="{$rootDir}forum.css" 
        rel="stylesheet" type="text/css"/>
  </head>

The XSLT processor does not actually deal with the CSS file. From the perspective of XSLT, the <link> tag is just text that is copied to the result tree during the transformation process. Later, when the web browser displays the XHTML page, the actual CSS file is loaded. This technique is great because styles can be shared across all web pages without complicating the XSLT stylesheets.

The remainder of the stylesheet is pretty basic -- just matching patterns in the XML and producing XHTML content to the result tree. One important thing to point out here is the way that hyperlinks are created:

<a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Post Message</a>

Since the ampersand character (&) is not allowed in an XML attribute value, it must be written using the &amp; built-in entity. As it turns out, browsers deal with this just fine, and the hyperlink works anyway.[28]

[28] We will see this again when dealing with WML in Chapter 10, "Wireless Applications".

What Is the URL?

 

You may be wondering how you are supposed to know what each hyperlink is actually supposed to be. At this stage of the game, you probably will not know, and your links will actually look something like this:

 
<a href="TODO: link to post a new message">Post Message</a>
 

This is fine for now, because you really won't know what to put there until the servlets are fully designed. Part of the servlet design process involves figuring out what parameters are required and what the legal values are. Until this work has been completed, however, an educated guess or "TODO" comment is fine.

Another key piece of this stylesheet shows how to call a utility template:


<xsl:call-template name="utils.printShortMonthName">
  <xsl:with-param name="monthNumber" select="@month"/>
</xsl:call-template>

The utils.printShortMonthName template is part of utils.xslt and is invoked just like a local template. The only difference is that the current stylesheet must import utils.xslt or the code will fail. Prefixing the template name with utils. has nothing to do with the actual filename; it is a convention adopted only for this application that makes the code a little easier to read, reducing the chances for naming conflicts.

The reusable XSLT stylesheet, utils.xslt, is shown next in Example 7-8.

Example 7-8. Reusable XSLT code

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <xsl:template name="utils.printShortMonthName">
    <xsl:param name="monthNumber"/>
    <xsl:choose>
      <xsl:when test="$monthNumber='0'">Jan</xsl:when>
      <xsl:when test="$monthNumber='1'">Feb</xsl:when>
      <xsl:when test="$monthNumber='2'">Mar</xsl:when>
      <xsl:when test="$monthNumber='3'">Apr</xsl:when>
      <xsl:when test="$monthNumber='4'">May</xsl:when>
      <xsl:when test="$monthNumber='5'">Jun</xsl:when>
      <xsl:when test="$monthNumber='6'">Jul</xsl:when>
      <xsl:when test="$monthNumber='7'">Aug</xsl:when>
      <xsl:when test="$monthNumber='8'">Sep</xsl:when>
      <xsl:when test="$monthNumber='9'">Oct</xsl:when>
      <xsl:when test="$monthNumber='10'">Nov</xsl:when>
      <xsl:when test="$monthNumber='11'">Dec</xsl:when>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="utils.printLongMonthName">
    <xsl:param name="monthNumber"/>
    <xsl:choose>
      <xsl:when test="$monthNumber='0'">January</xsl:when>
      <xsl:when test="$monthNumber='1'">February</xsl:when>
      <xsl:when test="$monthNumber='2'">March</xsl:when>
      <xsl:when test="$monthNumber='3'">April</xsl:when>
      <xsl:when test="$monthNumber='4'">May</xsl:when>
      <xsl:when test="$monthNumber='5'">June</xsl:when>
      <xsl:when test="$monthNumber='6'">July</xsl:when>
      <xsl:when test="$monthNumber='7'">August</xsl:when>
      <xsl:when test="$monthNumber='8'">September</xsl:when>
      <xsl:when test="$monthNumber='9'">October</xsl:when>
      <xsl:when test="$monthNumber='10'">November</xsl:when>
      <xsl:when test="$monthNumber='11'">December</xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Month numbers are indexed from position 0 to be consistent with the java.util.Calendar class, which also uses 0 to represent January. The templates convert the month number into an English month name.

viewMonth.xslt is shown in Example 7-9. It generates an XHTML page that shows all messages in a month for a particular board.

Example 7-9. XSLT for the View Month page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** viewMonth.xslt
**
** Shows a month-view of messages in a given board.
***********************************************************
-->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  <xsl:param name="rootDir" select="'../docroot/'"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" 
    indent="yes" 
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" 
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  <!-- ================= Global Variables ================= -->
  <xsl:variable name="global.boardName" select="/viewMonth/board/name"/>
  <xsl:variable name="global.boardID" select="/viewMonth/board/@id"/>
  <xsl:variable name="global.monthNum" select="/viewMonth/@month"/>
  <xsl:variable name="global.yearNum" select="/viewMonth/@year"/>
  
  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>
          <xsl:value-of select="$global.boardName"/>
        </title>
        <!-- reference an external CSS file to keep this
             XSLT stylesheet smaller -->
        <link href="{$rootDir}forum.css" 
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <div class="box1">
          <h1>
            <xsl:value-of select="$global.boardName"/>
          </h1>
        </div>
        <div class="box2">
          <xsl:text>Messages for </xsl:text>
          <xsl:call-template name="utils.printLongMonthName">
            <xsl:with-param name="monthNumber" select="$global.monthNum"/>
          </xsl:call-template>
          <xsl:text>, </xsl:text>
          <xsl:value-of select="$global.yearNum"/>
        </div>
        <!-- ===== Quick Actions ====== -->
        <h3>Quick Actions</h3>
        <ul>
          <li>
            <a href="postMsg?mode=postNewMsg&amp;boardID={$global.boardID}">
          Post</a> a new message...</li>
          <li>Return to the <a href="home">home page</a>...</li>
        </ul>
        <!-- ===== Recursively show the message tree ===== -->
        <xsl:apply-templates select="viewMonth/message"/>
      </body>
    </html>
  </xsl:template>

  <!--
  **********************************************************
  ** Display a one-line summary for each message.
  *******************************************************-->
  <xsl:template match="message">
    <xsl:param name="indent" select="0"/>
    
    <!-- indent according to the 'indent' parameter -->
    <div style="margin-left: {$indent}em;">
      <a href="viewMsg?msgID={@id}">
        <xsl:value-of select="subject"/>
      </a>
      <xsl:text> posted by </xsl:text>
      <xsl:apply-templates select="authorEmail"/>
      <xsl:text> on </xsl:text>
      <xsl:value-of select="@day"/>
      <xsl:text disable-output-escaping="yes">&amp;nbsp;
      <xsl:call-template name="utils.printShortMonthName">
        <xsl:with-param name="monthNumber" select="$global.monthNum"/>
      </xsl:call-template>
            <xsl:text disable-output-escaping="yes">&amp;nbsp;
      <xsl:value-of select="$global.yearNum"/>
      
      <!-- recursively select all messages that are 
          responses to this one. Increment the indentation 
          with each call -->
      <xsl:apply-templates select="message">
        <xsl:with-param name="indent" select="$indent + 1"/>
      </xsl:apply-templates>
    </div>
  </xsl:template>
  
  <!--
  **********************************************************
  ** Show the author's email address.
  *******************************************************-->
  <xsl:template match="authorEmail">
    <a href="mailto:{.}">
      <xsl:value-of select="."/>
    </a>
  </xsl:template>
</xsl:stylesheet>

Because viewMonth.xslt shows a summary view of a large number of messages, the actual text content for each message is not included in the output. Instead, the message subject, author, and create date are displayed. These lines are grouped and indented according to replies, making threads of discussion immediately visible.

This stylesheet declares a series of global variables. These can be referenced throughout the stylesheet and are designed to make the code more maintainable. Since each variable is prefixed with global., the code is easy to understand when using the variables:

<xsl:value-of select="$global.boardName"/>
NOTE: The global. naming convention is not a standard part of XSLT. It is just a convention used here to make the XSLT more self-documenting.

The interesting part of this stylesheet involves construction of the tree of messages. Since messages in the XML are hierarchical, the XSLT must recursively process the data to properly show threads of discussion. Here is another look at a portion of the viewMonth.xml file presented earlier in this chapter:

<viewMonth month="1" year="2001">
  <board id="1">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
  </board>
  <message id="1" day="1">
    <subject>First test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
    <message id="2" day="2">
      <subject>Re: First test message</subject>
      <authorEmail>aidan@nowhere.com</authorEmail>
    </message>
  </message>
  <message id="3" day="4">
    <subject>Another test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
  </message>
</viewMonth>

In the XSLT stylesheet, the first part of the recursive process selects all <message> elements occurring immediately below the <viewMonth> element:

<xsl:apply-templates select="viewMonth/message"/>

This selects messages with ids 1 and 3, causing the following template to be instantiated:

<xsl:template match="message">
  <xsl:param name="indent" select="0"/>

This template takes a parameter for the level of indentation. If the parameter is not specified, as in this first usage, it defaults to 0. This code is followed by very basic XSLT code to produce a one-line summary of the current message, and then the template recursively instantiates itself:

<xsl:apply-templates select="message">
  <xsl:with-param name="indent" select="$indent + 1"/>
</xsl:apply-templates>

This efficiently selects all <message> elements that occur immediately within the current message and increments the indentation by 1. This allows the stylesheet to indent replies appropriately. The recursive process continues until no messages remain.

Another stylesheet, viewMsg.xslt, is responsible for displaying a single message. This is a simple XSLT stylesheet and can be found in Appendix A, "Discussion Forum Code". The only remaining stylesheet, postMsg.xslt, is shown in Example 7-10. This stylesheet supports two modes of operation. Therefore, it is more complicated than the previous examples.

Example 7-10. XSLT for the Post/Reply message page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** postMsg.xslt
**
** Creates the "Post New Message" XHTML page and the
** "Reply to Message" XHTML page.
***********************************************************
-->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  
  <!-- pass the root directory as a parameter, thus
       allowing this stylesheet to refer to the CSS file -->
  <xsl:param name="rootDir" select="'../docroot/'"/>
  
  <xsl:output method="xml" version="1.0" encoding="UTF-8" 
    indent="yes" 
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" 
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

  <!-- ===== Global Variables ===== -->
  <xsl:variable name="global.subject" select="/postMsg/prefill/subject"/>
  <xsl:variable name="global.email" select="/postMsg/prefill/authorEmail"/>
  <xsl:variable name="global.message" select="/postMsg/prefill/message"/>
  
  <xsl:variable name="global.title">
    <xsl:choose>
      <xsl:when test="/postMsg/inResponseTo">
        <xsl:text>Reply to Message</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>Post New Message</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
    
  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title><xsl:value-of select="$global.title"/></title>
        <link href="{$rootDir}forum.css"
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <!-- show the page title and board name -->
        <div class="box1">
          <h1><xsl:value-of select="$global.title"/></h1>
          <div>
            <xsl:value-of select="postMsg/board/name"/>
          </div>
        </div>
        <xsl:apply-templates select="postMsg/inResponseTo"/>
        
        <div class="box2">
          <!-- optionally display error message -->
          <xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
            <p class="error">All fields are required...</p>
          </xsl:if>
          
          <!-- Create an XHTML form. The user will provide
               the subject, and Email address, and 
               the message text -->
          <form method="post" action="postMsg">
            <div>
            <input type="hidden" name="boardID" 
                value="{postMsg/board/@id}"/>
            
            <!-- Determine the mode of operation -->
            <xsl:choose>
              <xsl:when test="/postMsg/inResponseTo">
                <input type="hidden" name="origMsgID"
                         value="{postMsg/inResponseTo/@id}"/>
                <input type="hidden" name="mode" value="replyToMsg"/>
              </xsl:when>
              <xsl:otherwise>
                <input type="hidden" name="mode" value="postNewMsg"/>
              </xsl:otherwise>
            </xsl:choose>
            </div>
            
            <!-- Show the input fields in a table to
                 keep things aligned properly -->
            <table>
              <tr>
                <td>Subject:</td>
                <td>
                  <input type="text" name="msgSubject" 
                      value="{$global.subject}" size="60" maxlength="70"/>
                </td>
              </tr>
              <tr>
                <td nowrap="nowrap">Your Email:</td>
                <td>
                  <input type="text" name="authorEmail" 
                      value="{$global.email}" size="60" maxlength="70"/>
                </td>
              </tr>
              <tr valign="top">
                <td>Message:</td>
                <td>
                  <!-- xsl:text prevents the XSLT processor from collapsing to
                       <textarea/>, which caused problems with many browsers. -->
                  <textarea name="msgText" wrap="hard" rows="12" 
                      cols="60"><xsl:value-of 
                      select="$global.message"/><xsl:text> </xsl:text></textarea>
                </td>
              </tr>
              <!-- The last table row contains a submit
                   and cancel button -->
              <tr>
                <td> </td>
                <td>
                  <input type="submit" name="submitBtn" value="Submit"/>
                  <input type="submit" name="cancelBtn" value="Cancel"/>
                </td>
              </tr>
            </table>
          </form>
        </div>
      </body>
    </html>
  </xsl:template>
  
  <!--
  **********************************************************
  ** Show the text: 'In Response to: Msg Subject'
  *******************************************************-->  
  <xsl:template match="inResponseTo">
    <div>
      In Response to:
      <span style="font-weight: bold;">
      <xsl:value-of select="subject"/>
      </span>
    </div>
  </xsl:template>
</xsl:stylesheet> 

Since this stylesheet must work for posting new messages as well as for replying to messages, it must determine the appropriate mode of operation. This can be accomplished by checking for the existence of elements that occur only in one mode or the other. For example, the <inResponseTo> XML element occurs only when the user replies to an existing message. Therefore, the XSLT stylesheet can define a variable for the page title as follows:

<xsl:variable name="global.title">
  <xsl:choose>
    <xsl:when test="/postMsg/inResponseTo">
      <xsl:text>Reply to Message</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>Post New Message</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:when test="/postMsg/inResponseTo"> returns true when the <inResponseTo> element exists in the original XML data. In this case, the global.title variable is set to "Reply to Message." Otherwise, the title defaults to "Post New Message."

This stylesheet optionally displays an error message when the user partially fills out the XHTML form and submits the data. The servlet redisplays the page with an error message, allowing the user to fix the problem. It does this by inserting the following XML element into the data:

<error code="ALL_FIELDS_REQUIRED"/>

The XSLT stylesheet tests for the existence of this element as follows:

<xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
   <p class="error">All fields are required...</p>
</xsl:if>

An additional trick used in this stylesheet involves its interaction with a servlet. When the user submits the XHTML form data, the servlet must determine which mode of operation the user was in. For this task, the servlet looks for a request parameter called mode. Legal values for this parameter are replyToMsg and postNewMsg. Since the user is submitting an XHTML form, the easiest way to pass this data is via a hidden form field named mode. Here is the code that does the work:

<xsl:choose>
  <xsl:when test="/postMsg/inResponseTo">
    <input type="hidden" name="origMsgID"
         value="{postMsg/inResponseTo/@id}"/>
    <input type="hidden" name="mode" value="replyToMsg"/>
  </xsl:when>
  <xsl:otherwise>
    <input type="hidden" name="mode" value="postNewMsg"/>
  </xsl:otherwise>
</xsl:choose>

The stylesheet also inserts a hidden form field that contains the original message ID whenever the mode is replyToMsg. On the servlet side, the code looks something like this:

public void doGet(HttpServletRequest request, HttpServletResponse response) ... {
    String mode = request.getParameter("mode");
    if ("replyToMsg".equals(mode)) {
        String origMsgID = request.getParameter("origMsgID");
        ....



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.