<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:pf="http://www.daisy.org/ns/pipeline/functions" xmlns:f="http://www.daisy.org/ns/pipeline/internal-functions" xmlns:java="implemented-in-java" exclude-result-prefixes="#all" version="2.0">
<xsl:function name="pf:base-uri" as="xs:anyURI?">
<xsl:param name="arg" as="node()?"/>
<xsl:choose>
<xsl:when test="exists($arg/ancestor-or-self::*[@xml:base])">
<xsl:variable name="base" as="element()" select="$arg/ancestor-or-self::*[@xml:base][1]"/>
<xsl:sequence select="if (exists($base/parent::*)) then resolve-uri($base/@xml:base, pf:base-uri($base/parent::*)) else $base/@xml:base"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="pf:document-uri($arg)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="pf:document-uri" as="xs:anyURI?">
<xsl:param name="arg" as="node()?"/>
<xsl:sequence select="base-uri(pf:root($arg))"/>
</xsl:function>
<xsl:function name="pf:root" as="node()?">
<xsl:param name="arg" as="node()?"/>
<xsl:variable name="root" as="node()?" select="root($arg)"/>
<xsl:sequence select="collection()[. is $root][1]"/>
</xsl:function>
<xsl:function name="pf:resolve-uri" as="xs:anyURI?">
<xsl:param name="relative" as="xs:string?"/>
<xsl:param name="arg"/>
<xsl:if test="exists($arg)">
<xsl:sequence select="resolve-uri( $relative, if ($arg instance of node()) then pf:base-uri($arg) else $arg)"/>
</xsl:if>
</xsl:function>
<xsl:function name="pf:tokenize-uri" as="xs:string*">
<xsl:param name="uri" as="xs:string?"/>
<xsl:analyze-string select="concat('X',$uri)" regex="^X((jar:file|[^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?">
<xsl:matching-substring>
<xsl:sequence select="( if (regex-group(1)) then regex-group(2) else '', if (regex-group(3)) then regex-group(4) else '', regex-group(5), if (regex-group(6)) then regex-group(7) else '', if (regex-group(8)) then regex-group(9) else '' )"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
<xsl:function name="pf:recompose-uri" as="xs:string">
<xsl:param name="tokens" as="xs:string*"/>
<xsl:sequence select="string-join(( if($tokens[1]) then ($tokens[1],':') else (), if($tokens[2]) then ('//',$tokens[2]) else if($tokens[1] and not($tokens[1]=('mailto','file','jar:file'))) then '/' else (), $tokens[3], if($tokens[4]) then ('?',$tokens[4]) else (), if($tokens[5]) then ('#',$tokens[5]) else () ),'')"/>
</xsl:function>
<xsl:function name="pf:is-absolute" as="xs:boolean">
<xsl:param name="uri" as="xs:string?"/>
<xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))"/>
<xsl:sequence select="starts-with($tokens[3],'/')"/>
</xsl:function>
<xsl:function name="pf:is-relative" as="xs:boolean">
<xsl:param name="uri" as="xs:string?"/>
<xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))"/>
<xsl:sequence select="not($tokens[1]) and not(starts-with($tokens[3],'/'))"/>
</xsl:function>
<xsl:function name="pf:get-scheme" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:sequence select="pf:tokenize-uri(normalize-space($uri))[1]"/>
</xsl:function>
<xsl:function name="pf:get-path" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:sequence select="pf:tokenize-uri(normalize-space($uri))[3]"/>
</xsl:function>
<xsl:function name="pf:get-extension" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:sequence select="lower-case(replace(pf:get-path($uri),'^.*\.([^.]*)$','$1'))"/>
</xsl:function>
<xsl:function name="pf:replace-path" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:param name="path" as="xs:string?"/>
<xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))" as="xs:string*"/>
<xsl:sequence select="pf:recompose-uri(($tokens[1],$tokens[2],pf:normalize-path($path),$tokens[4],$tokens[5]))"/>
</xsl:function>
<doc xmlns="http://www.oxygenxml.com/ns/doc/xsl">
<desc>
<p>Normalize a URI.</p>
</desc>
<param name="uri">
<p>A relative or absolute URI</p>
</param>
<param name="fragment">
<p/>
</param>
<return>
<p>A relative or absolute URI</p>
</return>
</doc>
<java:function name="pf:normalize-uri" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
</java:function>
<java:function name="pf:normalize-uri" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:param name="fragment" as="xs:boolean"/>
</java:function>
<xsl:function name="pf:normalize-uri-legacy" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:sequence select="pf:normalize-uri($uri,true())"/>
</xsl:function>
<xsl:function name="pf:normalize-uri-legacy" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:param name="fragment" as="xs:boolean"/>
<xsl:variable name="uri">
<xsl:choose>
<xsl:when test="$uri">
<xsl:variable name="cp-base" select="string-to-codepoints('0A')" as="xs:integer+"/>
<xsl:analyze-string select="$uri" regex="(%[0-9A-F]{{2}})+" flags="i">
<xsl:matching-substring>
<xsl:variable name="upper-case" select="upper-case(.)"/>
<xsl:variable name="utf8-bytes" as="xs:integer+">
<xsl:analyze-string select="$upper-case" regex="%([0-9A-F]{{2}})" flags="i">
<xsl:matching-substring>
<xsl:variable name="nibble-pair" select=" for $nibble-char in string-to-codepoints(upper-case(regex-group(1))) return if ($nibble-char ge $cp-base[2]) then $nibble-char - $cp-base[2] + 10 else $nibble-char - $cp-base[1]" as="xs:integer+"/>
<xsl:sequence select="$nibble-pair[1] * 16 + $nibble-pair[2]"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:value-of select="if ($utf8-bytes = (65 to 90, 97 to 122, 48 to 57, 45, 46, 95, 126)) then codepoints-to-string(pf:utf8-decode($utf8-bytes)) else $upper-case"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$uri"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))" as="xs:string*"/>
<xsl:variable name="scheme" select="$tokens[1]"/>
<xsl:variable name="authority" select="$tokens[2]"/>
<xsl:variable name="path" select="$tokens[3]"/>
<xsl:variable name="query" select="$tokens[4]"/>
<xsl:variable name="fragment" select="if ($fragment) then $tokens[5] else ()"/>
<xsl:variable name="scheme" select="lower-case($scheme)"/>
<xsl:variable name="authority" select="lower-case($authority)"/>
<xsl:variable name="authority" select="if ($scheme='http' and ends-with($authority,':80')) then substring($authority,1,string-length($authority)-3) else $authority"/>
<xsl:variable name="path" select="pf:normalize-path($path)"/>
<xsl:variable name="uri" select="pf:recompose-uri(($scheme,$authority,$path,$query,$fragment))"/>
<xsl:variable name="uri" select="pf:file-expand83($uri)" use-when="function-available('pf:file-expand83')"/>
<xsl:sequence select="iri-to-uri($uri)"/>
</xsl:function>
<xsl:function name="pf:relativize-uri" as="xs:string">
<xsl:param name="uri" as="xs:string?"/>
<xsl:param name="base" as="xs:string?"/>
<xsl:variable name="uri-tokens" select="pf:tokenize-uri(pf:normalize-uri($uri))"/>
<xsl:variable name="base-tokens" select="pf:tokenize-uri(pf:normalize-uri($base))"/>
<xsl:choose>
<xsl:when test="(not($uri-tokens[1]) or $uri-tokens[1]=$base-tokens[1]) and (not($uri-tokens[2]) or $uri-tokens[2]=$base-tokens[2])">
<xsl:variable name="relative-path" select="pf:relativize-path($uri-tokens[3],$base-tokens[3])"/>
<xsl:choose>
<xsl:when test="$relative-path=replace($base-tokens[3],'^.*/([^/]+)$','$1') and $uri-tokens[4]='' and $uri-tokens[5]!=''">
<xsl:sequence select="concat('#',$uri-tokens[5])"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="pf:recompose-uri(('','',$relative-path,$uri-tokens[4],$uri-tokens[5]))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="pf:recompose-uri($uri-tokens)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="pf:normalize-path" as="xs:string?">
<xsl:param name="path" as="xs:string?"/>
<xsl:variable name="normalized" as="xs:string?" select=" replace( replace( replace( replace( replace($path,'^(\.(/|$))+','') ,'/(\.(/|$))+','/') ,'/+','/') ,'(^|/)\.\.$','$1../') ,'^/(\.\./)+','/') "/>
<xsl:sequence select=" if (matches($normalized,'([^/\.]|\.[^/\.]|[^/\.]\.|[^/]{3,})/\.\./')) then pf:normalize-path(replace($normalized,'([^/\.]|\.[^/\.]|[^/\.]\.|[^/]{3,})/\.\./','')) else $normalized"/>
</xsl:function>
<xsl:function name="pf:relativize-path" as="xs:string">
<xsl:param name="path" as="xs:string?"/>
<xsl:param name="base" as="xs:string?"/>
<xsl:choose>
<xsl:when test="starts-with($path,'/')">
<xsl:variable name="path-segments" select="tokenize($path, '/')"/>
<xsl:variable name="base-segments" select="tokenize($base, '/')[position()!=last()]"/>
<xsl:variable name="common-prefix-length" select=" (for $i in 1 to min((count($base-segments),count($path-segments) -1)) return if($base-segments[$i] eq $path-segments[$i]) then () else $i -1 ,min((count($base-segments),count($path-segments) -1)))[1]"/>
<xsl:variable name="upSteps" select="count($base-segments) -$common-prefix-length"/>
<xsl:sequence select="string-join(( for $i in 1 to $upSteps return '..', for $i in 1 to count($path-segments) - $common-prefix-length return $path-segments[$common-prefix-length + $i] ),'/')"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$path"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="pf:longest-common-uri" as="xs:string">
<xsl:param name="uris" as="xs:string*"/>
<xsl:choose>
<xsl:when test="count($uris)=1">
<xsl:value-of select="$uris"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uris[1]))" as="xs:string*"/>
<xsl:variable name="uri-filter" select="if (not($tokens[1])) then '' else if ($tokens[2]) then concat($tokens[1],'://',$tokens[2]) else $tokens[1]"/>
<xsl:variable name="uris" select="$uris[starts-with(.,$uri-filter)]"/>
<xsl:variable name="main-uri" select="for $part in tokenize(replace(pf:normalize-uri(replace($uris[1],'[#\?].*$','')),'/+','SLASH|/'),'/') return replace($part,'SLASH\|$','/')"/>
<xsl:variable name="count-common" as="xs:integer*">
<xsl:for-each select="$uris[position() > 1]">
<xsl:variable name="compare-uri" select="for $part in tokenize(replace(pf:normalize-uri(.),'/+','SLASH|/'),'/') return replace($part,'SLASH\|$','/')"/>
<xsl:sequence select="min(for $i in 1 to count($main-uri) return if ($main-uri[$i]=$compare-uri[$i]) then () else $i)"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="count-common" select="min(($count-common, count($main-uri)))"/>
<xsl:variable name="longest-common" select="$main-uri[position() < $count-common]"/>
<xsl:variable name="longest-common" select="concat($longest-common[1],if (matches($longest-common[1],'^\w+:/$') and (not($tokens[1]='file') or $tokens[2])) then '/' else '',string-join($longest-common[position()>1],''))"/>
<xsl:variable name="longest-common" select="replace($longest-common, '[^/]+$', '')"/>
<xsl:value-of select="$longest-common"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="pf:unescape-uri" as="xs:string">
<xsl:param name="string" as="xs:string?"/>
<xsl:variable name="cp-base" select="string-to-codepoints('0A')" as="xs:integer+"/>
<xsl:variable name="result">
<xsl:analyze-string select="$string" regex="(%[0-9A-F]{{2}})+" flags="i">
<xsl:matching-substring>
<xsl:variable name="utf8-bytes" as="xs:integer+">
<xsl:analyze-string select="." regex="%([0-9A-F]{{2}})" flags="i">
<xsl:matching-substring>
<xsl:variable name="nibble-pair" select=" for $nibble-char in string-to-codepoints( upper-case(regex-group(1))) return if ($nibble-char ge $cp-base[2]) then $nibble-char - $cp-base[2] + 10 else $nibble-char - $cp-base[1]" as="xs:integer+"/>
<xsl:sequence select="$nibble-pair[1] * 16 + $nibble-pair[2]"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:value-of select="codepoints-to-string( pf:utf8-decode( $utf8-bytes))"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:sequence select="string($result)"/>
</xsl:function>
<xsl:function name="pf:utf8-decode" as="xs:integer*">
<xsl:param name="bytes" as="xs:integer*"/>
<xsl:choose>
<xsl:when test="empty($bytes)"/>
<xsl:when test="$bytes[1] eq 0">
<xsl:sequence select="pf:utf8-decode( remove( $bytes, 1))"/>
</xsl:when>
<xsl:when test="$bytes[1] le 127">
<xsl:sequence select="$bytes[1], pf:utf8-decode( remove( $bytes, 1))"/>
</xsl:when>
<xsl:when test="$bytes[1] lt 224">
<xsl:sequence select=" ((($bytes[1] - 192) * 64) + ($bytes[2] - 128) ), pf:utf8-decode( remove( remove( $bytes, 1), 1))"/>
</xsl:when>
<xsl:when test="$bytes[1] lt 240">
<xsl:sequence select=" ((($bytes[1] - 224) * 4096) + (($bytes[2] - 128) * 64) + ($bytes[3] - 128) ), pf:utf8-decode( remove( remove( remove( $bytes, 1), 1), 1))"/>
</xsl:when>
<xsl:when test="$bytes[1] lt 248">
<xsl:sequence select=" ((($bytes[1] - 224) * 262144) + (($bytes[2] - 128) * 4096) + (($bytes[3] - 128) * 64) + ($bytes[4] - 128) ), pf:utf8-decode( $bytes[position() gt 4])"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="pf:utf8-decode( remove( $bytes, 1))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>