Example: Generating multiple output files

page 134 chapter chapter titleThe Hello World Exampletitle paraIf this chapter had any text, it would appear here.para chapter chapter titleXPathtitle paraIf this chapter had any text, it would appear here.para chapter chapter titleStylesheet Basicstitle paraIf this chapter had any text, it would appear here.para chapter chapter titleBranching and Control Elementstitle paraIf this chapter had any text, it would appear here.para chapter chapter titleFunctionstitle paraIf this chapter had any text, it would appear here.para chapter chapter titleCreating Links and Cross-Referencestitle paraIf this chapter had any text, it would appear here.para chapter chapter titleSorting and Grouping Elementstitle paraIf this chapter had any text, it would appear here.para chapter chapter titleCombining XML Documentstitle paraIf this chapter had any text, it would appear here.para chapter book For our first example, we want to create a stylesheet that converts the document to HTML, writing the contents of each chapter element to a separate HTML file. Heres what that stylesheet looks like: ?xml version=1.0? xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform xmlns:redirect=org.apache.xalan.xslt.extensions.Redirect extension-element-prefixes=redirect xsl:output method=html xsl:template match= xsl:choose xsl:when test=element-availableredirect:write xsl:for-each select=bookchapter redirect:write select=concatchapter, position, .html html head titlexsl:value-of select=titletitle head body h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap xsl:if xsl:if test=notposition=last pa href=chapter{position+1}.htmlNextap xsl:if body html redirect:write xsl:for-each xsl:when page 135 xsl:otherwise html head titlexsl:value-of select=booktitletitle head xsl:for-each select=bookchapter h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:for-each html xsl:otherwise xsl:choose xsl:template xsl:template match=para pxsl:apply-templates select=|textp xsl:template xsl:stylesheet Lets go through the relevant parts of this example. To begin with, our xsl:stylesheet element defines the redirect namespace prefix and tells the XSLT engine that the prefix will be used to refer to an extension element. xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform xmlns:redirect=org.apache.xalan.xslt.extensions.Redirect extension-element-prefixes=redirect The syntax of everything weve done so far is according to the standard, although theres a fair amount of latitude in what the XSLT engines do with the information weve defined. For example, when defining the redirect namespace, Xalan uses the value here as a Java class name. In other words, Xalan attempts to load the class org.apache.xalan.xslt. extensions.Redirect when it encounters an extension element or function defined with this namespace. The way other XSLT processors use the namespace URI can vary. To this point, weve simply defined our extension class so Xalan can find our code, load it, and invoke it. Our next step is to actually use it: xsl:when test=element-availableredirect:write xsl:for-each select=bookchapter redirect:write select=concatchapter, position, .html html head titlexsl:value-of select=titletitle head body h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap xsl:if xsl:if test=notposition=last pa href=chapter{position+1}.htmlNextap xsl:if body html redirect:write xsl:for-each xsl:when page 136 This code does several things: • It checks to see if our extension element is available. If it is, well use it; if not, the xsl:otherwise element will be evaluated instead. • For each chapter in our XML document, it calls an extension element from our Redirect class. In the example here, were calling the redirect:write element, which opens a file and directs all the output generated by Xalan into that file. Notice that the filename here is generated automatically; the filename for the first chapter is chapter1.html, the filename for the second is chapter2.html, etc. This convenient naming convention creates a unique filename for each chapter and makes it easy to figure out which filename contains the output from each chapter. • It creates the HTML tags to define the document and its title . After creating the head section, it creates an h1 for the chapter title, followed by a p generated from each para element in the XML source. • It generates hyperlinks between the different documents. If a given document is any chapter other than the first notposition=1 , it creates a link to the previous chapter. The filename for the previous chapter is generated with the expression chapter{position-1}.html . If the document is any chapter other than the last notposition=last , it creates a link to the next chapter. The filename for the next chapter is generated with the function call concatchapter, position+1, .html . In this example, we used both the curly brace notation and the xsl:attribute element. Both work the same way; for the rest of this chapter, well use the curly brace notation to save space. For more information, see the discussion of Section 3.3 in Chapter 3 . • After any required hyperlinks have been generated, it writes the closing HTML tags and ends the redirect:write element. Ending the redirect:write element closes the file. An individual output file looks like Figure 8-1 . Figure 8-1. An individual output file This particular chapter contains both a Previous and a Next link. The first chapter wont have a Previous link, and the last chapter wont have a Next ; other than that, all individual chapters are formatted the same way. page 137 That code covers how we generate multiple output files when the extension element is available. When it isnt available, we simply generate a single HTML file that contains the text of all the chapters: xsl:otherwise html head titlexsl:value-of select=booktitletitle head xsl:for-each select=bookchapter h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:for-each html xsl:otherwise In the xsl:otherwise element, we create a single HTML element, then process each chapter in turn. The output is a single large file; not ideal for a really large document, but an acceptable alternative when our extension element isnt available. In this relatively simple example, weve broken a single XML document into multiple HTML files, weve generated useful filenames for all of them, and weve automatically built hyperlinks between the different HTML files. If we add, delete, or move a chapter, we can simply rerun our stylesheet and all the files and links between them will be updated. For now, weve simply discussed how to use an extension; well talk about how to write your own extension later in this chapter.

8.1.1.2 Example: Using extension functions from multiple processors

So far, weve used an extension function to convert a single XML document into multiple HTML files. Unfortunately, our stylesheet only works with the Xalan XSLT processor. How can we write a stylesheet that will work with multiple XSLT processors? The answer is to define more extension elements, one for each processor. Heres a stylesheet that works with Xalan, Saxon, and XT: ?xml version=1.0? xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform xmlns:redirect=org.apache.xalan.xslt.extensions.Redirect xmlns:saxon=http:icl.comsaxon xmlns:xt=http:www.jclark.comxt extension-element-prefixes=redirect saxon xt xsl:output method=html xsl:template match= xsl:choose xsl:when test=containssystem-propertyxsl:vendor, James Clark xsl:for-each select=bookchapter xt:document method=xml href=chapter{position}.html html head titlexsl:value-of select=titletitle head body h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap xsl:if xsl:if test=notposition=last pa href=chapter{position+1}.htmlNextap xsl:if body html page 138 xt:document xsl:for-each xsl:when xsl:when test=containssystem-propertyxsl:vendor, Apache xsl:for-each select=bookchapter redirect:write select=concatchapter, position, .html html head titlexsl:value-of select=titletitle head body h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap xsl:if xsl:if test=notposition=last pa href=chapter{position+1}.htmlNextap xsl:if body html redirect:write xsl:for-each xsl:when xsl:when test=containssystem-propertyxsl:vendor, SAXON xsl:for-each select=bookchapter saxon:output href=chapter{position}.html html head titlexsl:value-of select=titletitle head body h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap xsl:if xsl:if test=notposition=last pa href=chapter{position+1}.htmlNextap xsl:if body html saxon:output xsl:for-each xsl:when xsl:otherwise html head titlexsl:value-of select=booktitletitle head xsl:for-each select=bookchapter h1xsl:value-of select=titleh1 xsl:apply-templates select=para xsl:for-each html xsl:otherwise xsl:choose xsl:template xsl:template match=para pxsl:apply-templates select=|textp xsl:template xsl:stylesheet