--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- merge_rdf.xslt - Merge Wikidata element properties from its RDF.
+ Copyright (C) 2020, 2021, 2022 Pierre Choffet
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of version 3 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<!-- LIMITATIONS:
+ - If WD already has P31, we don't use our value to prevent subclasses to be added
+-->
+<xsl:stylesheet version="1.0" exclude-result-prefixes=""
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:schema="http://schema.org/"
+ xmlns:wdef="https://purl.choffet.net/wdef"
+ xmlns:wdt="http://www.wikidata.org/prop/direct/"
+ xmlns:wikibase="http://wikiba.se/ontology#"
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="xml" encoding="utf-8" indent="yes" />
+ <xsl:strip-space elements="*" />
+
+ <xsl:param name="action" select='reduce' />
+ <xsl:param name="rdf-path" />
+
+ <xsl:variable name="wd-doc" select="document($rdf-path)" />
+ <xsl:key name="wd-description" match="rdf:RDF/rdf:Description" use="@rdf:about" />
+
+ <xsl:variable name="element-id">
+ <xsl:call-template name="substring-after-last">
+ <xsl:with-param name="string" select="$wd-doc/rdf:RDF/rdf:Description[1]/@rdf:about" />
+ <xsl:with-param name="delimiter" select="'/'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="wd-resource-prefix" select="'http://www.wikidata.org/entity/'" />
+ <xsl:variable name="wd-resource" select="$wd-doc/rdf:RDF/rdf:Description[@rdf:about = concat($wd-resource-prefix, $element-id)]" />
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="/">
+ <xsl:if test="$action != 'reduce'">
+ <xsl:message terminate="yes">"reduce" is the only available action for now.</xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <!-- Take action on WDEF label if wd has any -->
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:label">
+ <xsl:if test="../@wdef:id != $element-id or not($wd-resource/schema:name[@xml:lang = current()/@xml:lang])">
+ <xsl:copy-of select="." />
+ </xsl:if>
+ </xsl:template>
+
+ <!-- Take action on WDEF description if wd has any -->
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:description">
+ <xsl:if test="../@wdef:id != $element-id or not($wd-resource/schema:description[@xml:lang = current()/@xml:lang])">
+ <xsl:copy-of select="." />
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:property">
+ <xsl:choose>
+ <xsl:when test="../@wdef:id != $element-id">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="all-values-have-wd-equivalent">
+ <xsl:call-template name="all-values-have-wd-equivalent" />
+ </xsl:variable>
+ <xsl:if test="$all-values-have-wd-equivalent = 'no'">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:property/wdef:value">
+ <xsl:choose>
+ <xsl:when test="../../@wdef:id != $element-id">
+ <xsl:copy-of select="." />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="has-wd-equivalent">
+ <xsl:call-template name="value-has-wd-equivalent" />
+ </xsl:variable>
+ <xsl:if test="$has-wd-equivalent = 'no'">
+ <!-- <xsl:copy-of select="." /> -->
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:property/wdef:novalue">
+ <xsl:if test="../../@wdef:id != $element-id or not($wd-resource/rdf:type[@rdf:resource = concat('http://www.wikidata.org/prop/novalue/', ../@wdef:pid)])">
+ <xsl:copy-of select="." />
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="/wdef:knowledge/wdef:element/wdef:property/wdef:somevalue">
+ <xsl:message terminate="yes">Cannot deal with wdef:somevalue for now</xsl:message>
+ </xsl:template>
+
+ <xsl:template match="wdef:qualifier">
+ <xsl:variable name="has-wd-equivalent">
+ <xsl:call-template name="qualifier-has-wd-equivalent" />
+ </xsl:variable>
+
+ <xsl:if test="$has-wd-equivalent != 'yes'" >
+ <xsl:copy-of select="." />
+ </xsl:if>
+ </xsl:template>
+
+ <!-- To be called in a property context -->
+ <xsl:template name="all-values-have-wd-equivalent">
+ <xsl:variable name="all-outputs">
+ <xsl:for-each select="*">
+ <xsl:call-template name="value-has-wd-equivalent" />
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($all-outputs, 'no')">
+ <xsl:text>no</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>yes</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- To be called in a property context -->
+ <xsl:template name="any-value-has-wd-equivalent">
+ <xsl:variable name="all-outputs">
+ <xsl:for-each select="*">
+ <xsl:call-template name="value-has-wd-equivalent" />
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($all-outputs, 'yes')">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- To be called in a value context -->
+ <xsl:template name="value-has-wd-equivalent">
+ <xsl:variable name="PID" select="../@wdef:pid" />
+
+ <xsl:choose>
+ <xsl:when test="wdef:literal">
+ <xsl:choose>
+ <xsl:when test="$wd-resource/*[name(.) = concat('wdt:', $PID) and text() = current()/wdef:literal/text()]">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="wdef:ref-element">
+ <xsl:choose>
+ <!-- If WD already has P31, we take action on our value to prevent subclasses to be added -->
+ <xsl:when test="($PID = 'P31' and $wd-resource/*[name(.) = 'wdt:P31']) or ($wd-resource/*[name(.) = concat('wdt:', $PID) and @rdf:resource = concat($wd-resource-prefix, current()/wdef:ref-element)])">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="wdef:translation">
+ <xsl:choose>
+ <xsl:when test="$wd-resource/*[name(.) = concat('wdt:', $PID) and @xml:lang = current()/wdef:translation/@xml:lang and text() = current()/wdef:translation/text()]">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="wdef:quantity">
+ <xsl:variable name="wd-quantity-description" select="$wd-doc/rdf:RDF/rdf:Description[@rdf:about = $wd-doc/rdf:RDF/rdf:Description/*[name(.) = concat('psv:', $PID)]/@rdf:resource]" />
+
+ <xsl:choose>
+ <xsl:when test="not($wd-quantity-description)">
+ <xsl:text>no</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="quantity-wdef-format">
+ <xsl:if test="substring(wdef:quantity, 1, 1) != '-' and substring(wdef:quantity, 1, 1) != '+'">
+ <xsl:text>+</xsl:text>
+ </xsl:if>
+ <xsl:value-of select="wdef:quantity" />
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$wd-quantity-description/wikibase:quantityAmount = $quantity-wdef-format and $wd-quantity-description/wikibase:quantityUnit[@rdf:resource = concat($wd-resource-prefix, current()/wdef:quantity/@wdef:unit)]">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="wdef:time">
+ <!-- Generate wikidata date format -->
+ <xsl:variable name="wd-date-description" select="$wd-doc/rdf:RDF/rdf:Description[@rdf:about = $wd-doc/rdf:RDF/rdf:Description/*[name(.) = concat('psv:', $PID)]/@rdf:resource]" />
+
+ <xsl:choose>
+ <xsl:when test="not($wd-date-description)">
+ <xsl:text>no</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="date-wd-format">
+ <xsl:choose>
+ <xsl:when test="$wd-date-description/wikibase:timePrecision = 9">
+ <xsl:value-of select="substring($wd-date-description/wikibase:timeValue, 1, 4)" />
+ </xsl:when>
+ <xsl:when test="$wd-date-description/wikibase:timePrecision = 10">
+ <xsl:value-of select="substring($wd-date-description/wikibase:timeValue, 1, 7)" />
+ </xsl:when>
+ <xsl:when test="$wd-date-description/wikibase:timePrecision = 11">
+ <xsl:value-of select="substring($wd-date-description/wikibase:timeValue, 1, 10)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">Can only deal with precision between 9 and 11</xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="date-wdef-format">
+ <xsl:choose>
+ <xsl:when test="wdef:time/@wdef:precision = 9">
+ <xsl:value-of select="substring(wdef:time, 2, 4)" />
+ </xsl:when>
+ <xsl:when test="wdef:time/@wdef:precision = 10">
+ <xsl:value-of select="substring(wdef:time, 2, 7)" />
+ </xsl:when>
+ <xsl:when test="wdef:time/@wdef:precision = 11">
+ <xsl:value-of select="substring(wdef:time, 2, 10)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">Can only deal with precision between 9 and 11</xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="wd-time-at-least-precise">
+ <xsl:choose>
+ <xsl:when test="string-length($date-wd-format) >= string-length($date-wdef-format)">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="wd-time-compatible">
+ <xsl:choose>
+ <xsl:when test="$wd-time-at-least-precise = 'yes' and substring($date-wd-format, 1, string-length($date-wdef-format)) = $date-wdef-format">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:if test="not($wd-date-description/wikibase:timeCalendarModel[@rdf:resource = concat($wd-resource-prefix, 'Q1985727')])">
+ <xsl:message terminate="yes">Can only deal with gregorian calendar for now</xsl:message>
+ </xsl:if>
+
+ <xsl:choose>
+ <!-- Return true if wd is the same time, at least as precise as wdef -->
+ <xsl:when test="$wd-time-at-least-precise = 'yes' and $wd-time-compatible = 'yes'">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="$wd-time-at-least-precise = 'no' or $wd-time-compatible = 'no'">
+ <xsl:message terminate="yes">WD has time data but incompatible or less precise. We cannot deal with that for now.</xsl:message>
+ </xsl:if>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="wdef:qualifier">
+ <xsl:call-template name="all-qualifiers-have-wd-equivalent" />
+ </xsl:when>
+ <xsl:when test="count(*) > 1">
+ <xsl:text>no</xsl:text>
+ <xsl:message terminate="yes">cannot deal with more than one value for now</xsl:message>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="qualifier-has-wd-equivalent">
+ <!-- WARNING: Not extensively tested for now -->
+ <xsl:choose>
+ <xsl:when test="substring(../@wdef:id, 1, 1) = '?' or not($wd-doc/rdf:RDF/rdf:Description[@rdf:about = ../@wdef:id] and *[name(.) = concat('pq:', wdef:property/@wdef:pid) and @rdf:resource = concat($wd-resource-prefix, wdef:property/wdef:value/wdef:ref-element)])">
+ <xsl:text>no</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>yes</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="all-qualifiers-have-wd-equivalent">
+ <xsl:variable name="all-outputs">
+ <xsl:for-each select="wdef:qualifier">
+ <xsl:call-template name="qualifier-has-wd-equivalent" />
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($all-outputs, 'no')">
+ <xsl:text>no</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>yes</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="substring-after-last">
+ <xsl:param name="string" />
+ <xsl:param name="delimiter" />
+
+ <xsl:choose>
+ <xsl:when test="contains($string, $delimiter)">
+ <xsl:call-template name="substring-after-last">
+ <xsl:with-param name="string" select="substring-after($string, $delimiter)" />
+ <xsl:with-param name="delimiter" select="$delimiter" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$string" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet>