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.
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. 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. 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. 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. 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:
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]
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&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}&month={@month}&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&boardID={@id}">Post Message</a>
Since the ampersand character
(&) is not allowed in an XML
attribute value, it must be written using the
& built-in entity. As it turns out,
browsers deal with this just fine, and the hyperlink works
anyway.[28]
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&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">&nbsp;
<xsl:call-template name="utils.printShortMonthName">
<xsl:with-param name="monthNumber" select="$global.monthNum"/>
</xsl:call-template>
<xsl:text disable-output-escaping="yes">&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");
....