The Modular Structure of a Stylesheet


In the previous chapter I described the XSLT processing model, in which a stylesheet defines the rules by which a source tree is transformed into a result tree.

Stylesheets, like programs in other languages, can become quite long and complex, and so there is a need to allow them to be divided into separate modules. This allows modules to be reused, and to be combined in different ways for different purposes: for example, we might want to use two different stylesheets to display press releases on screen and on paper, but there might be components that both of these stylesheets share in common. These shared components can go in a separate module that is used in both cases.

We touched on another way of using multiple stylesheet modules in the previous chapter, where each module corresponds to one phase of processing in a multiphase transformation.

One can regard the complete collection of modules as a stylesheet program, and refer to its components as stylesheet modules.

One of the stylesheet modules is the principal stylesheet module. This is in effect the main program, the module that is identified to the stylesheet processor by the use of an <?xml-stylesheet?> processing instruction in the source document, or whatever command-line parameters or application programming interface (API) the vendor chooses to provide. The principal stylesheet module may fetch other stylesheet modules, using <xsl:include> and <xsl:import> elements. These may in turn fetch others, and so on.

The following example illustrates a stylesheet written as three modules: a principal module to do the bulk of the work, with two supporting stylesheet modules: one to obtain the current date, and one to construct a copyright statement.

Using <xsl: include>
start example

Source

The input document, sample.xml, looks like this.

  <?xml version="1.0" encoding="iso-8859-1"?>   <document>   <author>Michael Kay</author>   <title>XSLT 2.0 Programmer's Reference</title>   <copyright/>   <date/>   <abstract>A comprehensive guide to the XSLT 2.0   recommendation published by the World Wide Web Consortium   </abstract>   </document>  

Stylesheets

The stylesheet uses <xsl:include> . The effect of this stylesheet is to copy the source document unchanged to the result, except that any <date> elements are set to the current date, and any <copyright> elements are set to a string identifying the copyright owner.

There are three modules in this stylesheet program: principal.xsl, date.xsl, and copyright.xsl. The date.xsl module uses the XSLT 2.0 function current-date (); the other modules will work equally well with XSLT 1.0 or 2.0.

When you run the transformation, you only need to name the principal stylesheet module on the command line-the other modules will be fetched automatically. The way this stylesheet is written, all the modules must be in the same directory.

principal.xsl

The first module, principal.xsl, contains the main logic of the stylesheet.

  <?xml version="1.0" encoding="iso-8859-1"?>   <xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0"   >   <xsl:include href="date.xsl"/>   <xsl:include href="copyright.xsl"/>   <xsl:output method="xml" encoding="iso-8859-1" indent="yes"/>   <xsl:strip-space elements="*"/>   <xsl:template match="date">   <date><xsl:value-of select="$date"/></date>   </xsl:template>   <xsl:template match="copyright">   <copyright>   <xsl:call-template name="copyright"/>   </copyright>   </xsl:template>   <xsl:template match="*">   <xsl:copy>   <xsl:copy-of select="@*"/>   <xsl:apply-templates/>   </xsl:copy>   </xsl:template>   </xsl:stylesheet>  

It starts with two <xsl:include> elements to bring in the other modules. The <xsl:output> element indicates that the output should be in XML format, using the ISO 8859/1 character set (which makes it easy to view with a text editor), and with indentation to show the XML structure. The <xsl:strip-space> element indicates that whitespace nodes in the source document are to be ignored: I'll have a lot more to say about whitespace handling later in this chapter. Then there are three template rules, one for <date> elements, one for <copyright> elements, and one for everything else.

The template rule for <date> elements outputs the value of the variable named $date . This variable isn't defined in this stylesheet module, but it is present in the module date.xsl, so it can be accessed from here.

The template rule for <copyright> elements similarly calls the template named copyright. Again, there is no template of this name in this module, but there is one in the module copyright.xsl, so it can be called from here.

Finally, the template rule that matches all other elements («match="*" ») has the effect of copying the element unchanged from the source document to the output. The <xsl:copy> (page 240) and <xsl:copy-of > (page 245) instructions are explained in Chapter 5.

date.xsl

The next module, date.xsl, declares a global variable containing today's date. This calls the current-date() function in the standard XPath 2.0 function library, and the XSLT 2.0 format-date () function, which is described in Chapter 7 of this book.

  <xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="2.0"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   >   <xsl:variable name="date" as="xs:string"   select="format-date(current-date(), '[MNn] [Dlo], [Y]')"/>   </xsl:stylesheet>  

Although this is a rather minimal module, there's a good reason why you might want to separate this code into its own module: it's dependent on XSLT 2.0, and you might want to write an alternative version of the function that doesn't have this dependency. Note that we've set «version="2.0" » on the <xsl:stylesheet> element to document this dependency; the other modules in this stylesheet have «version="1.0" » .

copyright.xsl

Finally, the module copyright.xsl contains a named template that outputs a copyright statement. This template is called by the <xsl:call-template> instruction in the principal stylesheet. The template uses a variable $owner to construct the copyright statement: we'll see later how this is useful.

  <?xml version="1.0" encoding="iso-8859-1"?>   <xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0">   <xsl:variable name="owner">John Wiley and Sons</xsl:variable>   <xsl:template name="copyright">   <xsl:text>Copyright  </xsl:text>   <xsl:value-of select="$owner"/>   <xsl:text> 2004</xsl:text>   </xsl:template>   </xsl:stylesheet>  

The reason for separating this stylesheet program into three modules is that the date.xsl and copyright.xsl modules are reusable in other stylesheets. Functionally, the stylesheet would have exactly the same effect if the variable $date and the template named copyright were defined directly in the principal stylesheet module.

Output

  <?xml version="1.0" encoding="iso-8859-1" ?>   <document>   <author>Michael Kay</author>   <title>XSLT 2.0 Programmer's Reference</title>   <copyright>Copyright  John Wiley and Sons 2004</copyright>   <date>April 15th, 2004</date>   <abstract>A comprehensive guide to the XSLT 2.0 recommendation   published by the World Wide Web Consortium </abstract>   </document>  
end example
 

There is no syntactic difference between a principal stylesheet module and any other module; in fact any module can be used as a principal module.

This means that <xsl:include> and <xsl:import> can be used in any module, not only the principal module. So the stylesheet program is actually a tree of stylesheet modules, with the principal module at its root.

A stylesheet module is generally a complete XML document (the exception, an embedded stylesheet, will be described later on page 95). The document element (the outermost element of the XML document) is then either an <xsl:stylesheet> element or an <xsl:transform> element: the two names are synonymous. The elements immediately subordinate to the <xsl:stylesheet> or <xsl:transform> element are called declarations. The XSLT-defined declarations are listed on page 99.

The <xsl:include> and <xsl:import> declarations are always children of the <xsl:stylesheet> or <xsl:transform> element. Usually, declarations can appear in any order, but <xsl:import> is an exception: it must appear before any other declaration. Both elements take an href attribute whose value is a URI. Most commonly, it will be a relative URI, defining the location of the included or imported stylesheet module relative to the parent module. For example, <xsl:include href="mod1.xsl"/> fetches the module mod1.xsl located in the same directory as the parent module.

The difference between <xsl:include> and <xsl:import> is that conflicting definitions are resolved differently:

  • <xsl:include> effectively does a textual inclusion of the referenced stylesheet module, minus its containing <xsl:stylesheet> element, at the point where the <xsl:include> element is written. The included module is treated exactly as if its top-level elements, with their contents, appeared in the parent module in place of the <xsl:include> element itself.

  • <xsl:import> also incorporates the top-level elements from the referenced stylesheet module, but in this case the declarations in the imported module have lower import precedence than the declarations in the parent module. If there are conflicting declarations, the one with higher import precedence will generally win. The detailed rules actually depend on the type of definition, and are given in the specification of <xsl:import> on page 312 in Chapter 5. Importing a module is thus rather like defining a subclass, in that the parent module can use some declarations unchanged from the imported module, and override others with declarations of its own.

It may not come naturally to think of the importing module as a subclass of the imported module, because in a class hierarchy, the most general classes are near the root of the tree, whereas in the <xsl:import> tree, the most general classes are at the leaves of the tree. Nevertheless, this is how <xsl:import> should be used: general-purpose modules should always be imported into special-purpose modules, not the other way around.

The most common kind of declaration is the definition of a template rule, using an <xsl:template> element with a match attribute. As we saw in the previous chapter, if there are several template rules that match a particular node in the source tree, the first step in deciding which to use is to look at their import precedence, and discard all those with import precedence less than the highest. So a template rule defined in a particular stylesheet module will automatically take precedence over another matching rule in a module that it imports.

Where one module A imports two others, B and C, as shown in Figure 3-1, then A takes precedence over both B and C, and C also takes precedence over B, assuming that the <xsl:import> element that loads B precedes the <xsl:import> element that loads C.


Figure 3-1

When a stylesheet incorporates another using <xsl:include>, the declarations in the included stylesheet have the same import precedence as those in the parent stylesheet.

Where two declarations have the same import precedence (because they were in the same stylesheet module, or because one was in a module incorporated in the other using <xsl:include>), the rules for resolving conflicts depend on the kind of declaration. In some cases, for example declarations of named templates or variables , duplicate declarations with the same name are always reported as an error. In other cases, for example declarations of template rules, the implementer has the choice of reporting an error or choosing the declaration that occurs later in the stylesheet. Some implementers may pass this choice on to the user . The detailed rules are given in Chapter 5 for each kind of declaration, and they are summarized in the section for <xsl:import>, page 312.

Using <xsl: import>
start example

This extends the previous <xsl:include> example, showing how to use <xsl:import> to incorporate the declarations in another stylesheet module while overriding some of them.

Source

The input document for this example is sample.xml.

Stylesheet

Recall that the copyright.xsl module used a variable, $owner, to hold the name of the copyright owner. Suppose that we want to use the copyright template, but with a different copyright owner. We can achieve this by writing a revised principal stylesheet as follows (this is called principal2.xsl in the downloadable sample files).

This stylesheet uses <xsl:import> instead of <xsl:include> to incorporate the copyright.xsl module, and it then contains a new declaration of the $owner variable, which will override the declaration in the imported module. Note that the <xsl:import> element must come first.

  <?xml version="1.0" encoding="iso-8859-1"?>   <xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0"   >   <xsl:import href="copyright.xsl"/>   <xsl:variable name="owner">John Wiley Inc.</xsl:variable>   <xsl:include href="date.xsl"/>   <xsl:output method="xml" encoding="iso-8859-1" indent="yes"/>   <xsl:strip-space elements="*"/>   <xsl:template match="date">   <date><xsl:value-of select="$date"/></date>   </xsl:template>   <xsl:template match="copyright">   <copyright><xsl:call-template name="copyright"/></copyright>   </xsl:template>   <xsl:template match="*">   <xsl:copy>   <xsl:copy-of select="@*">   <xsl:apply-templates/>   </xsl:copy>   </xsl:template>   </xsl:stylesheet>  

Output

  <?xml version="1.0" encoding="iso-8859-1" ?>   <document>   <author>Michael Kay</author>   <title>XSLT Programmer's Reference</title>   <copyright>copyright  John Wiley Inc 2004</copyright>   <date>April 15th, 2004</date>   <abstract>A comprehensive guide to the XSLT 2.0 recommendation   published by the World Wide web Consortium </abstract>   </document>  

This example wouldn't work if you used <xsl:include> rather than <xsl:import>. It would complain that the variable $owner was declared twice. This is because with <xsl:include>, the two declarations have the same import precedence, so neither can override the other.

end example
 

It is an error for a stylesheet module to import or include itself, directly or indirectly, because doing so would define an infinite loop.

It isn't an error, however, for a stylesheet module to be included or imported at more than one place in the stylesheet program. The following isn't an error.

  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0">   <xsl:import href="date.xsl"/>   <xsl:import href="date.xsl"/>   </xsl:stylesheet>  

This may seem rather pointless, but in a highly modular structure it can sometimes happen by accident and be harmless. For example, several of your stylesheet modules might independently reference a commonly used module such as date.xsl. The effect is simply to load two copies of all the declarations in date.xsl, exactly as if two identical files with different names had been imported.

If the same module is fetched twice using <xsl:include>, the included declarations will have the same import precedence, which is likely to cause an error. If, for example, the included module defines a global variable or a named template, duplicate declarations will be reported. In other cases, for example where the file uses the <xsl:attribute-set> element to define named attribute sets, the duplicate declarations are harmless (the <xsl:attribute-set> element is described on page 214, in Chapter 5). However, if there is a risk of loading the same module twice, it makes sense to use <xsl:import> rather than <xsl:include>.

Note that with both <xsl:include> and <xsl:import>, the href attribute is fixed: it is possible for a stylesheet compiler to assemble all the modules in a stylesheet well before a source document is supplied to actually run the transformation. People often ask for some kind of facility to load stylesheet modules dynamically, based on a decision made while the transformation is running. The simple answer is that you can't do this: you have to construct the whole stylesheet before you can start running it.

Sometimes, this requirement arises when people try to use <xsl:import> "the wrong way round." It's fairly natural to think in terms of writing a general-purpose stylesheet G that imports A in some circumstances (say if the user is French) and imports B in other circumstances (if the user is Spanish). But that's the wrong way to do it: you should select A or B as the principal stylesheet module, and have both of these import the general-purpose module G. The special-purpose stylesheet module should always import the general-purpose module.

The XSL Working Group has recently added a facility for compile-time conditionals to the language. This allows you to write something like:

 <xsl:include href="mod1.xsl" use-when="system-property('xsl:vendor')= 'Xalan'"/>. 

This allows conditional inclusion of a module based on information that is known at compile time. There is more information on the use-when attribute later in the chapter: see the section Writing Portable Stylesheets.




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

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