<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:d="http://www.daisy.org/ns/pipeline/data" xmlns:map="http://www.w3.org/2005/xpath-functions/map" version="3.0" exclude-result-prefixes="#all"> <xsl:import href="http://www.daisy.org/pipeline/modules/common-utils/library.xsl"/> <xsl:import href="http://www.daisy.org/pipeline/modules/file-utils/library.xsl"/> <xsl:param name="limit-scope" as="xs:boolean" select="false()"/> <xsl:template match="/d:fileset"> <xsl:variable name="a" as="element(d:fileset)" select="."/> <xsl:variable name="b" as="element(d:fileset)" select="collection()[2]/*"/> <xsl:variable name="base" as="xs:string?" select="($b,$a)[@xml:base][1]/pf:normalize-uri(base-uri(.))"/> <xsl:copy> <xsl:if test="exists($base)"> <xsl:attribute name="xml:base" select="$base"/> </xsl:if> <xsl:sequence select="@* except @xml:base"/> <xsl:variable name="files-and-anchors" as="map(*)*"> <xsl:variable name="a" as="map(*)*"> <xsl:apply-templates mode="parse" select="$a"/> </xsl:variable> <xsl:variable name="b" as="map(*)*"> <xsl:apply-templates mode="parse" select="$b"/> </xsl:variable> <xsl:variable name="a-files" as="map(*)*" select="$a[not(map:contains(.,'id'))]"/> <xsl:variable name="a-anchors" as="map(*)*" select="$a[map:contains(.,'id')]"/> <xsl:variable name="b-files" as="map(*)*" select="$b[not(map:contains(.,'id'))]"/> <xsl:variable name="b-anchors" as="map(*)*" select="$b[map:contains(.,'id')]"/> <xsl:for-each select="$a-files"> <xsl:variable name="a-file" as="map(*)" select="."/> <xsl:variable name="b-file" as="map(*)*" select="$b-files[.('original-href')=$a-file('href')]"/> <xsl:choose> <xsl:when test="exists($b-file)"> <xsl:for-each select="$b-file"> <xsl:variable name="b-file" as="map(*)" select="."/> <xsl:map> <xsl:map-entry key="'href'" select="$b-file('href')"/> <xsl:map-entry key="'original-href'" select="$a-file('original-href')"/> </xsl:map> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:sequence select="$a-file"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:variable name="b-anchors-grouped-by-original-href-id" as="map(xs:string,map(xs:string,map(*)*))"> <xsl:map> <xsl:for-each-group select="$b-anchors" group-by=".('original-href')"> <xsl:map-entry key="current-grouping-key()"> <xsl:map> <xsl:for-each-group select="current-group()" group-by=".('original-id')"> <xsl:map-entry key="current-grouping-key()" select="current-group()"/> </xsl:for-each-group> </xsl:map> </xsl:map-entry> </xsl:for-each-group> </xsl:map> </xsl:variable> <xsl:for-each select="$a-anchors"> <xsl:variable name="a-anchor" as="map(*)" select="."/> <xsl:variable name="b-anchor" as="map(*)*" select="for $m in $b-anchors-grouped-by-original-href-id($a-anchor('href')) return $m($a-anchor('id'))"/> <xsl:choose> <xsl:when test="exists($b-anchor)"> <xsl:if test="count($b-anchor) > 1"> <xsl:call-template name="pf:warn"> <xsl:with-param name="msg" select="'Ambiguous mapping for anchor.'"/> </xsl:call-template> </xsl:if> <xsl:variable name="b-anchor" as="map(*)" select="$b-anchor[1]"/> <xsl:map> <xsl:map-entry key="'href'" select="$b-anchor('href')"/> <xsl:map-entry key="'id'" select="$b-anchor('id')"/> <xsl:map-entry key="'original-href'" select="$a-anchor('original-href')"/> <xsl:map-entry key="'original-id'" select="$a-anchor('original-id')"/> </xsl:map> </xsl:when> <xsl:otherwise> <xsl:variable name="b-file" as="map(*)*" select="$b-files[.('original-href')=$a-anchor('href')]"/> <xsl:choose> <xsl:when test="exists($b-file)"> <xsl:variable name="b-file" as="map(*)" select="$b-file[1]"/> <xsl:map> <xsl:map-entry key="'href'" select="$b-file('href')"/> <xsl:map-entry key="'id'" select="$a-anchor('id')"/> <xsl:map-entry key="'original-href'" select="$a-anchor('original-href')"/> <xsl:map-entry key="'original-id'" select="$a-anchor('original-id')"/> </xsl:map> </xsl:when> <xsl:otherwise> <xsl:sequence select="$a-anchor"/> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:if test="not($limit-scope)"> <xsl:for-each select="$b-files"> <xsl:variable name="b-file" as="map(*)" select="."/> <xsl:choose> <xsl:when test="some $a-file in $a-files satisfies $a-file('href')=$b-file('original-href')"> </xsl:when> <xsl:otherwise> <xsl:sequence select="$b-file"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:variable name="a-anchors-grouped-by-href-id" as="map(xs:string,map(xs:string,map(*)*))"> <xsl:map> <xsl:for-each-group select="$a-anchors" group-by=".('href')"> <xsl:map-entry key="current-grouping-key()"> <xsl:map> <xsl:for-each-group select="current-group()" group-by=".('id')"> <xsl:map-entry key="current-grouping-key()" select="current-group()"/> </xsl:for-each-group> </xsl:map> </xsl:map-entry> </xsl:for-each-group> </xsl:map> </xsl:variable> <xsl:for-each select="$b-anchors"> <xsl:variable name="b-anchor" as="map(*)" select="."/> <xsl:choose> <xsl:when test="some $m in $a-anchors-grouped-by-href-id($b-anchor('original-href')) satisfies exists($m($b-anchor('original-id')))"> </xsl:when> <xsl:otherwise> <xsl:variable name="a-file" as="map(*)*" select="$a-files[.('href')=$b-anchor('original-href')]"/> <xsl:choose> <xsl:when test="exists($a-file)"> <xsl:variable name="a-file" as="map(*)" select="$a-file[1]"/> <xsl:map> <xsl:map-entry key="'href'" select="$b-anchor('href')"/> <xsl:map-entry key="'id'" select="$b-anchor('id')"/> <xsl:map-entry key="'original-href'" select="$a-file('original-href')"/> <xsl:map-entry key="'original-id'" select="$b-anchor('original-id')"/> </xsl:map> </xsl:when> <xsl:otherwise> <xsl:sequence select="$b-anchor"/> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:if> </xsl:variable> <xsl:variable name="files-and-anchors" as="element()*"> <xsl:for-each select="$files-and-anchors"> <xsl:variable name="attributes" as="attribute()*"> <xsl:variable name="map" as="map(*)*" select="."/> <xsl:for-each select="map:keys($map)"> <xsl:attribute name="{.}" select="$map(.)"/> </xsl:for-each> </xsl:variable> <xsl:choose> <xsl:when test="$attributes[name()='id']"> <d:anchor> <xsl:sequence select="$attributes"/> </d:anchor> </xsl:when> <xsl:otherwise> <d:file> <xsl:sequence select="$attributes"/> </d:file> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:variable> <xsl:variable name="files-with-anchors" as="element(d:file)*"> <xsl:for-each-group select="$files-and-anchors" group-by="@original-href"> <xsl:for-each-group select="current-group()" group-by="@href"> <d:file> <xsl:attribute name="href" select="if (exists($base)) then pf:relativize-uri(@href,$base) else @href"/> <xsl:if test="not(@original-href=@href)"> <xsl:sequence select="@original-href"/> </xsl:if> <xsl:for-each select="current-group()[self::d:anchor]"> <xsl:copy> <xsl:sequence select="@id"/> <xsl:if test="not(@original-id=@id)"> <xsl:sequence select="@original-id"/> </xsl:if> </xsl:copy> </xsl:for-each> </d:file> </xsl:for-each-group> </xsl:for-each-group> </xsl:variable> <xsl:sequence select="$files-with-anchors"/> </xsl:copy> </xsl:template> <xsl:template mode="parse" match="/d:fileset"> <xsl:apply-templates mode="#current" select="*"> <xsl:with-param name="base" tunnel="yes" select="base-uri(.)"/> </xsl:apply-templates> </xsl:template> <xsl:template mode="parse" match="/d:fileset/d:file[@href]"> <xsl:variable name="normalized-file" as="map(*)"> <xsl:map> <xsl:apply-templates mode="#current" select="@*"/> </xsl:map> </xsl:variable> <xsl:variable name="normalized-file" as="map(*)" select="if (@original-href) then $normalized-file else map:put($normalized-file,'original-href',$normalized-file('href'))"/> <xsl:sequence select="$normalized-file"/> <xsl:apply-templates mode="#current"> <xsl:with-param name="normalized-parent" select="$normalized-file"/> </xsl:apply-templates> </xsl:template> <xsl:template mode="parse" match="d:file/@href| d:file/@original-href"> <xsl:param name="base" tunnel="yes" required="yes"/> <xsl:map-entry key="name()" select="pf:normalize-uri(resolve-uri(.,$base))"/> </xsl:template> <xsl:template mode="parse" match="/d:fileset/d:file/d:anchor[@id]"> <xsl:param name="normalized-parent" as="map(*)" required="yes"/> <xsl:map> <xsl:map-entry key="'href'" select="$normalized-parent('href')"/> <xsl:map-entry key="'original-href'" select="$normalized-parent('original-href')"/> <xsl:map-entry key="'id'" select="string(@id)"/> <xsl:map-entry key="'original-id'" select="if (@original-id) then string(@original-id) else string(@id)"/> </xsl:map> </xsl:template> <xsl:template mode="parse" match="@*|node()" priority="0.4"/> </xsl:stylesheet>