codetoad.com
  ASP Shopping CartForum & BBS
  - all for $20 from CodeToad Plus!
  
  Home || ASP | ASP.Net | C++/C# | DHTML | HTML | Java | Javascript | Perl | VB | XML || CodeToad Plus! || Forums || RAM 
Search Site:



Previous Page  Page 1 Page 2 Page 3 Page 4 Page 5 Page 6  Page 8 Page 9 Page 10 Page 11 Next Page  

Context-Dependent Processing

We've now introduced a number of elements into our XML structure that we're actually using elsewhere in different ways. For example, we use the <Character> element to indicate the name of a character in a description, and to hold information about a character within the <CastMember> element. The template makes no distinction between these two uses of the <Character> element, and does the same thing for each, making a <span> element with a character class:

 

<xsl:template match="Character">

  <span class="character">

    <xsl:apply-templates />

  </span>

</xsl:template>

 

At the moment, we're also creating a <span> element when we create the cast list, in the template matching the <CastMember> element:

 

<xsl:template match="CastMember">

  <li>

    <span class="character">

      <xsl:apply-templates select="Character" />

    </span>

    <span class="actor">

      <xsl:apply-templates select="Actor" />

    </span>

  </li>

</xsl:template>

 

This means we end up with two <span> elements around the names of the characters in the cast list. We can get rid of the superfluous <span> element either by reverting back to using <xsl:value-of> to get the name of the character, or by removing the <span> element in the template that matches <CastMember> elements (and the same applies for the <Actor> elements as well, since we might name actors in a description):

 

<xsl:template match="CastMember">

  <li>

    <xsl:apply-templates select="Character" />

    <xsl:apply-templates select="Actor" />

  </li>

</xsl:template>

 

So now we have the same template being used to process <Character> elements in different contexts. However, one of the extensions that we made earlier in this chapter was to have the <Character> and <Actor> elements within <CastMember> actually give both a name and a description of the character. When we take this into account, we have a problem because we don't want the <span> element to contain both the name and the description of the character. We need a different template for the <Character> and <Actor> elements when they are children of <CastMember> elements (ones that just apply templates to the <Name> element child of the <Character> or <Actor> element). The new templates for the <Character> elements that occur in <CastMember> elements need to look like:


 

<xsl:template match="Character">

  <span class="character">

    <xsl:apply-templates select="Name" />

  </span>

</xsl:template>

 

But we can't use this template with the <Character> elements that occur in <Description> elements because they don't contain <Name> elements. If we use this template with those elements, the <span> elements won't have any content.

 

So we need some way of having different templates for the different contexts in which these elements are allowed. We can do this by changing the value of the match attribute of <xsl:template>, and this is where we need to use patterns, which are why they are introduced next.

Patterns

So far we've seen four kinds of values for the match attribute of <xsl:template>:

 

        / matches the root node

        * matches any element

        text() matches text nodes

        the name of an element matches that element

 

These values are all examples of patterns. An XSLT processor uses the pattern specified in the match attribute of <xsl:template> to work out whether it can use a template to process a node to which you've told it to apply templates. In our case, we need one pattern to match <Character> elements that are children of <CastMember> elements, and another pattern to match <Character> elements that appear at any level within <Description> elements. In both cases, we're checking the context in which the <Character> element appears. The two patterns we need are:

 

        CastMember/Character to match <Character> elements that occur within <CastMember> elements

        Description//Character to match <Character> elements that occur nested to any level within <Description> elements

 

These types of patterns are technically known as location path patterns. As you can see, location path patterns look a lot like the location paths that we use to select nodes to process in select attributes, and it can be easy to get confused between the two. You use location paths to select nodes; they point from the current node to a set of other nodes in the tree, stepping down from element to child. You use location path patterns to match nodes; they test whether a particular node has particular ancestors, looking up the node tree to work out the context of the node.

 

A location path pattern is made up of a number of step patterns, separated by either / or //. If the separator is a /, then the pattern tests a parent-child relationship. For example, the pattern Description/Character matches <Character> elements whose immediate parent is a <Description> element. If the separator is //, on the other hand, then the pattern tests an ancestor-descendent relationship. For example, the pattern Description//Character matches <Character> elements that have a <Description> element as an ancestor at any level.


 

Location path patterns enable you to match elements according to the context in which they occur.

Identifying Elements in Different Contexts

In our XML document, TVGuide3.xml, there are some elements that have different meanings in different contexts. If you remember back that far, it was one of our design decisions when we first put together our XML structure that we would make use of the context an element was in, rather than use different names for elements in different contexts, to work out what an element meant and what we should do with it.

 

So now we have to deal with that decision by creating different templates with different match patterns for the different contexts in which an element can occur. The contexts within which different elements can occur are shown in the following table:

 

Element

Contexts

<Name>

child of <Channel>

child of <Character>

child of <Actor>

<Description>

child of <Program>

child of <Character>

child of <Actor>

<Character>

child of <CastMember>

descendent of <Description>

<Actor>

child of <CastMember>

descendent of <Description>

<Series>

child of <Program>

descendent of <Description>

<Program>

child of <Channel>

descendent of <Description>

<Channel>

child of <TVGuide>

descendent of <Description>

 

A stylesheet that deals with documents that follow our markup language really needs to have templates that deal with elements occurring in each of these possible contexts, using patterns that include
ancestry information.


 

At this stage, we'll create a new version of the stylesheet, TVGuide9.xsl, which contains separate templates for each of these elements in each of these contexts. You should be able to put together different templates for the elements in their different contexts as mapping rules. For example, the <Name> element can occur in three contexts – <Channel>, <Character>, and <Actor> – so there should be three corresponding templates:

 

<xsl:template match="Channel/Name">...</xsl:template>

<xsl:template match="Character/Name">...</xsl:template>

<xsl:template match="Actor/Name">...</xsl:template>

 

As you add these templates, you should consider whether some of the HTML that you're currently generating in higher-level templates can be generated in lower-level templates instead. For example, my feeling is that the <Name> element in the <Channel> element maps on to the <h2> heading element in the result, so the template should look like:

 

<xsl:template match="Channel/Name">

  <h2 class="channel"><xsl:value-of select="." /></h2>

</xsl:template>

 

But if you create the <h2> element in the above template, you don't need to create it in the template for the <Channel> element. So you need to change that template too, removing the <h2> element that you were creating within it:

 

<xsl:template match="TVGuide/Channel">

  <xsl:apply-templates select="Name" />

  <xsl:apply-templates select="Program" />

</xsl:template>

 

This template only applies to <Channel> elements that are children of the <TVGuide> element, not those that are descendents of <Description> elements.

 

If you go through this process religiously, you should end up with about 19 different templates, as in TVGuide9.xsl. Using TVGuide9.xsl with TVGuide3.xml results in a page in which both the <Character> elements within the cast list and those within the <Description> are treated properly, so the result looks like the following when you view it in Internet Explorer:


 

 

This process of adding templates on an element-by-element basis, taking account of the different contexts in which an element can occur, has left us with a lot of templates, but it has made the stylesheet more robust. We've handled the problem that we had with <Character> elements being treated differently in different contexts, and we've included templates to handle the possibility of things that aren't actually present in TVGuide3.xml, but which could happen in more complete documents that follow the markup language, such as <Channel> elements in <Description> elements.

Unnecessary Templates

Adding templates to deal with every kind of element, in every context, within a stylesheet can leave you with a stylesheet that contains lots of templates that actually don't do very much. This makes the stylesheet harder for you to maintain (because it's longer) and it makes more work for the processor when it needs to identify which template to apply in a particular situation. You need to find a judicious balance between the two.

 

First, you don't need to create a template for every element in every context. Remember that if the XSLT processor can't find a template that matches a node, then it will use a built-in template. If an element (and all its content) gets processed by the built-in templates, then you'll just get the value of the node. So, if you have templates that look like either of the following:

 

<xsl:template match="...">

  <xsl:value-of select="." />

</xsl:template>

 

<xsl:template match="...">

  <xsl:apply-templates />

</xsl:template>

 

then you may as well get rid of them.


 

Second, you can get rid of templates that essentially process the children of the element that they match in the order that they appear. For example, the revised template for the <Channel> elements that are children of <TVGuide> elements is like that:

 

<xsl:template match="TVGuide/Channel">

  <xsl:apply-templates select="Name" />

  <xsl:apply-templates select="Program" />

</xsl:template>

 

When a <Channel> element appears as a child of a <TVGuide> element then it can only contain a <Name> element followed by any number of <Program> elements. So telling the processor to apply templates first to the <Name> element and then to the <Program> elements has the same effect as telling the processor to apply templates to all the <Channel> element's children in the order they occur, which would be the template:

 

<xsl:template match="TVGuide/Channel">

  <xsl:apply-templates />

</xsl:template>

 

This template has the same effect as the built-in template for elements, so you can delete it without affecting the result of the stylesheet.

 

Finally, you can get rid of templates that match elements that are never selected for processing. Remember that a template is never actually used if the processor never gets told to apply templates to a node that matches that template. A template that is never matched will never be used, and can therefore be safely removed.

 

TVGuide9.xsl does contain a few templates that aren't really necessary, and to make it easier to understand we could prune it to create TVGuide10.xsl.

 

There are three templates that do the same thing as the built-in templates, and just contain an <xsl:apply-templates> or an <xsl:value-of> instruction that gives the value of the current node. They are the one matching <Name> element children of <Character> elements, the one matching <Name> element children of <Actor> elements, and the one matching <Description> element children of <Program> elements – you can safely delete them:

 

<xsl:template match="Character/Name">

  <xsl:value-of select="." />

</xsl:template>

 

<xsl:template match="Actor/Name">

  <xsl:value-of select="." />

</xsl:template>

 

<xsl:template match="Program/Description">

  <xsl:apply-templates />

</xsl:template>


 

We identified the template matching <Channel> element children of the <TVGuide> element as falling into the second category. It had two <xsl:apply-templates> in it, but these selected nodes in the same order as they occurred in the source document, essentially the same as applying templates to all the children, which again is just what the built-in templates do. So you can safely delete the
following template:

 

<xsl:template match="TVGuide/Channel">

  <xsl:apply-templates select="Name" />

  <xsl:apply-templates select="Program" />

</xsl:template>

 

Finally, in TVGuide9.xsl there are two templates that can never be applied – the one matching <Description> elements within <Character> elements, and the one matching <Description> elements within <Actor> elements. These templates will never get activated because the templates that match <Character> and <Actor> elements (within <CastMember> elements) never apply templates to their child <Description> elements. So these two templates can also be deleted:

 

<xsl:template match="Character/Description" />

 

<xsl:template match="Actor/Description" />

 

Once you've done all that, you should have something like the following stylesheet, TVGuide10.xsl. The ordering of the templates within the stylesheet doesn't matter.

 

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"

                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<xsl:template match="/">

  <html>

    ...

  </html>

</xsl:template>

 

<xsl:template match="Channel/Name">

  <h2 class="channel"><xsl:value-of select="." /></h2>

</xsl:template>

 

<xsl:template match="Channel/Program">

  <div>

    ...

  </div>

</xsl:template>

 

<xsl:template match="Program/Series">

 <span class="title"><xsl:value-of select="." /></span>

</xsl:template>

 

<xsl:template match="CastMember">

  <li>

    <xsl:apply-templates select="Character" />

    <xsl:apply-templates select="Actor" />


 

 

  </li>

</xsl:template>

 

<xsl:template match="CastMember/Character">

  <span class="character">

    <xsl:apply-templates select="Name" />

  </span>

</xsl:template>

 

<xsl:template match="CastMember/Actor">

  <span class="actor">

    <xsl:apply-templates select="Name" />

  </span>

</xsl:template>

 

<xsl:template match="Description//Character">

  <span class="character">

    <xsl:apply-templates />

  </span>

</xsl:template>

 

<xsl:template match="Description//Actor">

  <span class="actor">

    <xsl:apply-templates />

  </span>

</xsl:template>

 

<xsl:template match="Link">

  <a href="{@href}">

    <xsl:apply-templates />

  </a>

</xsl:template>

 

<xsl:template match="Description//Program">

  <span class="program"><xsl:apply-templates /></span>

</xsl:template>

 

<xsl:template match="Description//Series">

  <span class="series"><xsl:apply-templates /></span>

</xsl:template>

 

<xsl:template match="Description//Channel">

  <span class="channel"><xsl:apply-templates /></span>

</xsl:template>

 

</xsl:stylesheet>

 

Using TVGuide10.xsl with TVGuide3.xml should give the same result as TVGuide9.xsl did.

Previous Page  Page 1 Page 2 Page 3 Page 4 Page 5 Page 6  Page 8 Page 9 Page 10 Page 11 Next Page  




Click here to Buy!

Buy Beginning XSLT here

© Copyright 2002 Wrox Press This chapter is written by Jeni Tennison and taken from "Beginning XSLT" published by Wrox Press Limited in June 2002; ISBN 1861005946; copyright © Wrox Press Limited 2002; all rights reserved.

No part of these chapters may be reproduced, stored in a retrieval system or transmitted in any form or by any means -- electronic, electrostatic, mechanical, photocopying, recording or otherwise -- without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.











Recent Forum Threads
• C++
• Re: refresh parent after closing pop up window
• Dynamic Insertion
• Date and Time function around the world???
• Significant Factors
• Perl array access
• Re: huffman encoding and decoding in C++...
• Perl One Liner: Replace {(
• Re: html including php, accessing the functions


Recent Articles
ASP GetTempName
Decode and Encode UTF-8
ASP GetFile
ASP FolderExists
ASP FileExists
ASP OpenTextFile
ASP FilesystemObject
ASP CreateFolder
ASP CreateTextFile
Javascript Get Selected Text


© Copyright codetoad.com 2001-2009