<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:kiln="http://www.kcl.ac.uk/artshums/depts/ddh/kiln/ns/1.0"
                xmlns:map="http://apache.org/cocoon/sitemap/1.0"
                xmlns:xi="http://www.w3.org/2001/XInclude"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">

  <!-- Generate a single XML document containing a sitemap and all of
       the sitemaps mounted (recursively) from it, in a nested
       hierarchy. All references are made absolute (for cocoon: URLs)
       or root relative.

       Each mounted sitemaps are included as the child of the map:mount
       element that references it.

       Also supports mount tables. -->

  <xsl:param name="dir"/>
  <xsl:param name="file"/>
  <xsl:param name="uri_prefix"/>

  <xsl:template match="map:generate | map:part | map:read | map:transform">
      <xsl:copy>
         <xsl:call-template name="add-expanded-src">
            <xsl:with-param name="src" select="@src"/>
         </xsl:call-template>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="map:match[@type='mount-table']">
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xi:include>
            <xsl:attribute xmlns:dummy="http://www.example.org/dummy-ns" name="href">
               <xsl:text>cocoon://_internal/introspection/mount-table/</xsl:text>
               <xsl:value-of select="$dir"/>
               <xsl:value-of select="@pattern"/>
            </xsl:attribute>
         </xi:include>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="map:match">
      <xsl:copy>
         <xsl:attribute name="kiln:pattern">
            <xsl:value-of select="$uri_prefix"/>
            <xsl:value-of select="@pattern"/>
         </xsl:attribute>
         <xsl:call-template name="add-regexp-pattern">
            <xsl:with-param name="pattern" select="concat($uri_prefix, @pattern)"/>
         </xsl:call-template>
         <xsl:call-template name="add-pattern-groups">
            <xsl:with-param name="pattern" select="@pattern"/>
         </xsl:call-template>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="map:mount">
      <xsl:copy>
         <xsl:call-template name="add-expanded-src">
            <xsl:with-param name="src" select="@src"/>
         </xsl:call-template>
         <xsl:apply-templates select="@*|node()"/>
         <xi:include>
            <xsl:attribute xmlns:dummy="http://www.example.org/dummy-ns" name="href">
               <xsl:text>cocoon://_internal/introspection/pipeline/</xsl:text>
               <xsl:value-of select="$dir"/>
               <xsl:value-of select="@src"/>
               <xsl:text>/</xsl:text>
               <xsl:value-of select="$uri_prefix"/>
               <xsl:value-of select="@uri-prefix"/>
            </xsl:attribute>
         </xi:include>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="map:sitemap">
      <xsl:copy>
         <xsl:attribute name="kiln:file">
            <xsl:call-template name="reduce-path">
               <xsl:with-param name="path" select="concat($dir, $file)"/>
            </xsl:call-template>
         </xsl:attribute>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template name="add-expanded-src">
      <xsl:param name="src"/>
      <xsl:if test="normalize-space(@src)">
         <xsl:attribute name="kiln:src">
            <xsl:choose>
               <xsl:when test="starts-with($src, 'cocoon://')">
                  <xsl:value-of select="$src"/>
               </xsl:when>
               <xsl:when test="starts-with($src, 'cocoon:/')">
                  <xsl:text>cocoon://</xsl:text>
                  <xsl:call-template name="reduce-path">
                     <xsl:with-param name="path" select="concat($uri_prefix, substring-after($src, 'cocoon:/'))"/>
                  </xsl:call-template>
               </xsl:when>
               <xsl:when test="starts-with($src, 'empty:')">
                  <xsl:value-of select="$src"/>
               </xsl:when>
               <xsl:otherwise>
                  <xsl:call-template name="reduce-path">
                     <xsl:with-param name="path" select="concat($dir, $src)"/>
                  </xsl:call-template>
               </xsl:otherwise>
            </xsl:choose>
         </xsl:attribute>
      </xsl:if>
  </xsl:template>

  <xsl:template name="add-pattern-groups">
    <!-- Add numbered attributes for each wildcard in $pattern. -->
    <xsl:param name="pattern"/>
      <!-- In order to avoid having an empty first group, that will
         break the count from position(), ensure that all text before
         the first wildcard is removed. -->
    <xsl:variable name="stripped-pattern">
         <xsl:value-of select="substring($pattern,                             string-length(substring-before($pattern,                             '*')) + 1)"/>
      </xsl:variable>
      <xsl:for-each select="tokenize($stripped-pattern, '[^*]+')">
         <xsl:if test="normalize-space(.)">
            <xsl:attribute name="kiln:g{position()}">
               <xsl:choose>
                  <xsl:when test=". = '**'">
                     <xsl:text>FOO/BAR</xsl:text>
                  </xsl:when>
                  <xsl:otherwise>
                     <xsl:text>BAZ</xsl:text>
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:attribute>
         </xsl:if>
      </xsl:for-each>
  </xsl:template>

  <xsl:template name="add-regexp-pattern">
      <xsl:param name="pattern"/>
      <!-- Escape regular expression characters that should not be
         treated as such (anything except *). -->
    <xsl:variable name="escaped"
                    select="replace($pattern,                                          '[\-\|\^{}()?+.$\\\[\]]', '\\$0')"/>
      <xsl:attribute name="kiln:regexp">
         <xsl:text>^</xsl:text>
         <xsl:value-of select="replace(replace($escaped, '\*\*', '(.+)'), '\*', '([^/]+)')"/>
         <xsl:text>$</xsl:text>
      </xsl:attribute>
  </xsl:template>

  <xsl:template name="reduce-path">
    <!-- Reduce a root relative path containing "../" into a short
         form that does not contain such. -->
    <xsl:param name="path"/>
      <xsl:variable name="elements" select="tokenize($path, '/')"/>
      <xsl:variable name="indices" select="index-of($elements, '..')"/>
      <xsl:choose>
         <xsl:when test="exists($indices)">
            <xsl:variable name="reduced-elements"
                          select="remove(remove($elements, $indices[1]-1), $indices[1]-1)"/>
            <xsl:call-template name="reduce-path">
               <xsl:with-param name="path" select="string-join($reduced-elements, '/')"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:value-of select="$path"/>
         </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

  <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>