Skip to content

Commit 3552e5f

Browse files
milanmajchrakclaude
andcommitted
Fix source repository IRI and xml:lang in the CCMM crosswalk
- original_repository/iri named the item itself on every one of the 2628 records: it was derived with substring-before(uri, '/handle/'), which does not match LINDAT's hdl.handle.net URIs, so the otherwise branch emitted the whole item URI. XOAI already carries repository/@url; use it. - xml:lang was hardcoded "en" on subjects, alternate titles, descriptions and the rights wording. XOAI nests every value inside an element named after its language qualifier, so take the tag from there and fall back to "en" only for the unqualified "none" wrapper. Both covered by new tests. Re-checked over all 2628 live LINDAT records: still 2628/2628 valid against CCMM 1.1.0 in XSD 1.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 6d549e0 commit 3552e5f

3 files changed

Lines changed: 55 additions & 8 deletions

File tree

dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/CcmmXslTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,25 @@ public void ccmmAccessRightsFallBackToRestricted() throws Exception {
233233
equalTo("http://purl.org/coar/access_right/c_16ec"))));
234234
}
235235

236+
@Test
237+
public void ccmmOriginalRepositoryIsTheRepositoryUrl() throws Exception {
238+
// XOAI carries repository/@url; deriving it from the item URI names the item itself
239+
String result = apply("ccmm.xsl").to(resource("xoai-ccmm-test.xml"));
240+
assertThat(result, is(ccmm().withXPath(
241+
"//ccmm:metadata_identification/ccmm:original_repository/ccmm:iri",
242+
equalTo("https://lindat.mff.cuni.cz/repository/"))));
243+
}
244+
245+
@Test
246+
public void ccmmXmlLangFollowsTheXoaiLanguageWrapper() throws Exception {
247+
// a value stored under <element name="cs_CZ"> must not be tagged as English
248+
String result = apply("ccmm.xsl").to(resource("xoai-ccmm-test.xml"));
249+
assertThat(result, is(ccmm().withXPath(
250+
"//ccmm:subject/ccmm:title[.='korpus']/@xml:lang", equalTo("cs"))));
251+
assertThat(result, is(ccmm().withXPath(
252+
"//ccmm:subject/ccmm:title[.='linguistics']/@xml:lang", equalTo("en"))));
253+
}
254+
236255
// ---- CLARIN approximate dates (dc.date.issued = "0000") ----
237256

238257
@Test

dspace-oai/src/test/resources/xoai-ccmm-test.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
<field name="value">Czech language</field>
7979
<field name="value">NLP</field>
8080
</element>
81+
<element name="cs_CZ">
82+
<field name="value">korpus</field>
83+
</element>
8184
</element>
8285
<element name="title">
8386
<element name="none">
@@ -106,6 +109,7 @@
106109
</element>
107110
</element>
108111
<element name="repository">
112+
<field name="url">https://lindat.mff.cuni.cz/repository/</field>
109113
<field name="name">LINDAT/CLARIAH-CZ</field>
110114
<field name="mail">lindat-help@ufal.mff.cuni.cz</field>
111115
</element>

dspace/config/crosswalks/oai/metadataFormats/ccmm.xsl

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@
170170
<ccmm:original_repository>
171171
<ccmm:iri>
172172
<xsl:choose>
173+
<!--
174+
XOAI carries the repository's own base URL; use it. Deriving it from
175+
the item URI only works for /handle/ style URLs and otherwise names the
176+
item itself as the repository.
177+
-->
178+
<xsl:when test="doc:metadata/doc:element[@name='repository']/doc:field[@name='url']">
179+
<xsl:value-of select="normalize-space((doc:metadata/doc:element[@name='repository']/doc:field[@name='url'])[1])"/>
180+
</xsl:when>
173181
<xsl:when test="doc:metadata/doc:element[@name='dc']/doc:element[@name='identifier']/doc:element[@name='uri']/doc:element/doc:field[@name='value']">
174182
<xsl:variable name="uri" select="(doc:metadata/doc:element[@name='dc']/doc:element[@name='identifier']/doc:element[@name='uri']/doc:element/doc:field[@name='value'])[1]"/>
175183
<!--
@@ -288,9 +296,7 @@
288296
<!-- dc.title.alternative mapped to alternate_title -->
289297
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='title']/doc:element[@name='alternative']/doc:element/doc:field[@name='value']">
290298
<ccmm:alternate_title>
291-
<ccmm:title xml:lang="en">
292-
<xsl:value-of select="."/>
293-
</ccmm:title>
299+
<ccmm:title><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:title>
294300
</ccmm:alternate_title>
295301
</xsl:for-each>
296302
</xsl:template>
@@ -385,6 +391,24 @@
385391
<!-- ============================================================ -->
386392
<!-- time_reference (required, unbounded) -->
387393
<!-- ============================================================ -->
394+
<!--
395+
xml:lang for a text value, taken from the XOAI language wrapper that encloses it.
396+
XOAI nests every metadata value inside an element named after its language qualifier
397+
("en_US", "cs_CZ", ... or "none" when the field has no language). Called with the
398+
doc:field as the context node.
399+
-->
400+
<xsl:template name="XmlLangAttribute">
401+
<xsl:variable name="wrapper" select="string(../@name)"/>
402+
<xsl:attribute name="xml:lang">
403+
<xsl:choose>
404+
<xsl:when test="matches($wrapper, '^[a-z]{2,3}(_[A-Za-z]{2,4})?$')">
405+
<xsl:value-of select="substring-before(concat($wrapper, '_'), '_')"/>
406+
</xsl:when>
407+
<xsl:otherwise>en</xsl:otherwise>
408+
</xsl:choose>
409+
</xsl:attribute>
410+
</xsl:template>
411+
388412
<!--
389413
The temporal representation shared by the Issued and Created time references.
390414
An approximate date wins over dc.date.issued; a range becomes a time_interval,
@@ -665,7 +689,7 @@
665689
</xsl:choose>
666690
</ccmm:license>
667691
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='rights']/doc:element/doc:field[@name='value']">
668-
<ccmm:description xml:lang="en"><xsl:value-of select="."/></ccmm:description>
692+
<ccmm:description><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:description>
669693
</xsl:for-each>
670694
</ccmm:terms_of_use>
671695
</xsl:template>
@@ -677,13 +701,13 @@
677701
<!-- dc.subject -->
678702
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='subject']/doc:element/doc:field[@name='value']">
679703
<ccmm:subject>
680-
<ccmm:title xml:lang="en"><xsl:value-of select="."/></ccmm:title>
704+
<ccmm:title><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:title>
681705
</ccmm:subject>
682706
</xsl:for-each>
683707
<!-- dc.subject.* (nested qualifiers) -->
684708
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='subject']/doc:element/doc:element/doc:field[@name='value']">
685709
<ccmm:subject>
686-
<ccmm:title xml:lang="en"><xsl:value-of select="."/></ccmm:title>
710+
<ccmm:title><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:title>
687711
</ccmm:subject>
688712
</xsl:for-each>
689713
<!-- Fallback: if no subjects, provide a placeholder -->
@@ -701,7 +725,7 @@
701725
<!-- dc.description (abstract) -->
702726
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='description']/doc:element[@name='abstract']/doc:element/doc:field[@name='value']">
703727
<ccmm:description>
704-
<ccmm:description_text xml:lang="en"><xsl:value-of select="."/></ccmm:description_text>
728+
<ccmm:description_text><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:description_text>
705729
<ccmm:description_type>
706730
<ccmm:iri>https://vocabs.ccmm.cz/registry/codelist/DescriptionType/Abstract</ccmm:iri>
707731
<ccmm:label xml:lang="en">Abstract</ccmm:label>
@@ -711,7 +735,7 @@
711735
<!-- dc.description (general, non-qualified) -->
712736
<xsl:for-each select="doc:metadata/doc:element[@name='dc']/doc:element[@name='description']/doc:element/doc:field[@name='value']">
713737
<ccmm:description>
714-
<ccmm:description_text xml:lang="en"><xsl:value-of select="."/></ccmm:description_text>
738+
<ccmm:description_text><xsl:call-template name="XmlLangAttribute"/><xsl:value-of select="."/></ccmm:description_text>
715739
<ccmm:description_type>
716740
<ccmm:iri>https://vocabs.ccmm.cz/registry/codelist/DescriptionType/Abstract</ccmm:iri>
717741
<ccmm:label xml:lang="en">Abstract</ccmm:label>

0 commit comments

Comments
 (0)