Grouping Across Multiple Documents

page 130 This certainly isnt efficient; for each unique state, well have to call the document function once for every filename attribute. In other words, if we had 500 purchase orders from 50 unique states, we would have to open each of those 500 documents 51 times, invoking the document 25,500 times Its not pretty, but it works. Retrieving the values of all state elements is relatively straightforward. Well use the technique of creating a variable whose value contains output from an xsl:for-each element: xsl:variable name=list-of-states xsl:for-each select=documentreportpofilenamepurchase-ordercustomeraddressstate xsl:sort select=documentstates:name[abbrev=current] xsl:value-of select=.xsl:text xsl:text xsl:for-each xsl:variable This code produces the string ME MA MA WI for our current set of purchase orders. Our next step will remove any duplicate values from the list. Well do this with recursion, using the following algorithm: • Call our recursive template with two arguments: the list of states and the name of the last state we found. the first time we invoke this template, the name of the last state will be blank. • Break the list of states into two parts: The first state in the list, followed by the remaining states in the list. • If the list of states is empty, exit. If the first state in the list is different from the last state we found, output the first state and invoke the template on the remaining states on the list. If the first state in the list is the same as the last state we found, simply invoke the template on the remaining states on the list. Again, we use our technique of calling this template inside an xsl:variable element to save the list of unique states for later. Here is the xsl:variable element, along with the recursive template that removes duplicate state names from the string: xsl:variable name=list-of-unique-states xsl:call-template name=remove-duplicates xsl:with-param name=list-of-states select=list-of-states xsl:with-param name=last-state select= xsl:call-template xsl:variable xsl:template name=remove-duplicates xsl:param name=list-of-states xsl:param name=last-state select= xsl:variable name=next-state xsl:value-of select=substring-beforelist-of-states, xsl:variable xsl:variable name=remaining-states xsl:value-of select=substring-afterlist-of-states, xsl:variable xsl:choose xsl:when test=notstring-lengthnormalize-spacelist-of-states -- If the list of states is empty, do nothing -- xsl:when xsl:when test=notlast-state=next-state xsl:value-of select=next-state xsl:text xsl:text xsl:call-template name=remove-duplicates page 131 xsl:with-param name=list-of-states select=remaining-states xsl:with-param name=last-state select=next-state xsl:call-template xsl:when xsl:when test=last-state=next-state xsl:call-template name=remove-duplicates xsl:with-param name=list-of-states select=remaining-states xsl:with-param name=last-state select=next-state xsl:call-template xsl:when xsl:choose xsl:template At this point, we have a variable named list-of-unique-states that contains the value ME MA WI . Now all we have to do is get each value and output all the purchase orders from each state. Well use recursion yet again to make this happen. Well pass our list of unique states to our recursive template, which does the following: • Breaks the string into two parts: the first state in the list and the remaining states. • Outputs a heading for the first state in the list. • Invokes the document function against each purchase order. If a given purchase order is from the first state in the list, use xsl:apply-templates to transform it. • Invokes the template again for the remaining states. If no states remain the value of normalize-spaceremaining-states is an empty string, were done. Here is the root template and the recursive template we use to group our data. The result of our hard work looks like Figure 7-4 . Figure 7-4. Document featuring grouped items from multiple input files xsl:template match= html head titlexsl:value-of select=reporttitletitle head body h3Selected Purchase Orders - ibGroupedb by stateih3 page 132 xsl:call-template name=group-by-state xsl:with-param name=list-of-unique-states select=list-of-unique-states xsl:call-template body html xsl:template xsl:template name=group-by-state xsl:param name=list-of-unique-states xsl:variable name=next-state xsl:value-of select=substring-beforelist-of-unique-states, xsl:variable xsl:variable name=remaining-states xsl:value-of select=substring-afterlist-of-unique-states, xsl:variable hr h1Purchase Orders from xsl:value-of select=documentstates:name[abbrev=next-state] h1 xsl:for-each select=documentreportpofilenamepurchase-ordercustomeraddress xsl:if test=state=next-state xsl:apply-templates select=ancestor::purchase-order xsl:if xsl:for-each xsl:if test=normalize-spaceremaining-states xsl:call-template name=group-by-state xsl:with-param name=list-of-unique-states select=remaining-states xsl:call-template xsl:if xsl:template

7.5 Summary

This chapter completes our tour of the document function. This powerful function allows us to generate an output document containing elements from many different input documents. In our examples here, we generated several views of those input documents, but many more combinations might be useful. The biggest benefit of the document function is that it allows us to define views of multiple documents that are separate from those documents themselves. As we need to define other views, we dont have to change our input documents. The document function can save you a tremendous amount of development time in generating reports and other summarizing documents. page 133

Chapter 8. Extending XSLT

To this point, weve spent a lot of time learning how to use the built-in features of XSLT and XPath to get things done. Weve also talked about the somewhat unusual processing model that makes life challenging for programmers from the world of procedural languages a.k.a. Earth. But what do you do if you still cant do everything with XSLT and XPath? In this section, well discuss the XSLT extension mechanism that allows you to add new functions and elements to the language. Unfortunately, Version 1.0 of the XSLT standard doesnt define all of the details about how these things should work, so there are some inconsistencies between processors. The good news is that if you write an extension function or element that works with your favorite processor, another vendor cant do something sinister to prevent your functions or elements from working. On the other hand, if you decide to change XSLT processors, youll probably have to change your code. Most examples in this chapter are written for the Xalan processor. Well discuss how to write stylesheets that can work with multiple processors, and well briefly look at the differences between the various APIs supported by those processors. In addition, Xalan comes with extensions written in Java, but you can use other languages, as well. Well look at extensions written in Jython formerly JPython, JavaScript, and Jacl.

8.1 Extension Elements, Extension Functions, and Fallback Processing

Section 14 of the XSLT standard defines two kinds of extensions: extension elements and extension functions. Section 15 of the specification defines fallback processing, a way for stylesheets to respond gracefully when extension elements and functions arent available. Well talk about these items briefly, then well move on to some examples that illustrate the full range of extensions.

8.1.1 Extension Elements

An extension element is an element that should be processed by a piece of code external to the XSLT processor. In the case of the Java version of Xalan, our stylesheet defines the Java class that should be loaded and invoked to process the extension element. Although the implementation details vary from one XSLT processor to the next, well discuss how an extension element can access the XPath representation of our source document, how it can generate output, and how it can move through the XPath tree to manipulate the source document.

8.1.1.1 Example: Generating multiple output files

The whole point of extensions is to allow you to add new capabilities to the XSLT processor. One of the most common needs is the ability to generate multiple output documents. As we saw earlier, the document function allows you to have multiple input documents—but XSLT doesnt give us any way to create multiple output documents. Xalan, Saxon, and XT all ship with extensions that allow you to create such documents. Heres an XML document that well use for several of our examples in this chapter: ?xml version=1.0? book titleXSLTtitle chapter titleGetting Startedtitle paraIf this chapter had any text, it would appear here.para