Skip to content

Commit c7e33b5

Browse files
committed
New implementation of TEI to Markdown, and Markdown to TEI conversions using XSLT
1 parent b352531 commit c7e33b5

8 files changed

Lines changed: 3982 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
fail-fast: false
99
matrix:
1010
os: [ubuntu-latest, macos-latest, windows-latest]
11-
jdk: [8,11,17,21]
11+
jdk: [11,25]
1212
runs-on: ${{ matrix.os }}
1313
steps:
1414
- uses: actions/checkout@v4
@@ -19,7 +19,7 @@ jobs:
1919
java-version: ${{ matrix.jdk }}
2020
- run: mvn clean package
2121
- name: Persist built XAR
22-
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '8'
22+
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '11'
2323
uses: actions/upload-artifact@v4
2424
with:
2525
name: xar
@@ -28,7 +28,7 @@ jobs:
2828
retention-days: 1
2929
overwrite: true
3030
- name: Persist built ci-resources
31-
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '8'
31+
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '11'
3232
uses: actions/upload-artifact@v4
3333
with:
3434
name: ci-resources

pom.xml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
<public.xar.repo.uri>https://exist-db.org/exist/apps/public-repo</public.xar.repo.uri>
5555

5656
<!-- NOTE(AR): this is used for generating forms in the xml-maven-plugin below -->
57-
<saxon.version>9.9.1-8</saxon.version>
57+
<saxon.version>12.9</saxon.version>
58+
59+
<xspec.version>3.1.3</xspec.version>
5860

5961
<!-- used in the EXPath Package Descriptor -->
6062
<package-title>${project.name}</package-title>
@@ -214,6 +216,35 @@
214216
</execution>
215217
</executions>
216218
</plugin>
219+
<plugin>
220+
<groupId>io.xspec.maven</groupId>
221+
<artifactId>xspec-maven-plugin</artifactId>
222+
<version>${xspec.version}</version>
223+
<executions>
224+
<execution>
225+
<id>services-xspec-tests</id>
226+
<phase>test</phase>
227+
<goals>
228+
<goal>run-xspec</goal>
229+
</goals>
230+
<configuration>
231+
<testDir>src/test/xar-resources</testDir>
232+
</configuration>
233+
</execution>
234+
</executions>
235+
<dependencies>
236+
<dependency>
237+
<groupId>io.xspec</groupId>
238+
<artifactId>xspec</artifactId>
239+
<version>${xspec.version}</version>
240+
</dependency>
241+
<dependency>
242+
<groupId>net.sf.saxon</groupId>
243+
<artifactId>Saxon-HE</artifactId>
244+
<version>${saxon.version}</version>
245+
</dependency>
246+
</dependencies>
247+
</plugin>
217248
<plugin>
218249
<groupId>ro.kuberam.maven.plugins</groupId>
219250
<artifactId>kuberam-expath-plugin</artifactId>
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet
3+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
5+
xmlns:tei="http://www.tei-c.org/ns/1.0"
6+
xmlns:m2t="http://manuforma-form-to-tei"
7+
exclude-result-prefixes="xs"
8+
version="2.0">
9+
10+
<!--
11+
Convert TEI like submission from a manuForma form to TEI
12+
@author Adam Retter
13+
-->
14+
15+
<xsl:param name="new" as="xs:boolean" select="false()"/>
16+
<xsl:param name="id" as="xs:string" required="yes"/>
17+
<xsl:param name="uri" as="xs:string?" required="yes"/>
18+
<xsl:param name="user-id" as="xs:string" select="'guest'"/>
19+
<xsl:param name="user-name" as="xs:string" select="'Guest'"/>
20+
21+
22+
<xsl:import href="tei-to-markdown.xslt"/>
23+
24+
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
25+
26+
27+
<xsl:template match="tei:TEI">
28+
<xsl:copy>
29+
<xsl:apply-templates select="node()|@*[local-name() ne 'class']"/>
30+
</xsl:copy>
31+
</xsl:template>
32+
33+
<xsl:template match="tei:note|tei:summary[not(parent::tei:layoutDesc)]|tei:quote">
34+
<xsl:copy>
35+
<xsl:apply-templates select="@*"/>
36+
<xsl:apply-templates select="node()" mode="tei-to-markdown"/>
37+
</xsl:copy>
38+
</xsl:template>
39+
40+
<xsl:template match="tei:ab[@type eq 'factoid'][@subtype eq 'relation']">
41+
<xsl:call-template name="copy-with-updated-id-and-source">
42+
<xsl:with-param name="id" select="concat('factoid-', $id)"/>
43+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
44+
</xsl:call-template>
45+
</xsl:template>
46+
47+
<xsl:template match="tei:ab">
48+
<xsl:copy>
49+
<xsl:apply-templates select="@*"/>
50+
<xsl:apply-templates select="node()" mode="tei-to-markdown"/>
51+
</xsl:copy>
52+
</xsl:template>
53+
54+
<!-- Add a Contributor into the titleStmt Element -->
55+
<xsl:template match="tei:titleStmt">
56+
<xsl:copy>
57+
<xsl:apply-templates select="@*"/>
58+
<xsl:apply-templates select="tei:title,tei:author"/>
59+
<xsl:apply-templates select="tei:editor"/>
60+
<xsl:if test="$user-id ne 'guest' and empty(preceding-sibling::tei:editor[. eq $user-name])">
61+
<tei:editor xml:id="{$user-id}" role="contributor"><xsl:value-of select="$user-name"/></tei:editor>
62+
</xsl:if>
63+
<xsl:apply-templates select="tei:meeting,tei:respStmt"/>
64+
</xsl:copy>
65+
</xsl:template>
66+
67+
<xsl:template match="tei:idno[parent::tei:ab[@type eq 'factoid'][@subtype eq 'relation']]">
68+
<xsl:call-template name="copy-with-updated-id-and-source">
69+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:idno) + 1)"/>
70+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
71+
</xsl:call-template>
72+
</xsl:template>
73+
74+
<xsl:template match="tei:idno[parent::tei:publicationStmt]">
75+
<xsl:copy>
76+
<xsl:apply-templates select="node()|@*"/>
77+
<xsl:if test="$new">
78+
<xsl:value-of select="$uri"/>
79+
</xsl:if>
80+
</xsl:copy>
81+
</xsl:template>
82+
83+
<xsl:template match="tei:place[parent::tei:listPlace]">
84+
<xsl:call-template name="copy-with-updated-id-and-source">
85+
<xsl:with-param name="id" select="concat('place-', $id)"/>
86+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
87+
</xsl:call-template>
88+
</xsl:template>
89+
90+
<xsl:template match="tei:placeName[parent::tei:place/parent::tei:listPlace]">
91+
<xsl:call-template name="copy-with-updated-id-and-source">
92+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:placeName) + 1)"/>
93+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
94+
</xsl:call-template>
95+
</xsl:template>
96+
97+
<xsl:template match="tei:bibl[parent::tei:place/parent::tei:listPlace or parent::tei:person/parent::tei:listPerson or parent::tei:note/parent::tei:bibl/parent::tei:body or parent::tei:bibl/parent::tei:bibl/parent::tei:body]">
98+
<xsl:copy>
99+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
100+
<!-- xsl:attribute name="xml:id" select="concat('bibl-', count(preceding-sibling::tei:bibl) + 1)"/ -->
101+
<xsl:apply-templates select="node()"/>
102+
</xsl:copy>
103+
</xsl:template>
104+
105+
<xsl:template match="tei:bibl[parent::tei:body/parent::tei:text]">
106+
<xsl:copy>
107+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
108+
<!-- xsl:attribute name="xml:id" select="concat('work-', $id)"/ -->
109+
<xsl:apply-templates select="node()"/>
110+
</xsl:copy>
111+
</xsl:template>
112+
113+
<xsl:template match="tei:bibl[parent::tei:listBibl/parent::tei:additional/parent::tei:msDesc]">
114+
<xsl:copy>
115+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
116+
<!-- xsl:attribute name="xml:id" select="concat('bibl-', (count(parent::tei:listBibl/parent::tei:additional/preceding-sibling::tei:additional) + 1), '.', (count(parent::tei:listBibl/preceding-sibling::tei:listBibl) + 1), '.', (count(preceding-sibling::tei:bibl) + 1))"/ -->
117+
<xsl:apply-templates select="node()"/>
118+
</xsl:copy>
119+
</xsl:template>
120+
121+
<xsl:template match="tei:title[parent::tei:bibl/parent::tei:body]">
122+
<xsl:call-template name="copy-with-updated-id-and-source">
123+
<xsl:with-param name="id" select="concat('title-', count(preceding-sibling::tei:title) + 1)"/>
124+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
125+
</xsl:call-template>
126+
</xsl:template>
127+
128+
<xsl:template match="tei:person[parent::tei:listPerson]">
129+
<xsl:call-template name="copy-with-updated-id-and-source">
130+
<xsl:with-param name="id" select="concat('person-', $id)"/>
131+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
132+
</xsl:call-template>
133+
</xsl:template>
134+
135+
<xsl:template match="tei:persName[parent::tei:person/parent::tei:listPerson]">
136+
<xsl:call-template name="copy-with-updated-id-and-source">
137+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:persName) + 1)"/>
138+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
139+
</xsl:call-template>
140+
</xsl:template>
141+
142+
<xsl:template match="tei:msDesc[parent::tei:person/parent::tei:listPerson]">
143+
<xsl:call-template name="copy-with-updated-id-and-source">
144+
<xsl:with-param name="id" select="concat('ms-', $id)"/>
145+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
146+
</xsl:call-template>
147+
</xsl:template>
148+
149+
<xsl:template match="tei:msItem[parent::tei:msContents/parent::tei:msDesc]">
150+
<xsl:call-template name="copy-with-updated-id-and-source">
151+
<xsl:with-param name="id" select="concat('item-', (count(parent::tei:msContents/preceding-sibling::tei:msContents) + 1), '.', (count(preceding-sibling::tei:msItem) + 1))"/>
152+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
153+
</xsl:call-template>
154+
</xsl:template>
155+
156+
<xsl:template match="tei:layout[parent::tei:layoutDesc/parent::tei:objectDesc/parent::tei:physDesc/parent::tei:msDesc]">
157+
<xsl:call-template name="copy-with-updated-id-and-source">
158+
<xsl:with-param name="id" select="concat('layout-', (count(parent::tei:layoutDesc/parent::tei:objectDesc/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:layoutDesc/parent::tei:objectDesc/preceding-sibling::tei:objectDesc) + 1), '.', (count(parent::tei:layoutDesc/preceding-sibling::tei:layoutDesc) + 1), '.', (count(preceding-sibling::tei:layout) + 1))"/>
159+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
160+
</xsl:call-template>
161+
</xsl:template>
162+
163+
<xsl:template match="tei:handNote[parent::tei:handDesc/parent::tei:physDesc/parent::tei:msDesc]">
164+
<xsl:call-template name="copy-with-updated-id-and-source">
165+
<xsl:with-param name="id" select="concat('hand-', (count(parent::tei:handDesc/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:handDesc/preceding-sibling::tei:handDesc) + 1), '.', (count(preceding-sibling::tei:handNote) + 1))"/>
166+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
167+
</xsl:call-template>
168+
</xsl:template>
169+
170+
<xsl:template match="tei:item[parent::tei:list/parent::tei:additions/parent::tei:physDesc/parent::tei:msDesc]">
171+
<xsl:call-template name="copy-with-updated-id-and-source">
172+
<xsl:with-param name="id" select="concat('add-', (count(parent::tei:list/parent::tei:additions/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:list/parent::tei:additions/preceding-sibling::tei:additions) + 1), '.', (count(parent::tei:list/preceding-sibling::tei:list) + 1), '.', (count(preceding-sibling::tei:item) + 1))"/>
173+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
174+
</xsl:call-template>
175+
</xsl:template>
176+
177+
<xsl:template match="tei:relation[tei:active or tei:mutual or tei:passive]">
178+
<xsl:copy>
179+
<xsl:apply-templates select="@*[not(name(.) = ('active', 'mutual', 'passive'))]"/>
180+
<xsl:if test="tei:active/@ref">
181+
<xsl:attribute name="active" select="string-join(tei:active/@ref, ' ')"/>
182+
</xsl:if>
183+
<xsl:if test="tei:mutual/@ref">
184+
<xsl:attribute name="mutual" select="string-join(tei:mutual/@ref, ' ')"/>
185+
</xsl:if>
186+
<xsl:if test="tei:passive/@ref">
187+
<xsl:attribute name="passive" select="string-join(tei:passive/@ref, ' ')"/>
188+
</xsl:if>
189+
</xsl:copy>
190+
</xsl:template>
191+
192+
193+
<xsl:function name="m2t:extract-source" as="xs:string">
194+
<xsl:param name="input" as="xs:string" required="yes"/>
195+
<xsl:sequence select="concat('#', tokenize($input, '#')[last()])"/>
196+
</xsl:function>
197+
198+
<!-- Copies an element whilst adding or replacing its 'xml:id' and 'source' attributes -->
199+
<xsl:template name="copy-with-updated-id-and-source">
200+
<xsl:param name="id" as="xs:string" required="yes"/>
201+
<xsl:param name="source" as="xs:string" required="yes"/>
202+
<xsl:copy>
203+
<xsl:attribute name="xml:id" select="$id"/>
204+
<xsl:if test="@source[. ne '']">
205+
<xsl:attribute name="source" select="$source"/>
206+
</xsl:if>
207+
<xsl:apply-templates select="@*[not(name(.) = ('xml:id', 'source'))]|node()"/>
208+
</xsl:copy>
209+
</xsl:template>
210+
211+
212+
<!-- Default: Identity trasform everything whilst updating the 'source' attribute -->
213+
<xsl:template match="node()|@*">
214+
<xsl:copy>
215+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
216+
<xsl:if test="@source[. ne '']">
217+
<xsl:attribute name="source" select="concat('#', substring-after(@source, '#'))"/>
218+
</xsl:if>
219+
<xsl:apply-templates select="node()"/>
220+
</xsl:copy>
221+
</xsl:template>
222+
223+
</xsl:stylesheet>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet
3+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
5+
xmlns:tei="http://www.tei-c.org/ns/1.0"
6+
xmlns:m2t="http://markdown-2-tei"
7+
exclude-result-prefixes="xs"
8+
version="2.0">
9+
10+
<!--
11+
Convert Markdown to TEI from manuForma forms
12+
@author Adam Retter
13+
-->
14+
15+
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
16+
17+
18+
<xsl:template name="markdown-to-tei">
19+
<xsl:apply-templates select="." mode="markdown-to-tei"/>
20+
</xsl:template>
21+
22+
<xsl:template match="text()[matches(., '\*\*([^*]+)\*\*')]" mode="markdown-to-tei">
23+
<xsl:apply-templates select="m2t:bold(.)"/>
24+
</xsl:template>
25+
26+
<xsl:template match="text()[matches(., '(^|[^*])\*[^*]+\*([^*]|$)')]" mode="markdown-to-tei">
27+
<xsl:apply-templates select="m2t:italic(.)"/>
28+
</xsl:template>
29+
30+
<!-- Default: Identity trasform everything -->
31+
<xsl:template match="node()|@*" mode="markdown-to-tei" priority="-1">
32+
<xsl:copy>
33+
<xsl:apply-templates select="node()|@*" mode="#current"/>
34+
</xsl:copy>
35+
</xsl:template>
36+
37+
<xsl:function name="m2t:italic" as="node()+">
38+
<xsl:param name="markdown" as="text()" required="true"/>
39+
<xsl:analyze-string select="$markdown" regex="(^|[^*])\*([^*]+)\*([^*]|$)">
40+
<xsl:matching-substring>
41+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:emph rend="italic"><xsl:value-of select="regex-group(2)"/></tei:emph><xsl:if test="m2t:non-empty(regex-group(3))"><xsl:value-of select="regex-group(3)"/></xsl:if>
42+
</xsl:matching-substring>
43+
<xsl:non-matching-substring>
44+
<xsl:value-of select="."/>
45+
</xsl:non-matching-substring>
46+
</xsl:analyze-string>
47+
</xsl:function>
48+
49+
<xsl:function name="m2t:bold" as="node()+">
50+
<xsl:param name="markdown" as="text()" required="true"/>
51+
<xsl:analyze-string select="$markdown" regex="\*\*([^*]+)\*\*">
52+
<xsl:matching-substring>
53+
<tei:emph rend="bold"><xsl:value-of select="regex-group(1)"/></tei:emph>
54+
</xsl:matching-substring>
55+
<xsl:non-matching-substring>
56+
<xsl:value-of select="."/>
57+
</xsl:non-matching-substring>
58+
</xsl:analyze-string>
59+
</xsl:function>
60+
61+
<xsl:function name="m2t:non-empty" as="xs:string*">
62+
<xsl:param name="inputs" as="xs:string*" required="true"/>
63+
<xsl:sequence select="$inputs[string-length(.) gt 0]"/>
64+
</xsl:function>
65+
66+
</xsl:stylesheet>

0 commit comments

Comments
 (0)