Skip to content

Commit 7df3bf8

Browse files
alanpaxtonadamretter
authored andcommitted
[fix] fn:transform global parameters missing
Problem manifests when loading the stylesheet from eXist-db. Stylesheet document fetching into sources is not recognising as documents the documents it received from the DB (as opposed to ones which are constructed in memory). All our testing has been done with temporary in memory documents and we missed the case of transform source in eXist-db. To fix it, we just have to recognise org.exist.dom.persistent.DocumentImpl as well as org.exist.dom.memtree.DocumentImpl as a valid document source; similarly, the case where a document can be a NodeProxy wasn’t recognised, we just need to fetch the node from the proxy when we recognise it as such, and proceed with using a source wrapping the resulting node as the stylesheet source. This appears to make the
1 parent 26d643e commit 7df3bf8

File tree

4 files changed

+187
-7
lines changed

4 files changed

+187
-7
lines changed

exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Options.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import net.sf.saxon.s9api.XdmValue;
3333
import org.apache.commons.lang3.StringUtils;
3434
import org.exist.dom.memtree.NamespaceNode;
35+
import org.exist.dom.persistent.NodeProxy;
3536
import org.exist.security.PermissionDeniedException;
3637
import org.exist.xquery.ErrorCodes;
3738
import org.exist.xquery.XPathException;
@@ -527,7 +528,12 @@ private Source resolvePossibleStylesheetLocation(final String location) throws X
527528
"Can not access '" + location + "'" + e.getMessage());
528529
}
529530
if (document != null && document.hasOne() && Type.subTypeOf(document.getItemType(), Type.NODE)) {
530-
return new DOMSource((Node) document.itemAt(0));
531+
if (document instanceof NodeProxy proxy) {
532+
return new DOMSource(proxy.getNode());
533+
}
534+
else if (document.itemAt(0) instanceof Node node) {
535+
return new DOMSource(node);
536+
}
531537
}
532538
throw new XPathException(fnTransform, ErrorCodes.FODC0002,
533539
"Location '"+ location + "' returns an item which is not a document node");

exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.apache.logging.log4j.LogManager;
3535
import org.apache.logging.log4j.Logger;
3636
import org.exist.dom.QName;
37-
import org.exist.dom.memtree.DocumentImpl;
3837
import org.exist.util.Holder;
3938
import org.exist.xquery.ErrorCodes;
4039
import org.exist.xquery.XPathException;
@@ -177,7 +176,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
177176
final Document document;
178177
Source source = sourceNode.get();
179178
final Node node = ((DOMSource)sourceNode.get()).getNode();
180-
if (!(node instanceof DocumentImpl)) {
179+
if (!(node instanceof org.exist.dom.memtree.DocumentImpl) && !(node instanceof org.exist.dom.persistent.DocumentImpl)) {
181180
//The source may not be a document
182181
//If it isn't, it should be part of a document, so we build a DOMSource to use
183182
document = node.getOwnerDocument();
@@ -193,7 +192,6 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
193192
final Transform.TemplateInvocation invocation = new Transform.TemplateInvocation(
194193
options, sourceNode, delivery, xslt30Transformer, resultDocuments);
195194
return invocation.invoke();
196-
197195
} catch (final SaxonApiException | UncheckedXPathException e) {
198196
throw originalXPathException("Could not transform input: ", e, ErrorCodes.FOXT0003);
199197
}
@@ -205,6 +203,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
205203

206204

207205
private XsltExecutable compileExecutable(final Options options) throws XPathException {
206+
208207
final XsltCompiler xsltCompiler = org.exist.xquery.functions.fn.transform.Transform.SAXON_PROCESSOR.newXsltCompiler();
209208
final SingleRequestErrorListener errorListener = new SingleRequestErrorListener(Transform.ERROR_LISTENER);
210209
xsltCompiler.setErrorListener(errorListener);
@@ -223,7 +222,7 @@ private XsltExecutable compileExecutable(final Options options) throws XPathExce
223222
xsltCompiler.setURIResolver((href, base) -> {
224223
try {
225224
final URI hrefURI = URI.create(href);
226-
if (!options.resolvedStylesheetBaseURI.isPresent() && !hrefURI.isAbsolute() && StringUtils.isEmpty(base)) {
225+
if (options.resolvedStylesheetBaseURI.isEmpty() && !hrefURI.isAbsolute() && StringUtils.isEmpty(base)) {
227226
final XPathException resolutionException = new XPathException(fnTransform,
228227
ErrorCodes.XTSE0165,
229228
"transform using a relative href, \n" +
@@ -266,8 +265,7 @@ private XPathException originalXPathException(final String prefix, @Nonnull fina
266265

267266
cause = e;
268267
while (cause != null) {
269-
if (cause instanceof net.sf.saxon.trans.XPathException) {
270-
final net.sf.saxon.trans.XPathException xPathException = (net.sf.saxon.trans.XPathException)cause;
268+
if (cause instanceof final net.sf.saxon.trans.XPathException xPathException) {
271269
final StructuredQName from = xPathException.getErrorCodeQName();
272270
if (from != null) {
273271
final QName errorCodeQName = new QName(from.getLocalPart(), from.getURI(), from.getPrefix());
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" exclude-result-prefixes="xs math xd tei" version="3.0">
2+
3+
<xsl:param name="heading" as="xs:boolean" select="true()"/>
4+
<xsl:param name="documentID" as="xs:string" select="/tei:TEI/@xml:id"/>
5+
6+
<xsl:output indent="true"/>
7+
8+
<xsl:mode on-no-match="shallow-skip" use-accumulators="#all"/>
9+
<xsl:mode name="html" on-no-match="text-only-copy"/>
10+
11+
<xsl:accumulator name="document-nos" initial-value="()" as="xs:string*">
12+
<xsl:accumulator-rule match="tei:div[@type eq 'document']" select="($value, @n)" phase="end"/>
13+
</xsl:accumulator>
14+
15+
<xsl:accumulator name="document-ids" initial-value="()" as="xs:string*">
16+
<xsl:accumulator-rule match="tei:div[tei:div/@type = 'document']" select="()"/>
17+
<xsl:accumulator-rule match="tei:div[@type eq 'document']" select="($value, @xml:id)" phase="end"/>
18+
</xsl:accumulator>
19+
20+
<xsl:template match="tei:TEI">
21+
<div class="toc">
22+
<div class="toc__header">
23+
<h4 class="title">Contents</h4>
24+
</div>
25+
<nav aria-label="Side navigation,,," class="toc__chapters">
26+
<ul class="chapters js-smoothscroll">
27+
<xsl:apply-templates select="tei:text"/>
28+
</ul>
29+
</nav>
30+
</div>
31+
</xsl:template>
32+
33+
<xsl:template match="tei:div[@xml:id][not(@type = ('document'))]">
34+
<xsl:variable name="accDocs" as="xs:string*" select="accumulator-after('document-nos')"/>
35+
<xsl:variable name="prevDocs" as="xs:string*" select="accumulator-before('document-nos')"/>
36+
<xsl:variable name="docs" as="xs:string*" select="$accDocs[not(. = $prevDocs)]"/>
37+
<xsl:variable name="prevDocIDs" as="xs:string*" select="accumulator-before('document-ids')"/>
38+
<xsl:variable name="docIDs" as="xs:string*" select="accumulator-after('document-ids')[not(. = $prevDocIDs)]"/>
39+
<li data-tei-id="{@xml:id}">
40+
<xsl:if test="exists($docIDs) and tei:div[@type='document']">
41+
<xsl:attribute name="data-tei-documents" select="string-join($docIDs, ' ')"/>
42+
</xsl:if>
43+
44+
<a href="/{$documentID}/{@xml:id}">
45+
<xsl:apply-templates mode="html" select="tei:head"/>
46+
</a>
47+
<xsl:value-of select="(' (Document' || 's'[count($docs) gt 1] || ' ' || $docs[1] || ' - '[count($docs) gt 1] || $docs[last()][count($docs) gt 1] || ')')[exists($docs)]"/>
48+
49+
<xsl:where-populated>
50+
<ul class="chapters__nested">
51+
<xsl:apply-templates/>
52+
</ul>
53+
</xsl:where-populated>
54+
</li>
55+
</xsl:template>
56+
57+
<xsl:template match="tei:div[@xml:id eq 'toc']" priority="2"/>
58+
59+
<xsl:template match="tei:head/tei:note" mode="html"/>
60+
61+
<xsl:template match="tei:lb" mode="html">
62+
<br/>
63+
</xsl:template>
64+
65+
</xsl:stylesheet>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
(:
2+
: eXist-db Open Source Native XML Database
3+
: Copyright (C) 2001 The eXist-db Authors
4+
:
5+
6+
: http://www.exist-db.org
7+
:
8+
: This library is free software; you can redistribute it and/or
9+
: modify it under the terms of the GNU Lesser General Public
10+
: License as published by the Free Software Foundation; either
11+
: version 2.1 of the License, or (at your option) any later version.
12+
:
13+
: This library is distributed in the hope that it will be useful,
14+
: but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
: Lesser General Public License for more details.
17+
:
18+
: You should have received a copy of the GNU Lesser General Public
19+
: License along with this library; if not, write to the Free Software
20+
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
:)
22+
xquery version "3.1";
23+
24+
module namespace testTransform="http://exist-db.org/xquery/test/function_transform";
25+
import module namespace xmldb="http://exist-db.org/xquery/xmldb";
26+
27+
declare namespace test="http://exist-db.org/xquery/xqsuite";
28+
29+
declare variable $testTransform:doc := document {
30+
<TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="output">
31+
<teiHeader>
32+
<fileDesc>
33+
<titleStmt>
34+
<title/>
35+
</titleStmt>
36+
<publicationStmt>
37+
<p>Test document</p>
38+
</publicationStmt>
39+
<sourceDesc>
40+
<p>born digital</p>
41+
</sourceDesc>
42+
</fileDesc>
43+
</teiHeader>
44+
<text>
45+
<front>
46+
<div xml:id="pressrelease" type="section" subtype="press-release">
47+
<head>Press Release</head>
48+
</div>
49+
</front>
50+
<body>
51+
<div type="compilation" xml:id="comp">
52+
<head>Main Book</head>
53+
<div type="chapter" xml:id="ch1">
54+
<head>Chapter One</head>
55+
<div type="document" n="1" xml:id="d1"/>
56+
<div type="document" n="2" xml:id="d2"/>
57+
<div type="document" n="3" xml:id="d3"/>
58+
<div type="document" n="4" xml:id="d4"/>
59+
<div type="document" n="5" xml:id="d5"/>
60+
<div type="document" n="6" xml:id="d6"/>
61+
<div type="document" n="7" xml:id="d7"/>
62+
</div>
63+
<div type="chapter" xml:id="ch2">
64+
<head>Chapter Two</head>
65+
<div type="document" n="8" xml:id="d8"/>
66+
<div type="document" n="9" xml:id="d9"/>
67+
<div type="document" n="10" xml:id="d10"/>
68+
<div type="document" n="11" xml:id="d11"/>
69+
<div type="document" n="12" xml:id="d12"/>
70+
<div type="document" n="13" xml:id="d13"/>
71+
<div type="document" n="14" xml:id="d14"/>
72+
<div type="document" n="15" xml:id="d15"/>
73+
</div>
74+
<div type="chapter" xml:id="ch3">
75+
<head>Chapter Three</head>
76+
<div type="document" n="16" xml:id="d16"/>
77+
<div type="document" n="17" xml:id="d17"/>
78+
<div type="document" n="18" xml:id="d18"/>
79+
<div type="document" n="19" xml:id="d19"/>
80+
<div type="document" n="20" xml:id="d20"/>
81+
<div type="document" n="21" xml:id="d21"/>
82+
</div>
83+
<div type="chapter" xml:id="ch4">
84+
<head>Chapter Four</head>
85+
<div type="document" n="22" xml:id="d22"/>
86+
<div type="document" n="23" xml:id="d23"/>
87+
<div type="document" n="24" xml:id="d24"/>
88+
<div type="document" n="25" xml:id="d25"/>
89+
<div type="document" n="26" xml:id="d26"/>
90+
<div type="document" n="27" xml:id="d27"/>
91+
<div type="document" n="28" xml:id="d28"/>
92+
</div>
93+
</div>
94+
</body>
95+
</text>
96+
</TEI>
97+
};
98+
99+
declare
100+
%test:assertEquals("<div class=""toc""><div class=""toc__header""><h4 class=""title"">Contents</h4></div><nav aria-label=""Side navigation,,,"" class=""toc__chapters""><ul class=""chapters js-smoothscroll""><li data-tei-id=""pressrelease""><a href=""/output/pressrelease"">Press Release</a></li><li data-tei-id=""comp""><a href=""/output/comp"">Main Book</a> (Documents 1 - 28)<ul class=""chapters__nested""><li data-tei-id=""ch1"" data-tei-documents=""d1 d2 d3 d4 d5 d6 d7""><a href=""/output/ch1"">Chapter One</a> (Documents 1 - 7)</li><li data-tei-id=""ch2"" data-tei-documents=""d8 d9 d10 d11 d12 d13 d14 d15""><a href=""/output/ch2"">Chapter Two</a> (Documents 8 - 15)</li><li data-tei-id=""ch3"" data-tei-documents=""d16 d17 d18 d19 d20 d21""><a href=""/output/ch3"">Chapter Three</a> (Documents 16 - 21)</li><li data-tei-id=""ch4"" data-tei-documents=""d22 d23 d24 d25 d26 d27 d28""><a href=""/output/ch4"">Chapter Four</a> (Documents 22 - 28)</li></ul></li></ul></nav></div>")
101+
function testTransform:issue-4609() {
102+
let $create-collection := xmldb:create-collection("/db", "fn_transform_issue_4609")
103+
let $doc-store := xmldb:store("/db/fn_transform_issue_4609", "input.xml", $testTransform:doc)
104+
105+
(: this works :)
106+
let $result := ( fn:transform(map{
107+
"stylesheet-location": 'resource:/org/exist/xquery/tei-toc.xsl',
108+
"source-node": doc('/db/fn_transform_issue_4609/input.xml')
109+
}))
110+
return $result?output
111+
};

0 commit comments

Comments
 (0)