Subject: Re: testing for last node in a list From: Jeni Tennison <Jeni.Tennison@xxxxxxxxxxxxxxxx> Date: Wed, 24 May 2000 18:18:28 +0100 |
Ann Marie, >Here's another tough one. I like a challenge ;) >Now I need to output the @NAME of each ancestor, except the last one, with the href tag, >except for the last one. The last ancestor doesn't need a href tag because it is the name of the current >matched class. OK, so taking an original of: <?xml version="1.0" ?> <?xml:stylesheet type="text/xsl" href="test.xsl"?> <SCHEMA> <CLASS NAME="animal" /> <CLASS NAME="mammal" SUPERCLASS="animal" /> <CLASS NAME="canine" SUPERCLASS="mammal" /> <CLASS NAME="dog" SUPERCLASS="canine" /> <CLASS NAME="wolf" SUPERCLASS="canine" /> <CLASS NAME="feline" SUPERCLASS="mammal" /> <CLASS NAME="cat" SUPERCLASS="feline" /> </SCHEMA> then for the wolf, you now want to give a hierarchy something like: ... <a href="animal.html">animal</a> | <a href="mammal.html">mammal</a> | <a href="canine.html">canine</a> | wolf ... [deliberately simplified] >Using your approach, I ask myself what do I know about node X. In this case, the @NAME value of Node X >always equals the @NAME value of the matched node when the template was called. Or to be more precise, the @NAME value of the matched node when the template was *originally* called (i.e. from the <xsl:apply-templates select="." mode="hierarchy" /> within the CLASS-matching template). Unfortunately, 'you can't get there from here' - if I *only* know about node X, there is no way of knowing whether it was the one 'originally' selected or not (though see Caveat 2 below). Within a template the only things you know about are where you are, what the source document looks like and the values of any globally-defined parameters or variables - nothing about what previous processing has done to you. >So, I tried to define a variable to store the name of the matched class. Then I could compare the name >of the matched class to the name of the current context node. But the value of the variable is >automatically updated each time the template is called. Right, because the variable defining the name of the class is evaluated each time the template is called with respect to the current node, which changes each time the template is called. Caveat 1: There are lots of approaches that you could take to getting the output you want from your source document. Your requirements are gradually shifting (which is no bad thing - taking an incremental approach can be really helpful, if not essential), and it may be that at some point your requirements have changed so much that you want to rethink the approach you're taking. I don't think you're quite there yet, but it's worth staying open minded. Caveat 2: I've assumed (in putting |s in your hierarchy) that it's easy, given a node X, to tell whether it's at the *top* of the hierarchy, but impossible to tell whether it's at the *bottom* of the hierarchy. This rests on the assumption that you are constructing hierarchies for classes that have subclasses as well as for those classes at the bottom of the hierarchy (so all the classes in the example above rather than just dog, wolf & cat). If I'm wrong in this assumption let me know because a different approach would take advantage of that. OK, so here's one possible approach. You want the hierarchy-generating template to know whether it is generating output for the class at the bottom of the hierarchy, or one part of the way up. When need to know something and you can't work it out, you *have* to be told. The way you tell a template things is by passing it parameters. Let's call the parameter 'bottomNode' and make it either 'true' (if you're at the bottom) or 'false' (if you're not). (We could equally make a parameter called 'matchedNode' and give it the value of the name of the originally matched node - as you said, within the template we know the @NAME of the current node, and we could test it against that - but actually this is all we need for now.) We define the parameter just like a variable, but using the xsl:param element: <xsl:param name="bottomNode" /> Now, we could leave it like that and make sure we set it each time we apply the template, but most of the time that we're calling the template, we're not calling it on the bottomNode, so we may as well default it to false: <xsl:param name="bottomNode">false</xsl:param> Now we've defined that parameter, the template knows whether it's at the bottom of the hierarchy or not and we can test that to decide what to output. Rather than doing two tests (one to see whether we're at the top and only adding a | above if not, and one to see if we're at the bottom and only putting in a link if not), we may as well use this new information to test whether to add a | *below* as well. So: <xsl:template match="CLASS" mode="hierarchy"> <xsl:param name="bottomNode">false</xsl:param> <!-- applies hierarchy template to the parent of the current class --> <xsl:apply-templates select="key('classes', @SUPERCLASS)" mode="hierarchy"/> <!-- outputs details about the current class --> <xsl:choose> <xsl:when test="$bottomNode = 'false'"> <br data="{@NAME} -- {@SUPERCLASS}"> <a href="{@NAME}.html"> <xsl:value-of select="@NAME"/> </a> </br> <xsl:text>|</xsl:text> </xsl:when> <xsl:otherwise> <br data="{@NAME} -- {@SUPERCLASS}"> <xsl:value-of select="@NAME"/> </br> </xsl:otherwise> </xsl:choose> </xsl:template> OK, so the template is using the parameter as we want; we just need to make sure that it's being *passed* the parameter as we want. The hierarchy template is being applied twice: 1. within the template generating the basic information for the class 2. recursively within the hierarchy template Whenever the hierarchy template is applied within the template generating the basic information for the class, it is being applied to the class at the bottom of the hierarchy. So within that xsl:apply-templates, we want to make sure that the parameter is passed a value of 'true', which we do using an xsl:with-param element: <xsl:template match="CLASS"> ... <xsl:apply-templates select="." mode="hierarchy"> <xsl:with-param name="bottomNode">true</xsl:with-param> </xsl:apply-templates> ... </xsl:template match="CLASS"> Whenever the hierarchy template is applied recursively within itself, it's being applied to something that isn't at the bottom of the hierarchy. So it needs to have $bottomNode = 'false'. Fortunately, we made it 'false' by default, so there's no need to use an xsl:with-param within that xsl:apply-templates element (wasn't that great forward thinking on our part! ;) I've tested this approach and it works correctly in SAXON. Cheers, Jeni Dr Jeni Tennison Epistemics Ltd, Strelley Hall, Nottingham, NG8 6PE Telephone 0115 9061301 ? Fax 0115 9061304 ? Email jeni.tennison@xxxxxxxxxxxxxxxx XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: testing for last node in a list, Ann Marie Rubin - Su | Thread | Re: testing for last node in a list, Ann Marie Rubin - Su |
Re: Netscape Support for XSL - clie, Imran Rashid | Date | Re: Error..., Jeni Tennison |
Month |