diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/URIResolution.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/URIResolution.java
index 5abbe73da1..9c637e427f 100644
--- a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/URIResolution.java
+++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/URIResolution.java
@@ -56,8 +56,13 @@ static AnyURIValue resolveURI(final AnyURIValue relative, final AnyURIValue base
if (relativeURI.isAbsolute()) {
return relative;
}
- var baseURI = new URI(base.getStringValue() );
+ var baseURI = new URI(base.getStringValue());
if (!baseURI.isAbsolute()) {
+ var basePath = base.getStringValue();
+ if (basePath.startsWith("/")) {
+ var baseDir = basePath.substring(0, basePath.lastIndexOf('/') + 1);
+ return new AnyURIValue(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + baseDir + relative.getStringValue());
+ }
return relative;
}
try {
diff --git a/exist-core/src/test/java/org/exist/xquery/TransformTest.java b/exist-core/src/test/java/org/exist/xquery/TransformTest.java
index c92b0c2739..6c01147374 100644
--- a/exist-core/src/test/java/org/exist/xquery/TransformTest.java
+++ b/exist-core/src/test/java/org/exist/xquery/TransformTest.java
@@ -95,6 +95,38 @@ public void transform() throws XMLDBException {
"
End Template 1
" +
"", result);
}
+
+ /**
+ * Tests that a stylesheet can import another stylesheet stored in the
+ * same database collection using a relative path (filename only, no directory).
+ * Stylesheet passed as a URI string.
+ */
+ @Test
+ public void transformWithSameDirectoryImport() throws XMLDBException {
+ String query =
+ "import module namespace transform='http://exist-db.org/xquery/transform';\n" +
+ "let $xml := \n" +
+ "let $xsl := 'xmldb:exist:///db/"+TEST_COLLECTION_NAME+"/same-dir/a.xsl'\n" +
+ "return transform:transform($xml, $xsl, ())";
+ String result = execQuery(query);
+ assertEquals("From A
From B
", result);
+ }
+
+ /**
+ * Tests that a stylesheet can import another stylesheet stored in the
+ * same database collection using a relative path (filename only, no directory).
+ * Stylesheet passed as a node via doc().
+ */
+ @Test
+ public void transformWithSameDirectoryImportViaNode() throws XMLDBException {
+ String query =
+ "import module namespace transform='http://exist-db.org/xquery/transform';\n" +
+ "let $xml := \n" +
+ "let $xsl := doc('xmldb:exist:///db/"+TEST_COLLECTION_NAME+"/same-dir/a.xsl')\n" +
+ "return transform:transform($xml, $xsl, ())";
+ String result = execQuery(query);
+ assertEquals("From A
From B
", result);
+ }
private String execQuery(String query) throws XMLDBException {
@@ -171,6 +203,31 @@ public void setUp() throws ClassNotFoundException, IllegalAccessException, Insta
addXMLDocument(xsl1, doc1, "1.xsl");
addXMLDocument(xsl2, doc2, "2.xsl");
addXMLDocument(xsl3, doc3, "3.xsl");
+
+ service =
+ testCollection.getService(
+ CollectionManagementService.class);
+
+ Collection sameDir = service.createCollection("same-dir");
+ assertNotNull(sameDir);
+
+ String docA = "\n" +
+ "\n"+
+ "\n" +
+ "" +
+ "From A
" +
+ "" +
+ "";
+
+ String docB = "\n" +
+ "\n"+
+ "" +
+ "From B
" +
+ "" +
+ "";
+
+ addXMLDocument(sameDir, docA, "a.xsl");
+ addXMLDocument(sameDir, docB, "b.xsl");
}
@After
diff --git a/exist-core/src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java b/exist-core/src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java
index 1ca74a5b9e..a737651d97 100644
--- a/exist-core/src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java
+++ b/exist-core/src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java
@@ -58,6 +58,35 @@
*/
public class FunTransformITTest {
+ private static final XmldbURI TEST_IMPORT_XSLT_COLLECTION = XmldbURI.create("/db/fn-transform-import-test");
+ private static final XmldbURI IMPORT_A_XSLT_NAME = XmldbURI.create("a.xsl");
+ private static final XmldbURI IMPORT_B_XSLT_NAME = XmldbURI.create("b.xsl");
+
+ private static final String IMPORT_A_XSLT =
+ "\n" +
+ " \n" +
+ " \n" +
+ " From A
\n" +
+ " \n" +
+ "";
+
+ private static final String IMPORT_B_XSLT =
+ "\n" +
+ " From B
\n" +
+ "";
+
+ private static final String SAME_DIR_IMPORT_QUERY =
+ "fn:transform(map {\n" +
+ " \"stylesheet-location\": \"xmldb:exist:///db/fn-transform-import-test/a.xsl\",\n" +
+ " \"source-node\": document { }\n" +
+ "})?output";
+
+ private static final String SAME_DIR_IMPORT_VIA_NODE_QUERY =
+ "fn:transform(map {\n" +
+ " \"stylesheet-node\": doc(\"xmldb:exist:///db/fn-transform-import-test/a.xsl\"),\n" +
+ " \"source-node\": document { }\n" +
+ "})?output";
+
private static final XmldbURI TEST_IDENTITY_XSLT_COLLECTION = XmldbURI.create("/db/transform-identity-test");
private static final XmldbURI IDENTITY_XSLT_NAME = XmldbURI.create("xsl-identity.xslt");
@@ -143,6 +172,26 @@ public class FunTransformITTest {
@ClassRule
public static ExistEmbeddedServer existEmbeddedServer = new ExistEmbeddedServer(true, true);
+ /**
+ * Tests that fn:transform can resolve a relative xsl:import (same directory)
+ * when the stylesheet is given as a URI location.
+ */
+ @Test
+ public void sameDirectoryImportViaLocation() throws XPathException, PermissionDeniedException, EXistException {
+ final Source expected = Input.fromString("From A
From B
").build();
+ expectQuery(SAME_DIR_IMPORT_QUERY, expected);
+ }
+
+ /**
+ * Tests that fn:transform can resolve a relative xsl:import (same directory)
+ * when the stylesheet is given as a node loaded from the database.
+ */
+ @Test
+ public void sameDirectoryImportViaNode() throws XPathException, PermissionDeniedException, EXistException {
+ final Source expected = Input.fromString("From A
From B
").build();
+ expectQuery(SAME_DIR_IMPORT_VIA_NODE_QUERY, expected);
+ }
+
@Test
public void identityPersistentDom() throws XPathException, PermissionDeniedException, EXistException {
final Source expected = Input.fromString(IDENTITY_XML).build();
@@ -212,6 +261,11 @@ public static void storeResources() throws EXistException, PermissionDeniedExcep
Tuple(IDENTITY_XML_NAME, IDENTITY_XML)
);
+ createCollection(broker, transaction, TEST_IMPORT_XSLT_COLLECTION,
+ Tuple(IMPORT_A_XSLT_NAME, IMPORT_A_XSLT),
+ Tuple(IMPORT_B_XSLT_NAME, IMPORT_B_XSLT)
+ );
+
transaction.commit();
}
}