[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: testing for last node in a list

Subject: Re: testing for last node in a list
From: Ann Marie Rubin - Sun PC Networking Engineering <Annmarie.Rubin@xxxxxxxxxxxx>
Date: Wed, 24 May 2000 15:46:59 -0400 (EDT)
xml testing for last node
Jeni,

You are correct again. I am learning so much about XSL from your thorough explanations.

By experimenting, I found another way to do this. Within the CLASS-matching template, I apply templates 
only to the parent of the matched class node.  I changed the <xsl:apply-templates select="." 
mode="hierarchy" /> statement to <xsl:apply-templates select="key('classes', @SUPERCLASS)" 
mode="hierarchy"/>. 

Thanks again for your invaluable help.

Ann Marie


<xsl:choose>
    <xsl:when test="key('classes', @SUPERCLASS)">
        <xsl:apply-templates select="key('classes', @SUPERCLASS)" mode="hierarchy"/>
        <xsl:text> &#xa0;   &#xa0;|</xsl:text>
        <br></br>
        <xsl:text> &#xa0;  &#xa0;+--</xsl:text>
        <b><xsl:value-of select="@NAME"/></b>
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="@NAME"/><xsl:text> is a top level class.</xsl:text>
    </xsl:otherwise>
</xsl:choose>

	X-Sender: JTennison@NTServer
	To: Ann Marie Rubin - Sun PC Networking Engineering <Annmarie.Rubin@xxxxxxxxxxxx>
	From: Jeni Tennison <Jeni.Tennison@xxxxxxxxxxxxxxxx>
	Subject: Re: testing for last node in a list
	Cc: xsl-list@xxxxxxxxxxxxxxxx
	Mime-Version: 1.0
	X-MDaemon-Deliver-To: Annmarie.Rubin@xxxxxxxxxxxx
	Content-Transfer-Encoding: 8bit
	X-MIME-Autoconverted: from quoted-printable to 8bit by purol.East.Sun.COM id NAA01693
	
	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

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.