Introduction


This chapter renders all previous chapters moot. Okay, maybe that 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.

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.

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 more clear 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=" 'sayGoodbye' "/>   </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="sayGoodbye">   <xsl:value-of select=" 'Goodbye!' "/> </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"/>   <!--Apply templates by 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="sayGoodbye"/> <xsl:template match="f:func[@name='sayGoodbye']">   <xsl:text>Goodbye!&#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.

Generic Programming Versus Functional Programming

Generic programming is a method of organizing highly reusable components that can be customized with little or no loss of runtime performance. You can reuse generic components (classes and functions) by instantiating them with particular types and/or objects.

Functional programming programs with higher-order functions: functions that can take other functions as parameters and return functions as values.

You can interpret the template tagging technique in two ways. If you put on your generic programming hat, you can claim that the sayIt template is a generic template that is parameterized with another template. However, if you put on your functional programming hat, you can claim that the sayIt template is a higher-order function that takes another function as an argument. Those familiar with C++'s style of generic programming will probably argue that the second interpretation is more accurate because the parameterization occurs at a runtime rather than at compile time. However, I do not believe that generic programming language must be a compile-time construct. Later you will see that the element tags can actually do more than serve as a proxy for the name of a function; these tags can also carry data reminiscent of the TRaits technique used in C++-style generic programming.


See Also

XSLT 1.0

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://fxsl.sourceforge.net. 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://sourceforge.net/project/showfiles.php?group_id=53841.

XSLT 2.0

Dimitre Novatchev has a version of FXSL that takes advantage of the new capabilities of XSLT 2.0 (http://sourceforge.net/project/showfiles.php?group_id=53841). Most notably, his library takes great advantage of 2.0's functions and function overloading. I stongly recommend his library to XSLT 2.0 developers interested in "higher-order" programming in XSLT.

Here are some excerpts from Dimitre's post regarding fxsl and higher order XSLT programming. Here, HOF stands for Higher Order Function.

The benefit should be obvious:

1. Composability: f:map can be in any place where we could have a functional composition chain.

2. It is much simpler and more understandable to write:

f:map(fsomeFun( ), $seq)

than

<xsl:for-each select="$seq">

<xsl:sequence select="f:apply(fsomeFun( ), $seq)"/>

</xsl:for-each>

Having a HOF wrapper with the same name and signature as any one of all the F(unctions) & O(perators) provides essentially a functional programming system, where all standard XPath 2.0 F & Os (and all standard XSLT 2.0 functions) are higher order.

The programmer can immediately start using all standard XPath 2.0 F & Os (which he knows well) without having to do anything in addition. The necessary steep learning curve is almost avoided. This is wealth of functions that are already implemented and they have guaranteed support by any compliant XSLT 2.0 processor.

Another important result is a set of strict and simple rules how to write user-defined xsl:function's that are implemented as higher order.

Adhering to these rules makes XSLT 2.0 + FXSL really a closed (in relation of HOF support) functional programming system.

Yet another benefit is that all HOF functions could potentially be used in a non-XSLT environment (for example, if they are available in pre-compiled form), such as using embedded XPath 2.0 in a programming language, or why not in XQuery. Of course, in this approach any new HOF will have to be implemented in XSLT and precompiled before they can be referenced.

Trying to summarize all the benefits:

The FXSL + XSLT 2.0 functional programming system has already reached a state where the solutions to many very difficult problems can be simply expressed as one-liner XPath expressions.

This may represent a radical change in the way of reasoning and in how problems are solved in XSLT.

Here Dimitre demonstrates the ease of performing complex mathematical manipulations using FXSL.

The other, factual reason is that using XSLT for math computations is simple, compact, elegant, and fun.

For example, to get the sequence:

N^10, N = 1 to 10

one simply writes this one-line expression:

f:map(f:flip(f:pow( ),10), 1 to 10)

To get the sum of the tenth powers of all numbers from 1 to 10, one writes this one-liner:

sum(f:map(f:flip(f:pow( ),10), 1 to 10))

To get the tenth root of the above, this one-liner is used:

f:pow(sum(f:map(f:flip(f:pow( ),10), 1 to 10)), 0.1)




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net