Skip to content

Commit 958b396

Browse files
authored
Merge pull request #4931 from evolvedbinary/hotfix/eb-2061-transform-xmldburi-resolution
Fix the resolution of XmldbURIs from within fn:transform
2 parents bbc03d4 + 00aa699 commit 958b396

File tree

5 files changed

+288
-52
lines changed

5 files changed

+288
-52
lines changed

exist-core/src/main/java/org/exist/util/SaxonConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public final class SaxonConfiguration {
5454
private SaxonConfiguration(final net.sf.saxon.Configuration configuration) {
5555
this.configuration = configuration;
5656
this.processor = new Processor(configuration);
57+
//TODO (AP) This is a better place to configure URI/Resource resolution for Saxon within eXist
58+
//At present the configuration for Saxon to resolve xmldb:exist: URIs is restricted to fn:transform
5759
}
5860

5961
/**

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

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -507,41 +507,13 @@ private Source resolveStylesheetLocation(final String stylesheetLocation) throws
507507

508508
final URI uri = URI.create(stylesheetLocation);
509509
if (uri.isAbsolute()) {
510-
return resolvePossibleStylesheetLocation(stylesheetLocation);
510+
return URIResolution.resolveDocument(stylesheetLocation, context, fnTransform);
511511
} else {
512512
final AnyURIValue resolved = resolveURI(new AnyURIValue(stylesheetLocation), context.getBaseURI());
513-
return resolvePossibleStylesheetLocation(resolved.getStringValue());
513+
return URIResolution.resolveDocument(resolved.getStringValue(), context, fnTransform);
514514
}
515515
}
516516

517-
/**
518-
* Resolve an absolute stylesheet location
519-
*
520-
* @param location of the stylesheet
521-
* @return the resolved stylesheet as a source
522-
* @throws XPathException if the item does not exist, or is not a document
523-
*/
524-
private Source resolvePossibleStylesheetLocation(final String location) throws XPathException {
525-
526-
final Sequence document;
527-
try {
528-
document = DocUtils.getDocument(context, location);
529-
} catch (final PermissionDeniedException e) {
530-
throw new XPathException(fnTransform, ErrorCodes.FODC0002,
531-
"Can not access '" + location + "'" + e.getMessage());
532-
}
533-
if (document != null && document.hasOne() && Type.subTypeOf(document.getItemType(), Type.NODE)) {
534-
if (document instanceof NodeProxy proxy) {
535-
return new DOMSource(proxy.getNode());
536-
}
537-
else if (document.itemAt(0) instanceof Node node) {
538-
return new DOMSource(node);
539-
}
540-
}
541-
throw new XPathException(fnTransform, ErrorCodes.FODC0002,
542-
"Location '"+ location + "' returns an item which is not a document node");
543-
}
544-
545517
/**
546518
* URI resolution, the core should be the same as for fn:resolve-uri
547519
* @param relative URI to resolve
@@ -550,19 +522,11 @@ else if (document.itemAt(0) instanceof Node node) {
550522
* @throws XPathException if resolution is not possible
551523
*/
552524
private AnyURIValue resolveURI(final AnyURIValue relative, final AnyURIValue base) throws XPathException {
553-
final URI relativeURI;
554-
final URI baseURI;
555525
try {
556-
relativeURI = new URI(relative.getStringValue());
557-
baseURI = new URI(base.getStringValue() );
526+
return URIResolution.resolveURI(relative, base);
558527
} catch (final URISyntaxException e) {
559528
throw new XPathException(fnTransform, ErrorCodes.FORG0009, "unable to resolve a relative URI against a base URI in fn:transform(): " + e.getMessage(), null, e);
560529
}
561-
if (relativeURI.isAbsolute()) {
562-
return relative;
563-
} else {
564-
return new AnyURIValue(baseURI.resolve(relativeURI));
565-
}
566530
}
567531

568532
private XSLTVersion getXsltVersion(final Source xsltStylesheet) throws XPathException {

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -214,22 +214,24 @@ private XsltExecutable compileExecutable(final Options options) throws XPathExce
214214
xsltCompiler.setParameter(new net.sf.saxon.s9api.QName(qKey.getPrefix(), qKey.getLocalPart()), value);
215215
}
216216

217-
// Take URI resolution into our own hands when there is no base
218-
xsltCompiler.setURIResolver((href, base) -> {
219-
try {
220-
final URI hrefURI = URI.create(href);
221-
if (options.resolvedStylesheetBaseURI.isEmpty() && !hrefURI.isAbsolute() && StringUtils.isEmpty(base)) {
222-
final XPathException resolutionException = new XPathException(fnTransform,
217+
xsltCompiler.setURIResolver(new URIResolution.CompileTimeURIResolver(context, fnTransform) {
218+
@Override public Source resolve(final String href, final String base) throws TransformerException {
219+
// Correct error from URI resolution when there is no base
220+
try {
221+
final URI hrefURI = URI.create(href);
222+
if (options.resolvedStylesheetBaseURI.isEmpty() && !hrefURI.isAbsolute() && StringUtils.isEmpty(base)) {
223+
final XPathException resolutionException = new XPathException(fnTransform,
223224
ErrorCodes.XTSE0165,
224225
"transform using a relative href, \n" +
225-
"using option stylesheet-text, but without stylesheet-base-uri");
226-
throw new TransformerException(resolutionException);
226+
"using option stylesheet-text, but without stylesheet-base-uri");
227+
throw new TransformerException(resolutionException);
228+
}
229+
} catch (final IllegalArgumentException e) {
230+
throw new TransformerException(e);
227231
}
228-
} catch (final IllegalArgumentException e) {
229-
throw new TransformerException(e);
232+
// Checked the special error case, defer to eXist resolution
233+
return super.resolve(href, base);
230234
}
231-
// Pass it back
232-
return null;
233235
});
234236

235237
try {
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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+
23+
package org.exist.xquery.functions.fn.transform;
24+
25+
import org.exist.dom.persistent.NodeProxy;
26+
import org.exist.security.PermissionDeniedException;
27+
import org.exist.xmldb.XmldbURI;
28+
import org.exist.xquery.ErrorCodes;
29+
import org.exist.xquery.Expression;
30+
import org.exist.xquery.XPathException;
31+
import org.exist.xquery.XQueryContext;
32+
import org.exist.xquery.util.DocUtils;
33+
import org.exist.xquery.value.AnyURIValue;
34+
import org.exist.xquery.value.Sequence;
35+
import org.exist.xquery.value.Type;
36+
import org.w3c.dom.Node;
37+
38+
import javax.xml.transform.Source;
39+
import javax.xml.transform.TransformerException;
40+
import javax.xml.transform.URIResolver;
41+
import javax.xml.transform.dom.DOMSource;
42+
import java.net.URI;
43+
import java.net.URISyntaxException;
44+
45+
public class URIResolution {
46+
47+
/**
48+
* URI resolution, the core should be the same as for fn:resolve-uri
49+
* @param relative URI to resolve
50+
* @param base to resolve against
51+
* @return resolved URI
52+
* @throws URISyntaxException if resolution is not possible
53+
*/
54+
static AnyURIValue resolveURI(final AnyURIValue relative, final AnyURIValue base) throws URISyntaxException, XPathException {
55+
var relativeURI = new URI(relative.getStringValue());
56+
if (relativeURI.isAbsolute()) {
57+
return relative;
58+
}
59+
var baseURI = new URI(base.getStringValue() );
60+
if (!baseURI.isAbsolute()) {
61+
return relative;
62+
}
63+
try {
64+
var xBase = XmldbURI.xmldbUriFor(baseURI);
65+
var resolved = xBase.getURI().resolve(relativeURI);
66+
return new AnyURIValue(XmldbURI.XMLDB_URI_PREFIX + resolved);
67+
} catch (URISyntaxException e) {
68+
return new AnyURIValue(baseURI.resolve(relativeURI));
69+
}
70+
}
71+
72+
public static class CompileTimeURIResolver implements URIResolver {
73+
74+
private final XQueryContext xQueryContext;
75+
private final Expression containingExpression;
76+
77+
public CompileTimeURIResolver(XQueryContext xQueryContext, Expression containingExpression) {
78+
this.xQueryContext = xQueryContext;
79+
this.containingExpression = containingExpression;
80+
}
81+
82+
@Override
83+
public Source resolve(final String href, final String base) throws TransformerException {
84+
85+
try {
86+
final AnyURIValue baseURI = new AnyURIValue(base);
87+
final AnyURIValue hrefURI = new AnyURIValue(href);
88+
var resolved = resolveURI(hrefURI, baseURI);
89+
return resolveDocument(resolved.getStringValue());
90+
} catch (URISyntaxException e) {
91+
throw new TransformerException(
92+
"Failed to resolve " + href + " against " + base, e);
93+
} catch (XPathException e) {
94+
throw new TransformerException(
95+
"Failed to find document as result of resolving " + href + " against " + base, e);
96+
}
97+
}
98+
99+
protected Source resolveDocument(final String location) throws XPathException {
100+
return URIResolution.resolveDocument(location, xQueryContext, containingExpression);
101+
}
102+
}
103+
104+
/**
105+
* Resolve an absolute document location, stylesheet or included source
106+
*
107+
* @param location of the stylesheet
108+
* @return the resolved stylesheet as a source
109+
* @throws org.exist.xquery.XPathException if the item does not exist, or is not a document
110+
*/
111+
static Source resolveDocument(final String location, final XQueryContext xQueryContext, Expression containingExpression) throws XPathException {
112+
113+
final Sequence document;
114+
try {
115+
document = DocUtils.getDocument(xQueryContext, location);
116+
} catch (final PermissionDeniedException e) {
117+
throw new XPathException(containingExpression, ErrorCodes.FODC0002,
118+
"Can not access '" + location + "'" + e.getMessage());
119+
}
120+
if (document == null || document.isEmpty()) {
121+
throw new XPathException(containingExpression, ErrorCodes.FODC0002,
122+
"No document found at location '"+ location);
123+
}
124+
if (document.hasOne() && Type.subTypeOf(document.getItemType(), Type.NODE)) {
125+
if (document instanceof NodeProxy proxy) {
126+
return new DOMSource(proxy.getNode());
127+
}
128+
else if (document.itemAt(0) instanceof Node node) {
129+
return new DOMSource(node);
130+
}
131+
}
132+
throw new XPathException(containingExpression, ErrorCodes.FODC0002,
133+
"Location '"+ location + "' returns an item which is not a document node");
134+
}
135+
}

0 commit comments

Comments
 (0)