<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="http://www.w3.org/StyleSheets/base.css" type="text/css"?><?xml-stylesheet href="http://www.w3.org/2002/02/style-xsl.css" type="text/css"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" exclude-result-prefixes="html" xmlns:rel="http://www.w3.org/2004/12/makeRelativeUri" xmlns:str="http://www.w3.org/2001/10/str-util.xsl">

<xsl:import href="http://www.w3.org/2001/10/str-util.xsl" />

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <link rel="stylesheet" href="http://www.w3.org/StyleSheets/base"/>
    <title>Template to process relative URIs</title>
  </head>
  <body>
    <div class='head'><a href="/"><img src="/Icons/w3c_home" alt="W3C"/></a> | <a href="../../2001/10/xslt-toolbox">XSLT toolbox</a></div>
    <h1>Template to process relative URIs</h1>

    <p>This style sheet defines a template <code>rel:makeRelativeUri</code> that can be used to transform a given URI (parameter <code>from</code>) relative to a given point, and make it relative from a different point (parameter <code>to</code>), assuming that this point is given as a URI relative to the same initial point (@@@ make that simpler and the style sheet more complete by dealing with absolute URI too?).</p>
    
    <p>It outputs "nice" relative URIs, that is only going upwards (<code>../</code>) when needed, only using <code>./</code> when needed, etc.</p>

    <p>It comes with a Unit test, to allow easier debugging; the test is obtained by running the style sheet against any XML, and the output is given through <code>xsl:message</code>.</p>



    <p>See also another <a href="../../2000/07/uri43/uri.xsl">style sheet with a template to make absolute URIs</a>.</p>

    <p class="copyright">Copyright &#169; 1994-2004 <a href="http://www.w3.org/">World Wide Web Consortium</a>, (<a
href="http://www.csail.mit.edu/"><acronym title="Massachusetts Institute of
Technology">M.I.T.</acronym></a>, <a
href="http://www.ercim.org/"><acronym
title="European Research Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
href="http://www.keio.ac.jp/">Keio University</a>). All Rights
    Reserved. http://www.w3.org/Consortium/Legal/. W3C <a href="http://www.w3.org/Consortium/Legal/copyright-software">software licensing</a> rules apply.</p>
    <address><a href="http://www.w3.org/People/Dom/">Dominique Haza&#235;l-Massieux</a> - $Id: makeRelativeUri.xsl,v 1.5 2006/10/17 21:40:27 ijacobs Exp $</address>
    </body>
</html>


<!-- Tests run when in units test mode -->
<tests xmlns="http://www.w3.org/2004/12/makeRelativeUri">
  <test>
    <from>foo</from>
    <to>bar/baz/toto</to>
    <result>bar/baz/toto</result>
  </test>

  <test>
    <from>foo/bar/doh</from>
    <to>bar/baz/toto</to>
    <result>../../bar/baz/toto</result>
  </test>
  <test>
    <from>foo/bar/doh</from>
    <to>foo/</to>
    <result>../</result>
  </test>
  <test>
    <from>foo/bar/doh</from>
    <to>foo/bar/</to>
    <result>./</result>
  </test>
  <test>
    <from>foo/</from>
    <to>foo/bar/doh</to>
    <result>bar/doh</result>
  </test>
  <test>
    <from>foo/bar/doh</from>
    <to>foo</to>
    <result>../../foo</result>
  </test>
  <test>
    <from>foo/bar/doh</from>
    <to>foo/foo/bar</to>
    <result>../foo/bar</result>
  </test>
  <test>
    <from>foo/../doh/bar</from>
    <to>foo/bar</to>
    <result>../foo/bar</result>
  </test>
  <test>
    <from>foo/bar/../../doh/bar</from>
    <to>foo/bar</to>
    <result>../foo/bar</result>
  </test>

</tests>

<!-- For the Unit Test -->
<xsl:template match="/">
  <xsl:message>Starting</xsl:message>
  <xsl:for-each select="document('')/xsl:stylesheet/rel:tests/rel:test">
      <xsl:message>Running test # <xsl:value-of select="position()"/></xsl:message>
    <xsl:variable name="output">
      <xsl:call-template name="rel:makeRelativeUri">
        <xsl:with-param name="from" select="rel:from"/>
        <xsl:with-param name="to" select="rel:to"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="not($output=rel:result)">

        <xsl:message>-&gt; failed: got <xsl:value-of select="$output"/>, expected <xsl:value-of select="rel:result"/></xsl:message>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>success!</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

<!-- Actual template 
Don't break the API -->
<xsl:template name="rel:makeRelativeUri">
  <xsl:param name="from"/>
  <xsl:param name="to"/>
  <xsl:variable name="cleanFrom">
    <xsl:call-template name="cleanDotPath">
      <xsl:with-param name="path" select="$from"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="numberOfSlashInFrom" select="string-length($cleanFrom) - string-length(translate($cleanFrom,'/',''))"/>
  <xsl:choose>
    <xsl:when test="starts-with($cleanFrom,$to) and substring($to,string-length($to))='/' and not(contains(substring-after($cleanFrom,$to),'/'))">
      <xsl:text>./</xsl:text>
    </xsl:when>
    <xsl:when test="$numberOfSlashInFrom">
      <xsl:choose>
        <xsl:when test="substring-before($cleanFrom,'/')=substring-before($to,'/')">
          <xsl:call-template name="rel:makeRelativeUri">
            <xsl:with-param name="from" select="substring-after($cleanFrom,'/')"/>
            <xsl:with-param name="to" select="substring-after($to,'/')"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="addsDotDot">
            <xsl:with-param name="number" select="$numberOfSlashInFrom"/>
          </xsl:call-template>
          <xsl:value-of select="$to"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$to"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- API free -->
<xsl:template name="addsDotDot">
  <xsl:param name="number"/>
  <xsl:if test="$number &gt;= 1">
    <xsl:text>../</xsl:text>
    <xsl:call-template name="addsDotDot">
      <xsl:with-param name="number" select="$number - 1"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<!-- API free  -->
<xsl:template name="cleanDotPath">
  <xsl:param name="path"/>
  <xsl:choose>
    <xsl:when test="not(contains($path,'/../'))">
      <xsl:value-of select="$path"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="cleanedStart">
        <xsl:choose>
          <xsl:when test="contains(substring-before($path,'/../'),'/')">
            <xsl:call-template name="str:keep-before-last">
              <xsl:with-param name="string" select="substring-before($path,'/../')"/>
              <xsl:with-param name="delimiter" select="'/'"/>
            </xsl:call-template>
            <xsl:text>/</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text></xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:call-template name="cleanDotPath">
        <xsl:with-param name="path" select="concat($cleanedStart,substring-after($path,'/../'))"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
