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

RE: Can't identify last ancestor node

Subject: RE: Can't identify last ancestor node
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Tue, 28 Jun 2005 10:01:19 +0100
identify element test
You haven't told us what the context node is for your code snippets -
presumably it's the <menu> element that you're testing? In that case they
don't make any sense at all. Looking at the ancestors of a node can't tell
you whether it has any following siblings, for example. (From your question,
I think you may have confused your ancestors with your descendants).

Best way to do this is probably to set a global variable to the last menu
element:

<xsl:variable name="last-menu" select="(//menu)[last()]"/>

and then when processing each menu element, test

<xsl:if test=". is $last-menu">  (XSLT 2.0)

<xsl:if test="generate-id(.) = generate-id($last-menu)"/>  (XSLT 1.0)

Michael Kay
http://www.saxonica.com/
 

> -----Original Message-----
> From: Mat Bergman [mailto:matbergman@xxxxxxxxx] 
> Sent: 28 June 2005 09:36
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject:  Can't identify last ancestor node
> 
> Using this XML data:
> 
> <menudata>
> 	<menu name="link1"/>
> 	<menu name="link2">
> 		<menu name="link2a"/>
> 		<menu name="link2b"/>
> 	</menu>
> </menudata>
> 
> 
> I am producing this HTML output:
> <ul>
> <li>link1</li>
> <li>link2
> 	<ul>
> 	<li>link2a</li>
> 	<li>link2b</li>
> 	</ul>
> </li>
> </ul>
> 
> I am using a convoluted stylesheet that tokenizes an
> attribute and generates the HTML output based on the
> attribute's value. My problem is identifying the last
> nested node, in this example "link2b", so that I can
> customize that node's output.
> 
> My complete stylesheet is below, but to summarize I
> made two attempts:
> 
> <xsl:for-each select="ancestor::menu">
> 	<xsl:if test="position()=last()">
> 	<xsl:text>Write custom end text here</xsl:text>
> 	</xsl:if>
> </xsl:for-each>
> 
> and
> 
> <xsl:if test="ancestor::menu[last()]">
> <xsl:text>Write custom end tag here</xsl:text>
> </xsl:if>
> 
> Both attempts wrote to each individual node, not just
> the last one. How can I tell my stylesheet to write
> one thing for the last <menu> node, and something else
> for the rest?
> 
> Here's the entire stylesheet:
> 
> <?xml version="1.0"?>
> <xsl:stylesheet version="1.0"
>               
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> 	
> <xsl:output method="html" indent="yes"/>
> 
> <!-- siteID determines if a link is displayed for a
> specific cobrand -->
> <xsl:param name="siteID" select="2"/>
> 
> <!-- Displays navigation -->
> <xsl:template match="/">
> 
> 	<ul>
> 
> 	<!-- tokenize all <menu> nodes -->
> 	<xsl:for-each select="//menu">
> 
> 		<!-- Tokenize attributes to check if siteID is
> excluded -->
> 		<xsl:call-template name="tokenize">
> 			<xsl:with-param name="string" 
> select="@exsites"/>
> 			<xsl:with-param name="delimiters" select="','"/>
> 			<xsl:with-param name="level"
> select="'process-includeExclude'"/>
> 		</xsl:call-template>
> 
> 	</xsl:for-each>
> 
> 	</ul>
> 
> </xsl:template>
> 
> 
> <!-- Extract individual values from comma-delimited
> attribute -->
> <xsl:template name="tokenize">
> 	<xsl:param name="string" select="''" />
> 	<xsl:param name="delimiters" select="' &#x9;&#xA;'"
> />
> 	<xsl:param name="level" select="''" />
> 
> 	<xsl:call-template name="_tokenize-delimiters">
> 		<xsl:with-param name="string" select="$string" />
> 		<xsl:with-param name="delimiters"
> select="$delimiters" />
> 		<xsl:with-param name="level" select="$level" />
> 	</xsl:call-template>
> 
> </xsl:template>
> 
> 
> <xsl:template name="_tokenize-delimiters">
> 
> 	<xsl:param name="string" />
> 	<xsl:param name="delimiters" />
> 
> 	<xsl:param name="level" />
> 
> 	<xsl:param name="last-delimit"/> 
> 	<xsl:variable name="delimiter"
> select="substring($delimiters, 1, 1)" />
> 	
> 	<xsl:choose>
> 		<xsl:when test="not($delimiter)">
> 
> 		<!-- Sends individual attribute value for processing
> -->
> 		<xsl:call-template name="process-includeExclude" >
> 			<xsl:with-param 
> name="currentExsite"><xsl:value-of
> select="$string"/></xsl:with-param>	
> 		</xsl:call-template>
> 
> 	   </xsl:when>
> 
> 	<!-- process string until all tokens are separated
> -->
>    <xsl:when test="contains($string, $delimiter)">
>      <xsl:if test="not(starts-with($string,
> $delimiter))">
>        <xsl:call-template name="_tokenize-delimiters">
>          <xsl:with-param name="string"
> select="substring-before($string, $delimiter)" />
>          <xsl:with-param name="delimiters"
> select="substring($delimiters, 2)" />
>        <xsl:with-param name="level" select="$level" />
> 
>        </xsl:call-template>
>      </xsl:if>
>      <xsl:call-template name="_tokenize-delimiters">
>        <xsl:with-param name="string"
> select="substring-after($string, $delimiter)" />
>        <xsl:with-param name="delimiters"
> select="$delimiters" />
>        <xsl:with-param name="level" select="$level" />
>      </xsl:call-template>
>    </xsl:when>
>    <xsl:otherwise>
>      <xsl:call-template name="_tokenize-delimiters">
>        <xsl:with-param name="string" select="$string"
> />
>        <xsl:with-param name="delimiters"
> select="substring($delimiters, 2)" />
>        <xsl:with-param name="level" select="$level" />
>      </xsl:call-template>
>    </xsl:otherwise>
>  </xsl:choose>
> </xsl:template>
> 
> 
> <xsl:template name="process-includeExclude"
> match="menu">
> <xsl:param name="currentExsite" select="''" />
> 
> 	<!-- check if siteID is excluded or included -->
> 	<xsl:if test="$currentExsite!=$siteID">
> 	<xsl:if test="@incsites=$siteID or @incsites=0">
> 
> 		<!-- Write <li> tags -->
> 		<xsl:text
> disable-output-escaping="yes">&lt;li&gt;</xsl:text>		
> 
> 		<!-- Write list content -->
> 		<xsl:value-of select="@name"/>
> 
> 		<!-- Nested list - Write nested <ul> tags -->
> 		<xsl:if test="count(menu) > 0">
> 		<xsl:text
> disable-output-escaping="yes">&#10;&lt;ul&gt;&#10;</xsl:text>
> 		</xsl:if>
> 
> 		<!-- Write </li> tags for top-level and nested items
> -->
> 		<xsl:if test="count(menu) = 0">
> 		
> 		<xsl:choose>
> 
> <!-- This is where I am having trouble.  I want this
> to appear only at the last menu/menu node. Instead
> it's selecting all menu ancestors. -->
> 
> 			<!-- If it's the last sub-menu, close 
> with an </li>
> and a </ul> -->
> 			<xsl:when test="ancestor::menu[last()]">
> 				<xsl:text
> disable-output-escaping="yes">&lt;/li&gt;&lt;/ul&gt;
> 				</xsl:text>
> 			</xsl:when>
> 
> 			<!-- If it's not the last sub-menu, 
> close with an
> </li> -->
> 			<xsl:otherwise>
> 				<xsl:text
> disable-output-escaping="yes">&lt;/li&gt;
> 				</xsl:text>			
> 			</xsl:otherwise>
> 
> 		</xsl:choose>
> 
> 		</xsl:if>
> 
> 	</xsl:if>	
> 	</xsl:if>		
> 
> 
> </xsl:template>
> 
> </xsl:stylesheet>
> 
> 
> Thanks,
> 
> -Mat
> 
> 
> 
> 
> __________________________________________________
> Do You Yahoo!?
> Tired of spam?  Yahoo! Mail has the best spam protection around 
> http://mail.yahoo.com 

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.