## Re: [xsl] Unexpected result from <a href="...">...</a> in some cases

 Subject: Re: [xsl] Unexpected result from ... in some cases From: Quinn Taylor Date: Tue, 19 Aug 2008 22:44:33 -0600
On Aug 19, 2008, at 9:18 PM, Quinn Taylor wrote:

Ken, thanks so much for taking the time to help out. Sure enough, your solution works as advertised. :-)

One thing I'd like to simplify/optimize is the number of recursive descents. It seems that your code recurses all the way down to the base case and back up for each path element to correctly construct the parent href. This is certainly not an O(n^3) algorithm, but it seems like a triangular number (http://mathworld.wolfram.com/TriangularNumber.html ). For very long paths, this creates more work than necessary. Even so, I'll take what works over what doesn't. :-)

- Quinn

Come to think of it, a smart implementation could avoid recursion altogether and opt for a linear algorithm. The whole point is that you need to know how many ../ to string together for each path component. Note that this is always equal to the number of slashes between a given component and the last component. Thus, counting the / in a path (with a judicious use of contains() and substring- after() at the very beginning, then decrementing for each path element should give the correct count. Inside the <a> tags, a simple loop up to the current count should create the correct number of ../ in the link. I'm going to give that a shot as soon as I get my toddler to bed. ;-)

- Quinn

And, after reading the documentation more thoroughly, I realized there aren't for loops on variables, since XSL is a functional language. :-) Must've had that confused with <xsl:for-each> on XML elements. Regardless, I think I came up with a more straightforward solution that minimizes "yo-yo" recursion. Just thought I'd share. You can also see it in action here (for now):

http://dysart.cs.byu.edu/svn/cs628/code/

Thanks for the help, hope this is of use to someone else.

- Quinn

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="*">
<xsl:with-param name="path">/path/to/resource</xsl:with-param>
</xsl:call-template>
</xsl:template>

<!-- Split path components at '/', add parent links for each component. -->
<xsl:param name="path"/>
<xsl:choose>
<xsl:when test="\$path = '/'">
&lt;root&gt;
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="printComponentOfPath">
<xsl:with-param name="path" select="\$path"/>
<xsl:with-param name="levels">
<xsl:call-template name="occurences">
<xsl:with-param name="needle" select="'/'"/>
<xsl:with-param name="haystack" select="\$path"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<!-- Print links for each component except the last, with " / " in between. -->
<xsl:template name="printComponentOfPath">
<xsl:param name="path"/>
<xsl:param name="levels" select="0"/>
<xsl:variable name="tail" select="substring-after(\$path,'/')"/>
<xsl:choose>
<!-- If there is no text after a slash, this is the last component. -->
<xsl:when test="not(\$tail)">
<xsl:text> / </xsl:text>
<xsl:value-of select="\$path"/>
</xsl:when>
<!-- Otherwise, print a link for this component an continue on. -->
<xsl:otherwise>
<xsl:variable name="parentPath">
<xsl:call-template name="parentPath">
<xsl:with-param name="levels" select="\$levels"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:text> / </xsl:text>
</xsl:when>
<xsl:otherwise>
<a href="{\$parentPath}">&lt;root&gt;</a> <!-- Leading "/" -->
</xsl:otherwise>
</xsl:choose>
<!-- Recursive call to print next path component. -->
<xsl:call-template name="printComponentOfPath">
<xsl:with-param name="path" select="\$tail"/>
<xsl:with-param name="levels" select="\$levels - 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<!-- Find the number of occurrences of needle in haystack. -->
<xsl:template name="occurences">
<xsl:param name="needle"/>
<xsl:param name="haystack"/>
<xsl:param name="found" select="0"/>
<!-- Find the next needle; return the count so far if none exists. -->
<xsl:choose>
<xsl:when test="contains(\$haystack,\$needle)">
<xsl:call-template name="occurences">
<xsl:with-param name="needle" select="\$needle"/>
<xsl:with-param name="haystack" select="substring-after(\$haystack, \$needle)"/>
<xsl:with-param name="found" select="\$found + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="\$found"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<!-- Create a parent path with N occurrences of "../" -->
<xsl:template name="parentPath">
<xsl:param name="levels" select="0"/>
<xsl:if test="\$levels > 0">
<xsl:call-template name="parentPath">
<xsl:with-param name="levels" select="\$levels - 1"/>
</xsl:call-template>
<xsl:text>../</xsl:text>
</xsl:if>
</xsl:template>

</xsl:stylesheet>