<
xsl:stylesheet xmlns:xsl=
"http://www.w3.org/1999/XSL/Transform" xmlns:xs=
"http://www.w3.org/2001/XMLSchema" xmlns:dc=
"http://purl.org/dc/elements/1.1/" xmlns:f=
"http://www.daisy.org/ns/pipeline/internal-functions" xmlns:pf=
"http://www.daisy.org/ns/pipeline/functions" xmlns:opf=
"http://www.idpf.org/2007/opf" xmlns=
"http://www.idpf.org/2007/opf" version=
"2.0" xpath-default-namespace=
"http://www.idpf.org/2007/opf" exclude-result-prefixes=
"#all">
<
xsl:include href=
"epub3-vocab.xsl"/>
<
xsl:include href=
"http://www.daisy.org/pipeline/modules/common-utils/library.xsl"/>
<
xsl:param name=
"reserved-prefixes" required=
"yes"/>
<
xsl:param name=
"log-conflicts" required=
"yes"/>
<
xsl:param name=
"existing-id-outside-metadata" as=
"xs:string*" select=
"//@id[not(ancestor::metadata)]"/>
<
xsl:key name=
"unified-id" match=
"//meta[@id]|//dc:*[@id]" use=
"f:unified-id(@id)"/>
<
xsl:key name=
"unified-refines" match=
"//meta[@refines]" use=
"f:unified-id(@refines)"/>
<
xsl:template match=
"/*" priority=
"1">
<
xsl:variable name=
"prefix-attr" as=
"element(f:vocab)*" select=
"f:parse-prefix-decl(@prefix)"/>
<
xsl:variable name=
"implicit-prefixes" as=
"element(f:vocab)*" select=
"if ($reserved-prefixes='#default') then for $used in distinct-values( //meta/(@property|@scheme)[contains(.,':')]/substring-before(.,':')) return $f:default-prefixes[@prefix=$used] else f:parse-prefix-decl($reserved-prefixes)"/>
<
xsl:next-match>
<
xsl:with-param name=
"prefix-attr" tunnel=
"yes" select=
"$prefix-attr"/>
<
xsl:with-param name=
"implicit-prefixes" tunnel=
"yes" select=
"$implicit-prefixes"/>
<
xsl:with-param name=
"vocabs" tunnel=
"yes" select=
"($prefix-attr,$implicit-prefixes)"/>
</
xsl:next-match>
</
xsl:template>
<
xsl:template match=
"/*">
<
xsl:param name=
"prefix-attr" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:param name=
"implicit-prefixes" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
metadata>
<
xsl:namespace name=
"dc" select=
"'http://purl.org/dc/elements/1.1/'"/>
<
xsl:variable name=
"dcterms-vocab" as=
"element(f:vocab)" select=
"($implicit-prefixes[@uri=$f:default-prefixes[@prefix='dcterms']/@uri], $f:default-prefixes[@prefix='dcterms'])[1]"/>
<
xsl:choose>
<
xsl:when test=
"not($prefix-attr[@uri=$dcterms-vocab/@uri]) and not($implicit-prefixes[@uri=$dcterms-vocab/@uri])">
<
xsl:attribute name=
"prefix" select=
"string-join((@prefix, concat($dcterms-vocab/@prefix,': ',$dcterms-vocab/@uri)),' ')"/>
</
xsl:when>
<
xsl:otherwise>
<
xsl:sequence select=
"@prefix"/>
</
xsl:otherwise>
</
xsl:choose>
<
xsl:variable name=
"titles" select=
"//metadata/dc:title[not(@refines)]"/>
<
xsl:variable name=
"first-group" as=
"element()*" select=
"$titles intersect $titles[1]/ancestor::metadata/dc:title"/>
<
xsl:apply-templates select=
"$first-group"/>
<
xsl:if test=
"$log-conflicts='true'">
<
xsl:variable name=
"new-value" as=
"xs:string" select=
"f:serialize-value($first-group)"/>
<
xsl:for-each-group select=
"$titles except $first-group" group-by=
"count(ancestor::metadata/preceding::metadata)">
<
xsl:variable name=
"value" as=
"xs:string" select=
"f:serialize-value(current-group())"/>
<
xsl:if test=
"$value!=$new-value">
<
xsl:message>Updating '<
xsl:value-of select=
"name()"/>': replacing value <
xsl:value-of select=
"$value"/> with <
xsl:value-of select=
"$new-value"/>.</
xsl:message>
</
xsl:if>
</
xsl:for-each-group>
</
xsl:if>
<
xsl:variable name=
"identifiers" select=
"//metadata/dc:identifier[not(@refines)]"/>
<
xsl:variable name=
"first-group" as=
"element()*" select=
"$identifiers intersect $identifiers[1]/ancestor::metadata/dc:identifier"/>
<
xsl:apply-templates select=
"$first-group"/>
<
xsl:if test=
"$log-conflicts='true'">
<
xsl:variable name=
"new-value" as=
"xs:string" select=
"f:serialize-value($first-group)"/>
<
xsl:for-each-group select=
"$identifiers except $first-group" group-by=
"count(ancestor::metadata/preceding::metadata)">
<
xsl:variable name=
"value" as=
"xs:string" select=
"f:serialize-value(current-group())"/>
<
xsl:if test=
"$value!=$new-value">
<
xsl:message>Updating '<
xsl:value-of select=
"name()"/>': replacing value <
xsl:value-of select=
"$value"/> with <
xsl:value-of select=
"$new-value"/>.</
xsl:message>
</
xsl:if>
</
xsl:for-each-group>
</
xsl:if>
<
xsl:variable name=
"languages" select=
"//metadata/dc:language[not(@refines)]"/>
<
xsl:variable name=
"first-group" as=
"element()*" select=
"$languages intersect $languages[1]/ancestor::metadata/dc:language"/>
<
xsl:apply-templates select=
"$first-group"/>
<
xsl:if test=
"$log-conflicts='true'">
<
xsl:variable name=
"new-value" as=
"xs:string" select=
"f:serialize-value($first-group)"/>
<
xsl:for-each-group select=
"$languages except $first-group" group-by=
"count(ancestor::metadata/preceding::metadata)">
<
xsl:variable name=
"value" as=
"xs:string" select=
"f:serialize-value(current-group())"/>
<
xsl:if test=
"$value!=$new-value">
<
xsl:message>Updating '<
xsl:value-of select=
"name()"/>': replacing value <
xsl:value-of select=
"$value"/> with <
xsl:value-of select=
"$new-value"/>.</
xsl:message>
</
xsl:if>
</
xsl:for-each-group>
</
xsl:if>
<
meta property=
"dcterms:modified">
<
xsl:value-of select=
"format-dateTime( adjust-dateTime-to-timezone(current-dateTime(),xs:dayTimeDuration('PT0H')), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]Z')"/>
</
meta>
<
xsl:for-each-group select=
"//(dc:contributor|dc:coverage|dc:creator|dc:date|dc:description|dc:format |dc:publisher|dc:relation|dc:rights|dc:source|dc:subject|dc:type)[empty(@refines)]" group-by=
"name()">
<
xsl:variable name=
"first-group" as=
"element()*">
<
xsl:variable name=
"first-group" as=
"element()*" select=
"current-group()[ancestor::metadata is current()/ancestor::metadata]"/>
<
xsl:choose>
<
xsl:when test=
"self::dc:date|self::dc:source">
<
xsl:variable name=
"first" as=
"element()" select=
"$first-group[1]"/>
<
xsl:sequence select=
"$first"/>
<
xsl:for-each select=
"$first-group except $first">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding '<
xsl:value-of select=
"name()"/>' with value '<
xsl:value-of select=
"string(.)"/>': only one '<
xsl:value-of select=
"name()"/>' allowed.</
xsl:with-param>
</
xsl:call-template>
</
xsl:for-each>
</
xsl:when>
<
xsl:otherwise>
<
xsl:sequence select=
"$first-group"/>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:variable>
<
xsl:apply-templates select=
"$first-group"/>
<
xsl:if test=
"$log-conflicts='true'">
<
xsl:variable name=
"new-value" as=
"xs:string" select=
"f:serialize-value($first-group)"/>
<
xsl:for-each-group select=
"current-group() except $first-group" group-by=
"count(ancestor::metadata/preceding::metadata)">
<
xsl:variable name=
"value" as=
"xs:string" select=
"f:serialize-value(current-group())"/>
<
xsl:if test=
"$value!=$new-value">
<
xsl:message>Updating '<
xsl:value-of select=
"name()"/>': replacing value <
xsl:value-of select=
"$value"/> with <
xsl:value-of select=
"$new-value"/>.</
xsl:message>
</
xsl:if>
</
xsl:for-each-group>
</
xsl:if>
</
xsl:for-each-group>
<
xsl:for-each-group select=
"//meta[empty(@refines)]" group-by=
"f:expand-property(@property,$vocabs)/@uri">
<
xsl:choose>
<
xsl:when test=
"current-grouping-key()">
<
xsl:variable name=
"property" as=
"xs:string" select=
"current-grouping-key()"/>
<
xsl:variable name=
"first-group" as=
"element(opf:meta)*" select=
"current-group()[ancestor::metadata is current()/ancestor::metadata]"/>
<
xsl:variable name=
"first-group-processed" as=
"element(opf:meta)*">
<
xsl:apply-templates select=
"$first-group"/>
</
xsl:variable>
<
xsl:sequence select=
"$first-group-processed"/>
<
xsl:if test=
"$log-conflicts='true'">
<
xsl:variable name=
"discarded" as=
"element(opf:meta)*" select=
"current-group() except $first-group"/>
<
xsl:variable name=
"discarded" as=
"element(opf:meta)*">
<
xsl:for-each select=
"$discarded">
<
xsl:choose>
<
xsl:when test=
"$property='http://purl.org/dc/terms/modified'">
<
xsl:message>Discarding property '<
xsl:value-of select=
"@property"/>' with value '<
xsl:value-of select=
"."/>'. A new value is generated.</
xsl:message>
</
xsl:when>
<
xsl:when test=
"not(@property) and @name and @content">
</
xsl:when>
<
xsl:otherwise>
<
xsl:sequence select=
"."/>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:for-each>
</
xsl:variable>
<
xsl:if test=
"exists($discarded)">
<
xsl:choose>
<
xsl:when test=
"exists($first-group-processed)">
<
xsl:variable name=
"new-value" as=
"xs:string" select=
"f:serialize-value($first-group-processed[not(@refines)])"/>
<
xsl:for-each-group select=
"$discarded" group-by=
"count(ancestor::metadata/preceding::metadata)">
<
xsl:variable name=
"value" as=
"xs:string" select=
"f:serialize-value(current-group())"/>
<
xsl:if test=
"$value!=$new-value">
<
xsl:message>Updating property '<
xsl:value-of select=
"@property"/>': replacing value <
xsl:value-of select=
"$value"/> with <
xsl:value-of select=
"$new-value"/>.</
xsl:message>
</
xsl:if>
</
xsl:for-each-group>
</
xsl:when>
<
xsl:otherwise>
<
xsl:for-each select=
"$discarded">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding property '<
xsl:value-of select=
"@property"/>' with value '<
xsl:value-of select=
"string(.)"/>'.</
xsl:with-param>
</
xsl:call-template>
</
xsl:for-each>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:if>
</
xsl:if>
</
xsl:when>
<
xsl:when test=
"not(@property) and @name and @content">
</
xsl:when>
<
xsl:otherwise>
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding property '<
xsl:value-of select=
"@property"/>' from an undeclared vocab.</
xsl:with-param>
</
xsl:call-template>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:for-each-group>
<
xsl:for-each select=
"//metadata">
<
xsl:variable name=
"manifest" as=
"element(opf:manifest)?" select=
"parent::package/manifest"/>
<
xsl:apply-templates select=
"meta[replace(@refines,'^#','')=$manifest//item/@id]">
<
xsl:with-param name=
"copy-refines" tunnel=
"yes" select=
"true()"/>
</
xsl:apply-templates>
<
xsl:for-each select=
"meta[@refines][not(replace(@refines,'^#','')=$manifest//item/@id) and not(exists(key('unified-id',f:unified-id(@refines))))]">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding property '<
xsl:value-of select=
"@property"/>' with broken refines attribute '<
xsl:value-of select=
"@refines"/>'.</
xsl:with-param>
</
xsl:call-template>
</
xsl:for-each>
</
xsl:for-each>
<
xsl:apply-templates select=
"//link"/>
</
metadata>
</
xsl:template>
<
xsl:template match=
"dc:identifier[not(@id)]">
<
dc:identifier>
<
xsl:attribute name=
"id" select=
"f:unique-id(generate-id(),//@id)"/>
<
xsl:apply-templates select=
"node() | @* except @id"/>
</
dc:identifier>
</
xsl:template>
<
xsl:template match=
"dc:*">
<
xsl:next-match/>
<
xsl:apply-templates select=
"key('unified-refines',f:unified-id(@id))"/>
</
xsl:template>
<
xsl:template match=
"meta">
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:variable name=
"property" select=
"f:expand-property(@property,$vocabs)"/>
<
xsl:choose>
<
xsl:when test=
"$property/@uri='http://purl.org/dc/terms/modified'">
<
xsl:message>Discarding property '<
xsl:value-of select=
"@property"/>' with value '<
xsl:value-of select=
"."/>'. A new value is generated.</
xsl:message>
</
xsl:when>
<
xsl:when test=
"not(@property) and @name and @content">
</
xsl:when>
<
xsl:when test=
"not(normalize-space())">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding empty property '<
xsl:value-of select=
"@property"/>'.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:when test=
"$property/@uri=''">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding property '<
xsl:value-of select=
"@property"/>' from
an undeclared vocab.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:when test=
"$property/@prefix='' and $property/@name=('display-seq','meta-auth')">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">The deprecated property '<
xsl:value-of select=
"@property"/>' was found.</
xsl:with-param>
</
xsl:call-template>
<
xsl:next-match/>
</
xsl:when>
<
xsl:when test=
"$property/@prefix='' and not($property/@name=('alternate-script', 'display-seq', 'file-as', 'group-position', 'identifier-type', 'meta-auth', 'role', 'title-type', 'pageBreakSource'))">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding unknown property '<
xsl:value-of select=
"@property"/>'.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:otherwise>
<
xsl:next-match/>
</
xsl:otherwise>
</
xsl:choose>
<
xsl:apply-templates select=
"key('unified-refines',f:unified-id(@id))"/>
</
xsl:template>
<
xsl:template match=
"link">
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:variable name=
"rel" select=
"f:expand-property(@rel,$vocabs)"/>
<
xsl:choose>
<
xsl:when test=
"not(@href) or not(@rel)">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding link with no @href or @rel
attributes.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:when test=
"$rel/@prefix='' and $rel/@name=('marc21xml-record', 'mods-record','onix-record','xml-signature xmp-record')">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Found link with deprecated @rel value '<
xsl:value-of select=
"@rel"/>'. This value should be replaced by 'record' with a corresponding 'media-type' attribute.</
xsl:with-param>
</
xsl:call-template>
<
xsl:next-match/>
</
xsl:when>
<
xsl:when test=
"$rel/@prefix='' and not($rel/@name=('marc21xml-record', 'mods-record','onix-record','xml-signature xmp-record','record','acquire','alternate'))">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding link with unknown @rel value '<
xsl:value-of select=
"@rel"/>'.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:when test=
"$rel/@uri=''">
<
xsl:call-template name=
"pf:warn">
<
xsl:with-param name=
"msg">Discarding link with @rel value '<
xsl:value-of select=
"@property"/>' from an undeclared vocab.</
xsl:with-param>
</
xsl:call-template>
</
xsl:when>
<
xsl:otherwise>
<
xsl:next-match/>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:template>
<
xsl:template match=
"@property">
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:attribute name=
"property" select=
"f:expand-property(.,$vocabs)/@name"/>
</
xsl:template>
<
xsl:template match=
"@scheme">
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*" tunnel=
"yes" required=
"yes"/>
<
xsl:attribute name=
"scheme" select=
"f:expand-property(.,$vocabs)/@name"/>
</
xsl:template>
<
xsl:template match=
"@id">
<
xsl:attribute name=
"id" select=
"f:unified-id(.)"/>
</
xsl:template>
<
xsl:template match=
"@refines">
<
xsl:param name=
"copy-refines" tunnel=
"yes" as=
"xs:boolean?" select=
"false()"/>
<
xsl:choose>
<
xsl:when test=
"$copy-refines">
<
xsl:sequence select=
"."/>
</
xsl:when>
<
xsl:otherwise>
<
xsl:attribute name=
"refines" select=
"concat('#',f:unified-id(.))"/>
</
xsl:otherwise>
</
xsl:choose>
</
xsl:template>
<
xsl:template match=
"node() | @*">
<
xsl:copy>
<
xsl:apply-templates select=
"node() | @*"/>
</
xsl:copy>
</
xsl:template>
<
xsl:template match=
"/phony" xpath-default-namespace=
"">
<
xsl:next-match/>
</
xsl:template>
<
xsl:function name=
"f:unified-id" as=
"xs:string">
<
xsl:param name=
"id" as=
"attribute()?"/>
<
xsl:variable name=
"count" select=
"count($id/ancestor::metadata/preceding::metadata)" as=
"xs:integer"/>
<
xsl:sequence select=
"f:unique-id( concat( if (starts-with($id,'#')) then substring($id,2) else $id, if ($count) then $count+1 else ''), $existing-id-outside-metadata)"/>
</
xsl:function>
<
xsl:function name=
"f:unique-id" as=
"xs:string">
<
xsl:param name=
"id" as=
"xs:string"/>
<
xsl:param name=
"existing" as=
"xs:string*"/>
<
xsl:sequence select=
" if (not($id=$existing)) then $id else f:unique-id(concat('x',$id),$existing) "/>
</
xsl:function>
<
xsl:function name=
"f:expand-property" as=
"element(f:property)">
<
xsl:param name=
"property" as=
"attribute()?"/>
<
xsl:param name=
"vocabs" as=
"element(f:vocab)*"/>
<
xsl:variable name=
"prefix" select=
"substring-before($property,':')" as=
"xs:string"/>
<
xsl:variable name=
"reference" select=
"replace($property,'(.+:)','')" as=
"xs:string"/>
<
xsl:variable name=
"vocab" as=
"xs:string" select=
"($vocabs[@prefix=$prefix]/@uri, if ($prefix='' and $reference!='') then $vocab-package-uri else (), '' )[1]"/>
<
f:property prefix=
"{$prefix}" uri=
"{if ($vocab) then concat($vocab,$reference) else ''}" name=
"{string-join(($prefix,$reference)[not(.='')],':')}"/>
</
xsl:function>
<
xsl:function name=
"f:serialize-value" as=
"xs:string">
<
xsl:param name=
"elems" as=
"element()*"/>
<
xsl:sequence select=
"if (count($elems)=0) then '[]' else if (count($elems)=1) then concat('''',$elems/string(.),'''') else concat('[''',string-join($elems/string(.),''', '''),''']')"/>
</
xsl:function>
</
xsl:stylesheet>