Chapter 14. Generic and Functional Programming

The brilliant moves we occasionally make would not have been possible without the prior dumb ones.

Stanley Goldstein

Introduction

This chapter renders all previous chapters moot. Okay, maybe this is a slight exaggeration. The fact is that the examples in previous chapters solve the particular problems they address. They are also useful for didactic purposes. If an example does not solve a problem you face, it might point the way to a solution. The desired solution could be a small modification of the code or a reapplication of the same techniques.

This chapter sets its goals a little higher. It presents examples that solve a very broad range of problems without requiring customization of the example’s core code. Those of you who are familiar with C++, and specifically the Standard Template Library (STL), already know the power you can obtain by creating generic code (generic algorithms) and reusing them in various contexts. Others who use functional programming languages (e.g., Lisp, ML, or Haskell) also know of the great power obtained through the creation of higher-order functions: general-purpose functions that are specialized by accepting special purpose functions as arguments. This chapter shows that XSLT, although not specifically designed as a generic or functional language, has inherent capabilities to enable similar usage.

Warning

The techniques used in this chapter stretch the abilities of XSLT quite a bit. Not everyone will want to use the examples, some of which are complex and slow. Nevertheless, I am reminded of the days before C++ had native support for templates. You could fake generic programming by using macros, but the results were awkward. However, enough people saw the potential, and templates soon became a first-class feature in C++, and possibly one of C++’s most important characteristics, despite the proliferation of other OO languages. Pushing the language envelope in this way puts pressure on the language and possibly makes it evolve faster. This faster development is good because languages that cease to evolve often die out.

Before diving into the examples, let’s first discuss some of the general techniques used in this chapter. This will allow the examples to concentrate on the application of the techniques rather than their mechanics.

Extending the Content of Global Variables

This chapter extensively uses XSLT’s ability to import (xsl:import) and override templates, variables, and other top-level elements in the importing spreadsheet.

Tip

I like to use the object-oriented term override when discussing xsl:import; however, a more technically correct explanation notes that some top-level elements in the importing stylesheet have higher import precedence than matching elements in the imported stylesheet. You can find a complete explanation of how each XSLT top-level element works with respect to xsl:import in Michael Kay’s XSLT Programmer’s Reference (Wrox, 2001).

This chapter takes advantage of the ability to combine a global variable’s contents defined in an imported stylesheet with one defined in an importing stylesheet.

The following stylesheet defines two variables. The first, $data1-public-data , is unique to this stylesheet. The second, $data, is defined in terms of the first, but can be overridden:

<!-- data1.xslt -->
   
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:d="http://www.ora.com/XSLTCookbook/NS/data">
   
<xsl:output method="xml" indent="yes"/>
   
<d:data value="1"/>
<d:data value="2"/>
<d:data value="3"/>
   
<xsl:variable name="data1-public-data" select="document('')/*/d:*"/>
<xsl:variable name="data" select="$data1-public-data"/>
   
<xsl:template match="/">
  <demo>
    <xsl:copy-of select="$data"/>
  </demo>
</xsl:template>
   
</xsl:stylesheet>

Now define another stylesheet that extends the value of $data. It too defines a unique variable that is the union of the first stylesheet’s public data and locally defined data. It then redefines $data in terms of this union:

<!-- data2.xslt -->
   
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:d="http://www.ora.com/XSLTCookbook/NS/data">
   
<xsl:import href="data1.xslt"/>
   
<xsl:output method="xml" indent="yes"/>
   
<d:data value="5"/>
<d:data value="7"/>
<d:data value="11"/>
   
<xsl:variable name="data2-public-data" 
    select="document('')/*/d:* | $data1-public-data"/>
   
<xsl:variable name="data" select="$data2-public-data"/>
   
</xsl:stylesheet>

The output of data1.xslt is:

<demo xmlns:d="data">
   <d:data value="1"/>
   <d:data value="2"/>
   <d:data value="3"/>
</demo>

The output of data2.xslt is:

<demo xmlns:d="data">
   <d:data value="1"/>
   <d:data value="2"/>
   <d:data value="3"/>
   <d:data value="5"/>
   <d:data value="7"/>
   <d:data value="11"/>
</demo>

Defining the second stylesheet’s $data in terms of the first’s, without the need for the extra variables, would be convenient; however, XSLT treats this definition circularly. The technique defines a named set that is operated on by templates in a core stylesheet but allows importing stylesheets to expand the set. The motivation for this will become clearer as you proceed.

Using Template Tags

XSLT provides no direc t way to pass the name of a template to another template so that the second template can invoke the first indirectly. In other words, the following code is illegal in XSLT 1.0 and 2.0:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<!-- THIS IS NOT LEGAL XSLT -->
   
<xsl:template match="/">
  <!-- We can call templates by name ...-->
  <xsl:call-template name="sayIt">
    <xsl:with-param name="aTempl" select=" 'sayHello' "/>
  </xsl:call-template>
  
  <xsl:call-template name="sayIt">
    <xsl:with-param name="aTempl" select=" 'sayGoodby' "/>
  </xsl:call-template>
</xsl:template>
   
<xsl:template name="sayIt">
  <xsl:param name="aTempl"/>
               <!—But not when the name is indirectly specified with a variable —>
               <xsl:call-template name="{$aTemple}"/>
</xsl:template>
   
<xsl:template name="sayHello">
  <xsl:value-of select=" 'Hello!' "/>
</xsl:template>
   
<xsl:template name="sayGoodby">
  <xsl:value-of select=" 'Goodby!' "/>
</xsl:template>
   
</xsl:stylesheet>

As it turns out, you can create some powerful and reusable code if you can figure out how to achieve this level of indirection within the confines of XSLT. Fortunately, you can achieve this goal by using matching instead of naming. The trick is to define a template that can match only one particular piece of data. That piece of data is called a template tag, and by convention, you define the tag directly above the template it matches:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://www.ora.com/XSLTCookbook/namespaces/func">
   
<xsl:output method="text"/>
   
<xsl:template match="/">
  <xsl:call-template name="sayIt">
    <xsl:with-param name="aTempl" select=" 'sayHello' "/>
  </xsl:call-template>
  <xsl:call-template name="sayIt">
    <xsl:with-param name="aTempl" select=" 'sayGoodbye' "/>
  </xsl:call-template>
</xsl:template>
   
<xsl:template name="sayIt">
  <xsl:param name="aTempl"/>
  <!--Applay templates selecting a tag element that is unique to the template we want 
  to invoke -->
  <xsl:apply-templates select="document('')/*/f:func[@name=$aTempl]"/>
</xsl:template>
   
<!— A tagged template consists of a tag element and a template that matches that tagged element —>
               <f:func name="sayHello"/>
<xsl:template match="f:func[@name='sayHello']">
  <xsl:text>Hello!&#xa;</xsl:text>
</xsl:template>
   
<!— Another tagged template —>
               <f:func name="sayGoodby"/>
<xsl:template match="f:func[@name='sayGoodby']">
  <xsl:text>Goodby!&#xa;</xsl:text>
</xsl:template>
   
</xsl:stylesheet>

In this particular case, these contortions are pure overkill because you could simply create a template that takes the output string as data. The true power of this technique is only realized when the tagged functions compute something the caller can use.

When using this technique, use a sanity-checking template that will match when no tagged template matches:

  <xsl:template match="f:func">
       <xsl:message terminate="yes">
     BAD FUNC! Template may not match generic:func declaration.
       </xsl:message>
  </xsl:template>

Mike Kay points out that another generic programming technique has the templates match themselves, as in:

<xsl:template name="f:sayHello"
              match="xsl:template[@name='f:sayHello']">
  <xsl:text>Hello!&#xa;</xsl:text>
</xsl:template>
   
<xsl:template name="f:sayGoodbye"
              match="xsl:template[@name='f:sayGoodbye']">
  <xsl:text>Goodbye!&#xa;</xsl:text>
</xsl:template>

By using this technique, you can still call the template by name without any problems, and it still looks like a normal template. This chapter does not use this technique because we sometimes like to associate other data with the template tags and thus prefer them to be separate elements.

See Also

Dimitre Novatchev was the first person, to my knowledge, to discover techniques for generic and functional programming in XSLT. He wrote several articles on the topic. See http://topxml.com/members/profile.asp?id=i1005. The generic programming recipes in this chapter were developed before I discovered Dimitre’s work and vary in some ways from Dimitre’s approach. I would recommend viewing Dimitre’s work only after you are comfortable with these examples. Dimitre pushes the edge of what can be done further than I do, and his techniques are thus somewhat more challenging. Dimitre has an XSLT library called FXSL - an XSLT functional programming library that can be downloaded from http://topxml.com/xsl/articles/dice.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.175.180