WEEK 1 Day 13
Working with Multifile Stylesheets

In yesterday’s lesson, the focus was on sorting the elements in a source document before sending them to the output. Lesson 12 also concentrated on all kinds of numbering, from simple numbering to composite numbering in different formats.

In today’s lesson, you will learn how to create reusable stylesheets. This helps you to build stylesheets and include or import them into other stylesheets so that those stylesheets can use the functionality of the original stylesheets without having to use much code. As you will see, this enables you to build libraries of templates that perform common operations. You can then reuse these templates across many stylesheets.

Today you will learn the following:

• How to create multifile stylesheets by including or importing other stylesheets

• The difference between including and importing stylesheets

• How you can benefit from including and importing stylesheets

• The rules surrounding template precedence when you’re working with included or imported templates

Using Multiple Stylesheets

Within a project or a company, you are likely to have many XML documents. Some of these documents may contain the same elements as other documents, and they may have to be handled in the same manner. Rewriting the code for these elements is probably not your idea of fun, which is why you can create stylesheets consisting of multiple files to reuse templates and other constructs.

The Benefits of Multiple Files

Obviously, working with multiple files and being able to reuse them are huge benefits, specifically when you’re working in large stylesheets or with many stylesheets that need to be just slightly different for different purposes. These capabilities also allow you to work with files that have some general purpose, such as inserting a header, footer, menu, or toolbar in an HTML page. In addition, you can create a file that defines global variables to store a color scheme, font formatting, and so on for your company or a specific project. This file then can be reused across stylesheets so that each stylesheet uses the same formatting. When you need to change the formatting, changing the file that defines all the variables changes all stylesheets immediately, so all output is always consistent.

You can work with multifile stylesheets by including or importing other stylesheets. When you include a stylesheet, its contents are copied into the stylesheet including it. When you import a stylesheet, this is not the case, so you can override elements from the stylesheet you import. Depending on the elements in question, elements might be merged instead. This is of benefit, for instance, if your header color is normally blue, but in a specific case it needs to be red; the stylesheet doing the import can override the default color but still use all the other formatting definitions. This capability is very common in object-oriented programming languages such as SmallTalk, C++, and Java. If you’re familiar with these languages or concepts, you are already familiar with many of the benefits.

The Drawbacks of Multiple Files

Being able to reuse already-written functionality is very powerful, but the rules governing including and importing other stylesheets are complex. These rules are complex to avoid problems from duplicated elements as much as possible. So, when you work with multiple files, you need to be very aware of all the rules involved if you want to get the output you want.

Even though these rules prevent most problems, not all includes and imports are allowed. They still cause errors. These errors might not occur at design time because errors can depend on elements in the source document. These errors are a big problem because XSLT was designed, for the most part, to never fail, as long as the syntax of the source XML and the stylesheet are correct. Until now, all failures you encountered were design-time problems, so you could deal with them when you created the stylesheet and rest assured that your stylesheet would always work (although the result might still be wrong, of course). Now you are faced with an element that might cause runtime failures, even if your stylesheet worked properly at design time.

An additional problem is that you need to be aware of the dependencies between stylesheets. If you change a stylesheet that is imported or included by another stylesheet, the changes may cause problems in the other stylesheet. This means that if you change a stylesheet, you need to check whether the stylesheets that depend on it still work, too.

Including Stylesheets

When you include a stylesheet in another stylesheet, you are copying the contents of the xsl:stylesheet element of the stylesheet being included. Those contents then replace the element that is used to include the other stylesheet. The two (or more) stylesheets then basically act as though they are one stylesheet, and all elements within it have to follow the same rules as though it is just one stylesheet. This makes the order in which you include different stylesheets significant, as well as the position in the stylesheet including other stylesheets.

Note

The stylesheet that includes other stylesheets is called the including stylesheet, whereas the stylesheet being included is called the included stylesheet.

You can include one stylesheet into another stylesheet by using the xsl:include element. The href attribute, the only attribute of xsl:include, is mandatory and must contain a valid Uniform Resource Identifier (URI) to a document. It can be either a relative URI or an absolute URI. In most cases, you have full control over the project, so chances are you will be using only a relative URI that points either to a file within the same directory as the stylesheet including the other stylesheet or in a directory relative to that one. Only if your project involves a set of stylesheets that are distributed around several servers or if you’re using stylesheets provided by a third party do you use absolute URIs. Following are some samples of the different kinds of xsl:include elements you might use:

<xsl:include href="myinclude.xsl" />  
<xsl:include href="../includes/myinclude.xsl" />  
<xsl:include href="http://www.somewebsite.com/xslincludes/myinclude.xsl" />  

The first two samples are relative URIs. The first points to a file in the same directory, and the second points to a file in a sibling directory named includes. The last sample is an absolute URI pointing to a file on another Web site. Be aware that if the file for the stylesheet being included can’t be found, the processor will raise an error.

Caution

You can include a stylesheet in a stylesheet that is included itself. The same rules apply in this situation. However, be aware that the URI is relative to the stylesheet doing the include. So, stylesheet A includes stylesheet B, which is in a different directory from stylesheet A. Then, if stylesheet B includes another stylesheet, the URI of the xsl:include element in that stylesheet is relative to the directory of stylesheet B, not that of stylesheet A.

The best way to get the hang of using xsl:include and the issues involved is to look at an example. That way, you can quickly see what’s going on and how changes affect the outcome. For these samples, the familiar menu XML is again the base of operations. For quick reference, it is shown in Listing 13.1.

LISTING 13.1 Sample XML with Menu Data

?<?xml version=″“1.0”" encoding="UTF-8"?>
<menu>
  <appetizers title="Work up an Appetite">
    <dish id="1" price="8.95">Crab Cakes</dish>
    <dish id="2" price="9.95">Jumbo Prawns</dish>
    <dish id="3" price="10.95">Smoked Salmon and Avocado Quesadilla</dish>
    <dish id="4" price="6.95">Caesar Salad</dish>
  </appetizers>
  <entrees title="Chow Time!">
    <dish id="5" price="19.95">Grilled Salmon</dish>
    <dish id="6" price="17.95">Seafood Pasta</dish>
    <dish id="7" price="16.95">Linguini al Pesto</dish>
    <dish id="8" price="18.95">Rack of Lamb</dish>
    <dish id="9" price="16.95">Ribs and Wings</dish>
  </entrees>
    <desserts title="To Top It Off">
    <dish id="10" price="6.95">Dame Blanche</dish>
    <dish id="11" price="5.95">Chocolat Mousse</dish>
    <dish id="12" price="6.95">Banana Split</dish>
  </desserts>
</menu>

Note

You can download the sample listings in this lesson from the publisher’s Web site.

The stylesheet in Listing 13.2 creates a nice-looking menu from the XML in Listing 13.1. This stylesheet does not use xsl:include because you first need a stylesheet that can be included in another stylesheet.

LISTING 13.2 Stylesheet Creating an HTML Menu

1:   <?xml version=″“1.0”" encoding="UTF-8"?>
2:   <xsl:stylesheet version=″“1.0”"
3:     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:   <xsl:output method="html" encoding="utf-8" />
6:   <xsl:strip-space elements="*" />
7:
8:   <xsl:template match="appetizers|entrees|desserts">
9:     <table>
10:      <xsl:apply-templates />
11:    </table>
12:  </xsl:template>
13:
14:  <xsl:template match="dish">
15:    <tr>
16:      <td><xsl:value-of select="." /></td>
17:      <td><xsl:value-of select="concat ('$',@price)" /></td>
18:    </tr>
19:  </xsl:template>
20: </xsl:stylesheet>

ANALYSIS

Listing 13.2 creates an HTML table for each course. In that table, each dish element is rendered as a table row, with the name and price separated in table cells. So that this is done right, the courses are handled by one template (line 8), and a separate template on line 14 handles all the dish elements. Note that no template matches the root node. The stylesheet was created this way on purpose so that the template is easier to reuse and will not cause an error because two templates match the root node. Lines 5–6 make sure the output encoding and type are correct and that there is no redundant whitespace.

Caution

One of the most common mistakes with the xsl:include element is having two templates with the exact same match expression, such as two templates matching the root node of the source document. This situation will cause an error, so you need to create your stylesheets in such a way that this cannot occur.

Listing 13.3 shows the result from applying Listing 13.2 to Listing 13.1. Note that, by default, Saxon creates indented HTML code.

OUTPUT

LISTING 13.3 Result from Applying Listing 13.2 to Listing 13.1

<table>
    <tr>
      <td>Crab Cakes</td>
      <td>$8.95</td>
    </tr>
    <tr>
      <td>Jumbo Prawns</td>
      <td>$9.95</td>
    </tr>
    <tr>
      <td>Smoked Salmon and Avocado Quesadilla</td>
      <td>$10.95</td>
    </tr>
    <tr>
      <td>Caesar Salad</td>
      <td>$6.95</td>
    </tr>
</table>
<table>
    <tr>
      <td>Grilled Salmon</td>
      <td>$19.95</td>
    </tr>
    <tr>
      <td>Seafood Pasta</td>
      <td>$17.95</td>
    </tr>
    <tr>
      <td>Linguini al Pesto</td>
      <td>$16.95</td>
    </tr>
    <tr>
      <td>Rack of Lamb</td>
      <td>$18.95</td>
    </tr>
    <tr>
      <td>Ribs and Wings</td>
      <td>$16.95</td>
    </tr>
</table>
<table>
    <tr>
      <td>Dame Blanche</td>
      <td>$6.95</td>
    </tr>
    <tr>
      <td>Chocolat Mousse</td>
      <td>$5.95</td>
    </tr>
    <tr>
      <td>Banana Split</td>
      <td>$6.95</td>
    </tr>
</table>

ANALYSIS

Listing 13.3 shows three HTML tables, one for each course. The dish name and its price are put in a separate table cell.

Listing 13.3 inserts only HTML tables; other elements such as the HTML base elements html and body are not inserted. You might conceivably have a different stylesheet that reuses the templates in Listing 13.2 and among other things adds the base HTML elements. The stylesheet in Listing 13.4 does exactly that.

LISTING 13.4 Stylesheet Including Listing 13.2

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:include href="13list02.xsl" />
6:
7:    <xsl:template match="/">
8:      <html>
9:      <body>
10:       <h1>Menu</h1>
11:       <xsl:apply-templates />
12:     </body>
13:     </html>
14:   </xsl:template>
15: </xsl:stylesheet>

ANALYSIS

Apart from line 5 containing the xsl:include element, Listing 13.4 is rather straightforward. The template matching the root node inserts the base HTML code and a header. It then invokes other templates, in this case the ones that were included on line 5. Listing 13.5 shows you that the result is basically Listing 13.3 with the HTML base tags surrounding the tables and a header added before the first table starts.

OUTPUT

LISTING 13.5 Result from Applying Listing 13.4 to Listing 13.1

<html>
      <body>
      <h1>Menu</h1>
      <table>
        <tr>
           <td>Crab Cakes</td>
           <td>$8.95</td>
        </tr>
        <tr>
           <td>Jumbo Prawns</td>
           <td>$9.95</td>
        </tr>
        <tr>
           <td>Smoked Salmon and Avocado Quesadilla</td>
           <td>$10.95</td>
        </tr>
        <tr>
           <td>Caesar Salad</td>
           <td>$6.95</td>
        </tr>
      </table>
<!--Result truncated. The tables created are the same as Listing 13.4-->
      </body>
</html>

ANALYSIS

Listing 13.5 has been truncated for easier reading. Basically, it shows you that the elements inserted in Listing 13.4 are added to the output of Listing 13.2 just as if Listing 13.4 contains the templates of Listing 13.2. This is, in fact, more or less the case.

As I said earlier, the top-level elements from the included stylesheet are copied over to the including stylesheet, at the position of the xsl:include element. So, after the xsl:include element in Listing 13.4 is handled by the processor, the resulting stylesheet, for all intents and purposes, looks like Listing 13.6.

Caution

The position of the xsl:include element in the including stylesheet is of paramount importance. A change in location will change where the elements from the included stylesheet are placed. This may have an effect on the calculated precedence of templates and other location-related issues.

LISTING 13.6 Revised Listing 13.4 with Listing 13.2 Included

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="html" encoding="utf-8" />
6:    <xsl:strip-space elements="*" />
7:
8:    <xsl:template match="appetizers|entrees|desserts">
9:       <table>
10:       <xsl:apply-templates />
11:     </table>
12:   </xsl:template>
13:
14:   <xsl:template match="dish">
15:     <tr>
16:       <td><xsl:value-of select="." /></td>
17:       <td><xsl:value-of select="concat ('$',@price)" /></td>
18:     </tr>
19:   </xsl:template>
20:
21:   <xsl:template match="/">
22:      <html>
23:      <body>
24:       <h1>Menu</h1>
25:       <xsl:apply-templates />
26:      </body>
27:      </html>
28:   </xsl:template>
29: </xsl:stylesheet>

ANALYSIS

Listing 13.6 contains the top-level elements from Listing 13.2 included in Listing 13.4. Lines 5–19 are new and take the place of the original xsl:include element.

Because of the way the including stylesheets work, you can apply the same rules to the elements as you would to an entire stylesheet. This means that duplicate attribute-sets are merged into one, just as if you had defined two attribute-sets in the same stylesheet, as explained on Day 5, “Inserting Text and Elements.” The same goes for xsl:output, xsl:strip-space, and xsl:preserve-space elements, as explained on Day 7, “Controlling the Output.”

Caution

If you remember the lessons on Days 5 and 7, you also will remember that if a collision occurs between elements or attributes, the last one occurring in the resulting stylesheet wins. This point is very significant because where you include a stylesheet makes a lot of difference. Also, be aware that some processors may choose to raise an error and stop processing when certain collisions occur.

Suppose the including stylesheet defines UTF-8 as output encoding, but the included stylesheet defines UTF-16. If the xsl:include element occurs before the xsl:output element in the including stylesheet, the output is in UTF-8. However, if the xsl:include element comes after the xsl:output element, the xsl:output element in the included stylesheet occurs last; hence, UTF-16 is used as output encoding instead. Therefore, it is a very good idea to include other stylesheets before any other top-level elements. This way, you make sure that the elements in the including stylesheet always win if a conflict ever occurs.

Duplicate Templates

One situation that you need to be more careful with is having duplicate templates. Suppose you want to override the template dealing with the separate menu courses, so the HTML table would get a border. In that case, you could add the template in Listing 13.7 to Listing 13.4.

LISTING 13.7 Overriding Template for Courses

<xsl:template match="appetizers|entrees|desserts">
    <table border="1">
       <xsl:apply-templates />
    </table>
</xsl:template>

ANALYSIS

When you add Listing 13.7 to Listing 13.6 and apply it to Listing 13.1, the processor can report an error, use one of the templates and continue, or both. If it uses the second approach, it picks the template that is last in the code. This, of course, depends on whether the xsl:include element is inserted before the template or after it. In the former case, the template defined in Listing 13.7 is used; in the latter case, the template in Listing 13.2 is used.

The problem here is that not all processors operate the same. MSXSL and Xalan, for instance, do not report an error, so how are you to know that something may be wrong? Saxon, on the other hand, reports an error and continues processing, so you would spot the error. It is therefore a good idea to test with several processors before proceeding or, better still, try to avoid these situations by clever design.

Note

Day 21, “Designing XML and XSLT Applications,” will deal with issues surrounding the design of stylesheets for reusability.

Duplicate Variables and Parameters

One of the more annoying problems with xsl:include is that duplicate variables and parameters are not allowed. This restriction again conforms to the rules explained on Day 8, “Working with Variables,” and Day 9, “Working with Parameters,” so it doesn’t come as a real surprise. Because the same rules apply, the same scoping rules also apply, so you can still have duplicate variables and parameters in different templates because they do not share the same scope. But you cannot have duplicate global variables and parameters. If you use a central variable to store formatting information, you therefore cannot override it or merge it with another variable like you would do with attribute-sets. So, in these situations, you might want to consider using attribute-sets instead of variables.

Importing Stylesheets

Importing stylesheets into other stylesheets is another way to split them up into chunks and reuse them. You may be wondering why you would need another mechanism for this task if you can already include other stylesheets. The answer is that the rules for importing stylesheets are a lot different from those for including stylesheets. Although the basic goal is the same—reusing code—the methods differ, so you can pick the one that best suits your needs.

The Difference Between Including and Importing

When you include stylesheets, you basically create a new stylesheet. For the resulting stylesheet, the same rules apply as with any other stylesheets. Therefore, you must make sure that while you include stylesheets, you do not include any stylesheets that will make the resulting stylesheet erroneous. When you import stylesheets, however, something different happens. Instead of just copying over the code for the other stylesheets, the code is copied and given an import precedence or priority.

Note

The stylesheet that imports other stylesheets is called the importing stylesheet, whereas the stylesheet being imported is called the imported stylesheet.

For all the elements in the imported stylesheet, the precedence is lower than the elements in the importing stylesheet. This means that a template in the importing stylesheet cannot collide with a template from the imported stylesheet that matches the same node or nodes. Earlier, you learned that the same principle is not true when you include a stylesheet.

Although importing stylesheets has some advantages over including stylesheets, it is much harder to predict how the resulting stylesheet will operate. The point is that when you include stylesheets, you can just replace the xsl:include element with the code from the included stylesheet. Importing a stylesheet is not that simple. Predicting what the resulting stylesheet will look like and which precedence rules are in place is therefore much harder. The obvious result is that seeing what will actually happen is much harder.

Tip

Try to use xsl:include instead of xsl:import wherever it is applicable. By doing so, you can save yourself a lot of headaches from trying to determine the import precedence.

How to Import a Stylesheet

Importing a stylesheet is as easy as including one, with the sole difference that you use the xsl:import element. The following samples are similar to those shown for including stylesheets:

<xsl:import href="myimport.xsl" />
<xsl:import href="../imports/myimport.xsl" />
<xsl:import href="http://www.somewebsite.com/xslimports/myimport.xsl" />

As with including stylesheets, the relative paths are always calculated relative to the stylesheet doing the import, even if that stylesheet has been included or imported by another stylesheet that resides in another directory or on another server.

xsl:import is a top-level element, with the additional restriction that it must be inserted before any other top-level element is used. This is different from including a stylesheet, which may occur at any given point. Importing the same stylesheet more than once, either directly or indirectly, is not an error. This point is actually more significant than you might think. A stylesheet may rely on some other stylesheet when it is the root stylesheet. A new stylesheet that needs both the original stylesheets may elect to import them both, even though one of them is already being imported by the other. In doing so, you can change the import precedence of a stylesheet. This situation is depicted in Figure 13.1.

FIGURE 13.1 Import precedence with multiple imports.

Image

Figure 13.1 shows two situations. In the first, stylesheet A imports stylesheet B, which in turn imports stylesheet C. In this case, stylesheet C has the lowest import precedence and then stylesheet B. In the second situation, stylesheet A imports stylesheet C again after it has imported stylesheet B. Because stylesheet C is imported after stylesheet B, stylesheet C’s import precedence is higher. So, when stylesheet A imports stylesheet C again, the import precedence changes.

Figure 13.2 depicts a more elaborate importing hierarchy, so you can see how import precedence really works.

FIGURE 13.2 Import precedence with hierarchy of imports.

Image

In Figure 13.2, many imports are going on. So, how do you decide which imports have a higher precedence? The rule is that the stylesheet doing the importing has a higher precedence than the stylesheet being imported. Also, the stylesheet imported last has a higher precedence than the one imported first. The import precedence for the stylesheets in Figure 13.2 is therefore A, C, E, D, B, and again C, which comes down to A, C, E, D, and B because the extra import of C is never used.

Because of the import precedence, collisions of top-level elements are much less likely, although not impossible. So, in cases in which including a stylesheet fails, importing a stylesheet might still work. Each element is bound by its own set of import rules. The most significant are, of course, the rules surrounding templates.

Overriding Templates

For templates, the act of importing stylesheets has a lot in common with inheritance in object-oriented programming. Each stylesheet you import probably has several templates that you want to use. However, some templates may not do exactly what you want. You can inherit the templates that work for you and override the templates that don’t by creating a new template with the same matching rule or name.

Overriding templates is much different from including stylesheets, where having duplicate templates can cause big problems. Earlier, Listing 13.7 was added to a stylesheet that included Listing 13.2. With the different processors, including the stylesheet may or may not result in an error. Listing 13.8 shows the same concept, but imports Listing 13.2 rather than includes it.

LISTING 13.8 Stylesheet Importing Listing 13.2

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:import href="13list02.xsl" />
6:
7:    <xsl:template match="/">
8:       <html>
9:       <body>
10:       <h1>Menu</h1>
11:       <xsl:apply-templates />
12:      </body>
13:      </html>
14:   </xsl:template>
15:
16:   <xsl:template match="appetizers|entrees|desserts">
17:     <table border="1">
18:       <xsl:apply-templates />
19:     </table>
20:   </xsl:template>
21: </xsl:stylesheet>


ANALYSIS

Listing 13.8 imports Listing 13.2 on line 5. The template that creates the HTML table in Listing 13.2 is redefined in 13.8 on line 16 in Listing 13.8. Because this template has a higher import precedence, you can be sure that this template is used instead of the template in Listing 13.8. So, the resulting HTML table now has border=”1” specified because of line 17, which is a change over Listing 13.2.

Because of the import precedence, you can import stylesheets that weren’t specifically designed for code reuse. These stylesheets often contain a template matching the root node, but you can override it with your own templates. You can override all the templates that you don’t want to use and use only those templates for which you wanted to import the stylesheet in the first place.

The fact that you override a template of an imported stylesheet doesn’t necessarily mean that you have to re-create all the functionality of the original template. You can use the xsl:apply-imports element to invoke the template that matches the same node and has the next highest import precedence. This way, you can expand the original template by adding text and elements around the original result. This concept is shown in Listing 13.9.

LISTING 13.9 Stylesheet Using xsl:apply-imports to Invoke a Template from an Imported Stylesheet

1:  <?xml version= 1.0  encoding= UTF-8 ?>
2:  <xsl:stylesheet version= 1.0 
3:    xmlns:xsl= http://www.w3.org/1999/XSL/Transform >
4:
5:    <xsl:import href= 13list02.xsl  />
6:
7:    <xsl:template match= / >
8:       <html>
9:       <body>
10:       <h1>Menu</h1>
11:       <xsl:apply-templates />
12:      </body>
13:      </html>
14:   </xsl:template>
15:
16:   <xsl:template match= appetizers|entrees|desserts >
17:      <hr />
18:       <xsl:apply-imports />
19:      <hr />
20:   </xsl:template>
21: </xsl:stylesheet>

ANALYSIS

Listing 13.9 is similar to Listing 13.8. It imports Listing 13.2 on line 5, and the template on line 7 inserts the base HTML elements and a header. The template on line 16 is much different, however. This template inserts a horizontal line with the HTML hr element at the start and end of each course. The xsl:apply-imports element on line 18 then invokes the template matching the same node, which is the template on line 8 of Listing 13.2. That template inserts a table for each of the courses just like before. This table is now surrounded by horizontal lines. Listing 13.10 shows the result from applying Listing 13.9 to Listing 13.1.

OUTPUT

LISTING 13.10 Result from Applying Listing 13.9 to Listing 13.1

<html>
      <body>
      <h1>Menu</h1>
      <hr>
      <table>
        <tr>
           <td>Crab Cakes</td>
           <td>$8.95</td>
        </tr>
        <tr>
           <td>Jumbo Prawns</td>
           <td>$9.95</td>
        </tr>
        <tr>
           <td>Smoked Salmon and Avocado Quesadilla</td>
           <td>$10.95</td>
        </tr>
        <tr>
           <td>Caesar Salad</td>
           <td>$6.95</td>
        </tr>
      </table>
      <hr>
      <hr>
      <table>
        <tr>
           <td>Grilled Salmon</td>
           <td>$19.95</td>
        </tr>
<!--result truncated-->
      </table>
      <hr>
<!--result truncated-->
      </body>
</html>

ANALYSIS Listing 13.10 is truncated for better reading, but it shows you that the result of Listing 13.9 applied to Listing 13.1 is similar to the result shown in Listing 13.5. The difference is that each table is surrounded by two horizontal lines. Figure 13.3 shows what Listing 13.10 looks like when viewed in a browser, so you can see that the horizontal lines are inserted.

FIGURE 13.3 Listing 13.10 viewed in a browser.

Image

If you want to change what is going on inside an imported template, using xsl:apply-imports doesn’t help much. In that case, you still have to rewrite the whole template. The xsl:apply-templates element is only meant to add to the output of imported templates. The more basic the original templates are, the easier it is to alter the final output.

Tip

If you intend to use the xsl:apply-imports element, you should break up functionality in imported stylesheets into templates performing basic functionality. The templates that invoke the imported templates can then add more specific functionality.

Import Rules for Other Elements

Templates are the most important elements in a stylesheet because they do the actual processing. However, several other top-level elements are available, and each has its own import rules. These elements are discussed next.

Attribute-sets

When you have more than one attribute-set with the same name, the attribute-sets are merged into one. Any attribute that occurs in both attribute-sets gets the value that is defined in the attribute-set with the highest import precedence. This means that you can easily override attribute-sets from imported stylesheets because the values in the importing stylesheet’s attribute-set always win.

When two attribute-sets have the same import precedence and colliding attribute values, the regular rules surrounding attribute-sets apply. This means that the processor can report an error or take the value of the last attribute-set defined. You can see how this principle works in practice by looking at Listing 13.11.

LISTING 13.11 Stylesheet Using an Attribute-set

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="html" encoding="utf-8" />
6:    <xsl:strip-space elements="*" />
7:
8:     <xsl:attribute-set name="table">
9:       <xsl:attribute name="bgcolor">#ffffcc</xsl:attribute>
10:       <xsl:attribute name="width">90%</xsl:attribute>
11:    </xsl:attribute-set>
12:
13:    <xsl:template match="appetizers|entrees|desserts">
14:      <table xsl:use-attribute-sets="table">
15:        <xsl:apply-templates />
16:      </table>
17:    </xsl:template>
18:
19:   <xsl:template match="dish">
20:     <tr>
21:       <td><xsl:value-of select="." /></td>
22:       <td><xsl:value-of select="concat ('$',@price)" /></td>
23:     </tr>
24:   </xsl:template>
25: </xsl:stylesheet>

ANALYSIS

Listing 13.11 is much like Listing 13.2, except that on line 8 an attribute-set named table is defined; this attribute-set is used on line 14 when creating an HTML table for each course.

You can now create another stylesheet that uses the attribute-set from Listing 13.11 and changes it somewhat. Listing 13.12 is such a stylesheet.

LISTING 13.12 Stylesheet Importing and Changing Listing 13.11

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:import href="13list11.xsl" />
6:
7:    <xsl:attribute-set name="table">
8:      <xsl:attribute name="bgcolor">#cccccc</xsl:attribute>
9:      <xsl:attribute name="border">1</xsl:attribute>
10:   </xsl:attribute-set>
11: </xsl:stylesheet>

ANALYSIS

The stylesheet in Listing 13.12 is very small. Basically, it inherits the stylesheet in Listing 13.11, creating the exact same output, except that the attribute-set named table is changed on line 7. The bgcolor attribute on line 8 overrides the bgcolor attribute on line 9 of Listing 13.11. The width attribute is still used, and line 9 defines a new attribute named border. For all intents and purposes, the attribute-set that will be used when the stylesheet is processed is shown in Listing 13.13.

LISTING 13.13 Attribute-set from Merged Listings 13.11 and 13.12

<xsl:attribute-set name="table">
  <xsl:attribute name="bgcolor">#cccccc</xsl:attribute>
  <xsl:attribute name="width">90%</xsl:attribute>
  <xsl:attribute name="border">1</xsl:attribute>
</xsl:attribute-set>

ANALYSIS

In Listing 13.13, the new attribute-set now has three attributes instead of two. Because the bgcolor attribute is overridden, the table will have a different background color than before. Your ability to merge stylesheets is useful when you have a stylesheet that already does most of what you want, but you want to change some minor details. As you can see, this process is easy, and the new stylesheet is remarkably short.

Decimal Formats

Import precedence is of no significance to the xsl:decimal-format element. Having more than one xsl:decimal-format element with the same name (unless their definitions are identical) causes an error. The processor always reports an error if this is the case, so you can handle this problem at design time.

Output Control

The xsl:output element behaves more or less like it would in a single stylesheet, with only one exception: If attributes that have different values are encountered, the one with the highest import precedence wins. If those elements have the same import precedence, the processor can report an error or take the last one specified. In essence, the rules for xsl:output follow the rules for attribute-sets.

Whitespace Handling

You handle whitespace by using the xsl:strip-space and xsl:preserve-space elements. Practically speaking, you will have conflicts if an element is matched by both of these elements. In that case, the one with the higher import precedence wins. If the import precedence rules do not give a clear winner, the matching rules explained on Day 7 are used. These rules are very much the same as template matching rules.

Because of the import precedence, you might get some surprising results: Rules that win when there is no import precedence may not win when there is. Listings 13.14 and 13.15 illustrate this point.

LISTING 13.14 Stylesheet Stripping Whitespace

<?xml version=″“1.0”" encoding="UTF-8"?>
<xsl:stylesheet version=″“1.0”"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:strip-space elements="appetizers entrees desserts" />

  <xsl:template match="/">
    <xsl:copy-of select="." />
  </xsl:template>
</xsl:stylesheet>

ANALYSIS

The stylesheet in Listing 13.14 copies all the elements from the source XML but strips the whitespace in the appetizers, entrees, and desserts elements.

Listing 13.15 imports this stylesheet.

LISTING 13.15 Stylesheet Importing Listing 13.14 and Preserving Space

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:import href="13list14.xsl" />
6:
7:    <xsl:preserve-space elements="*" />
8:
9:    <xsl:template match="/">
10:     <xsl:copy-of select="." />
11:   </xsl:template>
12: </xsl:stylesheet>

ANALYSIS

On line 5, the stylesheet in Listing 13.15 imports Listing 13.14. As you can see, this stylesheet is imported before any other elements, as is required. Line 7 contains an xsl:preserve-space element telling the processor to preserve whitespace for all elements. Because this element is less specific than the xsl:strip-space element in Listing 13.14, the xsl:strip-space element would normally win over the xsl:preserve-space element when it comes to the elements specified in the xsl:strip-space element. However, because import precedence favors the xsl:preserve-space element, the xsl:strip-space element is completely ignored, and Listing 13.14 is copied over without any whitespace stripping.

In the unlikely case that a conflict occurs between elements with the same import precedence, the processor can again report an error or take the last elements specified.

Variables and Parameters

Variables and parameters are fairly straightforward. If two global variables or parameters have the same name, the one with the highest import precedence is used. Having two variables or parameters with the same name and the same import precedence causes an error, so you have to deal with this issue at design time. Because import precedence is hierarchical, this problem can occur only if you defined the same global variable (or parameter) twice or included a stylesheet that defines the same global variable.

Summary

In today’s lesson, you learned that you can either include or import existing stylesheets. Including stylesheets is useful for breaking up functionality into reusable pieces. However, you need to make sure that no conflicts occur between elements in the included stylesheet and the including stylesheet. The basic rule you can use is that the whole stylesheet will act as one after all includes have been inserted.

Importing stylesheets is different from including stylesheets in that elements in imported stylesheets get an import precedence that defines if and when these elements are used. This way, you can take an existing, complete stylesheet and override key elements, such as templates and attribute-sets, to change the output.

Including and importing therefore have two different purposes. Including is more useful for making reusable template libraries, whereas importing is more useful when you already have stylesheets that do what you want for the most part.

Tomorrow’s lesson is about the other side of the coin: using multiple source XML documents so that their data can be processed as one document. Like importing and including stylesheets can break up a stylesheet in smaller pieces that are easier to handle, being able to use multiple XML sources does the same for large XML documents.

Q&A

Q If I can import a stylesheet, I don’t see why I would ever want to include one. When would I include a stylesheet rather than import it?

A When importing a stylesheet, you always have to deal with import precedence. This means that when you have a hierarchy of imports, you might inadvertently override a template you don’t want to override. You can avoid this problem by including instead of importing. Including stylesheets is less complex and therefore preferred if you don’t get any colliding elements.

Q How do I determine the import precedence for an element?

A Keep in mind that imports are always done first in a stylesheet and that the later an element is imported, the higher its import precedence is. The easiest way to determine import precedence is to map out the imports and includes and then determine where conflicting elements occur. Based on the map, you can determine which will win.

Q Can I see the resulting stylesheet before it is used to process a document?

A The existing processors don’t allow you to see the resulting stylesheet. After tomorrow’s lesson, you will be able to create a stylesheet that does this for including stylesheets. For importing stylesheets, seeing the resulting stylesheet is possible but much harder.

Workshop

This workshop tests whether you understand all the concepts you learned today. It is helpful to know and understand the answers before starting tomorrow’s lesson. You can find the answers to the quiz questions and exercises in Appendix A.

Quiz

1. True or False: A stylesheet that is included can contain a global variable with the same name as in another included stylesheet or the including stylesheet.

2. True or False: The template occurring last after all imports and includes have been done has the highest import precedence.

3. What happens when an imported stylesheet and the importing stylesheet both have an xsl:output element?

4. Can you include a template and then import one?

5. Does importing or including have any bearing on local variables?

Exercise

1. Create a new stylesheet that either includes or imports Listing 13.6. Make sure that it creates output from Listing 13.1 so that each course HTML table is preceded by a header containing the course title.

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

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