xsl-list
[Top] [All Lists]

Re: Can't identify last ancestor node

2005-06-28 05:48:16
On Tue, 2005-06-28 at 01:36 -0700, Mat Bergman wrote:
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?

Don't use xsl:text to write xml. Don't think in terms of generating
start and end tags. You can't separate the two. Forget about the ul s
for a bit, pull out your conditions, and make certain your creation of
li s looks something like:

<li><xsl:value-of select="@name"/></li>

and nothing like:

<xsl:text
disable-output-escaping="yes">&lt;li&gt;</xsl:text>             
<xsl:value-of select="@name"/>
<xsl:text
disable-output-escaping="yes">&lt;/li&gt;</xsl:text>
 
Then you can start asking: How do I get some ul s in there? (Hint: no
xsl:text allowed. Really.)

Though someone else will have to answer because I'll be offline.

sdc

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 

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--




--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--