From 99ef5fdef8124869814b5efe041ee196765ba4a6 Mon Sep 17 00:00:00 2001 From: Laurettta Date: Thu, 7 May 2026 00:25:43 +0100 Subject: [PATCH 1/3] [test] Tests to reproduce relative XSLT import issues: https://github.com/evolvedbinary/elemental/issues/169 --- .../java/org/exist/xquery/TransformTest.java | 69 ++++++++++++++++++ .../fn/transform/FunTransformITTest.java | 70 +++++++++++++++++++ 2 files changed, 139 insertions(+) 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..062bd60d32 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,50 @@ public void transform() throws XMLDBException { "

End Template 1

" + "", result); } + + @Test + public void transformWithSameDirectoryImportViaDbLocation() throws XMLDBException { + final String query = + "import module namespace transform='http://exist-db.org/xquery/transform';\n" + + "let $xml := \n" + + "let $xsl := '/db/" + TEST_COLLECTION_NAME + "/same-dir/a.xsl'\n" + + "return transform:transform($xml, $xsl, ())"; + final String result = execQuery(query); + assertEquals("

From A

From B

", result); + } + + @Test + public void transformWithSameDirectoryImportViaXmldbLocation() throws XMLDBException { + final 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, ())"; + final String result = execQuery(query); + assertEquals("

From A

From B

", result); + } + + @Test + public void transformWithSameDirectoryImportViaDbNode() throws XMLDBException { + final String query = + "import module namespace transform='http://exist-db.org/xquery/transform';\n" + + "let $xml := \n" + + "let $xsl := doc('/db/" + TEST_COLLECTION_NAME + "/same-dir/a.xsl')\n" + + "return transform:transform($xml, $xsl, ())"; + final String result = execQuery(query); + assertEquals("

From A

From B

", result); + } + + @Test + public void transformWithSameDirectoryImportViaXmldbNode() throws XMLDBException { + final 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, ())"; + final String result = execQuery(query); + assertEquals("

From A

From B

", result); + } private String execQuery(String query) throws XMLDBException { @@ -171,6 +215,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..aa1a152413 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,47 @@ */ 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_VIA_DB_LOCATION_QUERY = + "fn:transform(map {\n" + + " \"stylesheet-location\": \"/db/fn-transform-import-test/a.xsl\",\n" + + " \"source-node\": document { }\n" + + "})?output"; + + private static final String SAME_DIR_IMPORT_VIA_XMLDB_LOCATION_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_DB_NODE_QUERY = + "fn:transform(map {\n" + + " \"stylesheet-node\": doc(\"/db/fn-transform-import-test/a.xsl\"),\n" + + " \"source-node\": document { }\n" + + "})?output"; + + private static final String SAME_DIR_IMPORT_VIA_XMLDB_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 +184,30 @@ public class FunTransformITTest { @ClassRule public static ExistEmbeddedServer existEmbeddedServer = new ExistEmbeddedServer(true, true); + @Test + public void sameDirectoryImportViaDbLocation() throws XPathException, PermissionDeniedException, EXistException { + final Source expected = Input.fromString("

From A

From B

").build(); + expectQuery(SAME_DIR_IMPORT_VIA_DB_LOCATION_QUERY, expected); + } + + @Test + public void sameDirectoryImportViaXmldbLocation() throws XPathException, PermissionDeniedException, EXistException { + final Source expected = Input.fromString("

From A

From B

").build(); + expectQuery(SAME_DIR_IMPORT_VIA_XMLDB_LOCATION_QUERY, expected); + } + + @Test + public void sameDirectoryImportViaDbNode() throws XPathException, PermissionDeniedException, EXistException { + final Source expected = Input.fromString("

From A

From B

").build(); + expectQuery(SAME_DIR_IMPORT_VIA_DB_NODE_QUERY, expected); + } + + @Test + public void sameDirectoryImportViaXmldbNode() throws XPathException, PermissionDeniedException, EXistException { + final Source expected = Input.fromString("

From A

From B

").build(); + expectQuery(SAME_DIR_IMPORT_VIA_XMLDB_NODE_QUERY, expected); + } + @Test public void identityPersistentDom() throws XPathException, PermissionDeniedException, EXistException { final Source expected = Input.fromString(IDENTITY_XML).build(); @@ -212,6 +277,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(); } } From 07de7d646f864e48e9c5ec1d6e18909af67ef9ab Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Wed, 3 Jun 2026 17:38:07 +0200 Subject: [PATCH 2/3] [bugfix] Make sure that /db URIs are converted to xmldb:exist:///db URIs when setting Base URI for XSLT transformations --- .../org/exist/xquery/functions/fn/transform/Options.java | 9 ++++++++- .../org/exist/xquery/functions/transform/Transform.java | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Options.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Options.java index 2d37625501..072dbbdddc 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Options.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Options.java @@ -54,6 +54,7 @@ import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.XdmValue; import org.exist.dom.memtree.NamespaceNode; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.ErrorCodes; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; @@ -490,7 +491,13 @@ private Tuple2 getStylesheet(final MapType options) throws XPath final List> results = new ArrayList<>(1); final Optional stylesheetLocation = Options.STYLESHEET_LOCATION.get(options).map(StringValue::getStringValue); if (stylesheetLocation.isPresent()) { - results.add(Tuple(stylesheetLocation.get(), resolveStylesheetLocation(stylesheetLocation.get()))); + final String strStylesheetLocationUri; + if (stylesheetLocation.get().startsWith("/db/")) { + strStylesheetLocationUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + stylesheetLocation.get(); + } else { + strStylesheetLocationUri = stylesheetLocation.get(); + } + results.add(Tuple(strStylesheetLocationUri, resolveStylesheetLocation(stylesheetLocation.get()))); } final Optional stylesheetNode = Options.STYLESHEET_NODE.get(options).map(NodeValue::getNode); diff --git a/exist-core/src/main/java/org/exist/xquery/functions/transform/Transform.java b/exist-core/src/main/java/org/exist/xquery/functions/transform/Transform.java index d880ebcf4f..f135703f66 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/transform/Transform.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/transform/Transform.java @@ -181,7 +181,11 @@ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathExce // Parameter 1 & 2 final Sequence inputNode = args[0]; - final Item stylesheetItem = args[1].itemAt(0); + Item stylesheetItem = args[1].itemAt(0); + if (stylesheetItem instanceof StringValue sv && sv.toString().startsWith("/db/")) { + // adjust /db like URI to xmldb:exist:///db/ like URI + stylesheetItem = new StringValue(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + sv.toString().substring(3)); + } // Parse 3rd parameter final Node options = args[2].isEmpty() ? null : ((NodeValue) args[2].itemAt(0)).getNode(); From fd6c79eb246f3d2e068fb7efb64511b06405bd14 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Wed, 3 Jun 2026 18:02:26 +0200 Subject: [PATCH 3/3] [bugfix] Node#getBaseURI() and the XPath function fn:base-uri() now return a URI. Instead of '/db/a/b/c.xml' the result is now 'xmldb:exist:///db/a/b/c.xml'. This fixes a number of issues with relative XSLT stylesheet imports. Closes https://github.com/evolvedbinary/elemental/issues/169 --- exist-core/pom.xml | 14 ++++++- .../exist/dom/memtree/CDATASectionImpl.java | 36 ++++++++++++++++ .../org/exist/dom/persistent/AttrImpl.java | 8 +++- .../dom/persistent/CDATASectionImpl.java | 42 +++++++++++++++++++ .../org/exist/dom/persistent/CommentImpl.java | 8 +++- .../exist/dom/persistent/DocumentImpl.java | 2 +- .../org/exist/dom/persistent/ElementImpl.java | 7 +++- .../persistent/ProcessingInstructionImpl.java | 8 +++- .../org/exist/dom/persistent/TextImpl.java | 8 +++- .../exist/util/EXistURISchemeURIResolver.java | 26 ++++++++++++ .../xquery/functions/validation/Shared.java | 26 +++++++++++- .../java/org/exist/xquery/XQueryTest.java | 8 ++-- exist-core/src/test/xquery/base-uri.xql | 4 +- .../test/xquery/indexing/indexes-base-uri.xq | 32 ++++++++++++-- .../indexing/lucene/AbstractFieldConfig.java | 30 ++++++++----- .../exist/xquery/modules/lucene/Query.java | 2 +- 16 files changed, 231 insertions(+), 30 deletions(-) diff --git a/exist-core/pom.xml b/exist-core/pom.xml index 330179a94d..ad63e6aff9 100644 --- a/exist-core/pom.xml +++ b/exist-core/pom.xml @@ -806,6 +806,7 @@ src/test/xquery/base-uri.xql src/test/xquery/parenthesizedLocationStep.xml src/test/xquery/tail-recursion.xml + src/test/xquery/indexing/indexes-base-uri.xq src/test/xquery/maps/maps.xqm src/test/xquery/numbers/format-numbers.xql src/test/xquery/util/util.xml @@ -894,6 +895,7 @@ src/main/java/org/exist/dom/QName.java src/main/java/org/exist/dom/memtree/AbstractCharacterData.java src/main/java/org/exist/dom/memtree/AttrImpl.java + src/main/java/org/exist/dom/memtree/CDATASectionImpl.java src/main/java/org/exist/dom/memtree/CommentImpl.java src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java src/main/java/org/exist/dom/memtree/DocumentImpl.java @@ -913,6 +915,7 @@ src/test/java/org/exist/dom/persistent/BasicNodeSetTest.java src/main/java/org/exist/dom/persistent/BinaryDocument.java src/test/java/org/exist/dom/persistent/CDataIntergationTest.java + src/main/java/org/exist/dom/persistent/CDATASectionImpl.java src/main/java/org/exist/dom/persistent/CommentImpl.java src/test/java/org/exist/dom/persistent/CommentTest.java src/test/java/org/exist/dom/persistent/DefaultDocumentSetTest.java @@ -1115,6 +1118,7 @@ src/test/java/org/exist/util/CollationsTest.java src/main/java/org/exist/util/Configuration.java src/test/java/org/exist/util/DOMSerializerTest.java + src/main/java/org/exist/util/EXistURISchemeURIResolver.java src/test/java/org/exist/util/LeasableTest.java src/test/java/org/exist/util/MediaTypeResolverTest.java src/main/java/org/exist/util/MimeTable.java @@ -1417,6 +1421,7 @@ src/test/java/org/exist/xquery/functions/validate/JingSchematronTest.java src/test/java/org/exist/xquery/functions/validate/JingXsdTest.java src/main/java/org/exist/xquery/functions/validation/Jaxp.java + src/main/java/org/exist/xquery/functions/validation/Shared.java src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java src/main/java/org/exist/xquery/functions/xmldb/FunXCollection.java src/main/java/org/exist/xquery/functions/xmldb/XMLDBGetMimeType.java @@ -1522,6 +1527,7 @@ src/test/xquery/pi.xqm src/test/xquery/tail-recursion.xml src/test/xquery/type-promotion.xqm + src/test/xquery/indexing/indexes-base-uri.xq src/test/xquery/maps/maps.xqm src/test/xquery/numbers/format-numbers.xql src/test/xquery/securitymanager/acl.xqm @@ -1616,7 +1622,8 @@ src/main/java/org/exist/dom/QName.java src/main/java/org/exist/dom/memtree/AbstractCharacterData.java src/main/java/org/exist/dom/memtree/AttrImpl.java - src/main/java/org/exist/dom/memtree/CommentImpl.java + src/main/java/org/exist/dom/memtree/CDATASectionImpl.java + src/main/java/org/exist/dom/memtree/CommentImpl.java src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java src/main/java/org/exist/dom/memtree/DocumentImpl.java src/test/java/org/exist/dom/memtree/DocumentImplTest.java @@ -1643,6 +1650,7 @@ src/test/java/org/exist/dom/persistent/BasicNodeSetTest.java src/main/java/org/exist/dom/persistent/BinaryDocument.java src/test/java/org/exist/dom/persistent/CDataIntergationTest.java + src/main/java/org/exist/dom/persistent/CDATASectionImpl.java src/main/java/org/exist/dom/persistent/CommentImpl.java src/test/java/org/exist/dom/persistent/CommentTest.java src/test/java/org/exist/dom/persistent/DefaultDocumentSetTest.java @@ -1913,6 +1921,7 @@ src/test/java/org/exist/util/CollectionOfArrayIteratorTest.java src/main/java/org/exist/util/Configuration.java src/test/java/org/exist/util/DOMSerializerTest.java + src/main/java/org/exist/util/EXistURISchemeURIResolver.java src/main/java/org/exist/util/IPUtil.java src/main/java/org/exist/util/JREUtil.java src/test/java/org/exist/util/LeasableTest.java @@ -2254,6 +2263,7 @@ src/test/java/org/exist/xquery/functions/validate/JingSchematronTest.java src/test/java/org/exist/xquery/functions/validate/JingXsdTest.java src/main/java/org/exist/xquery/functions/validation/Jaxp.java + src/main/java/org/exist/xquery/functions/validation/Shared.java src/test/java/org/exist/xquery/functions/xmldb/AbstractXMLDBTest.java src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java src/main/java/org/exist/xquery/functions/xmldb/FunXCollection.java @@ -2848,4 +2858,4 @@ The BaseX Team. The original license statement is also included below.]]> - + \ No newline at end of file diff --git a/exist-core/src/main/java/org/exist/dom/memtree/CDATASectionImpl.java b/exist-core/src/main/java/org/exist/dom/memtree/CDATASectionImpl.java index 1a2ac78056..84c204af13 100644 --- a/exist-core/src/main/java/org/exist/dom/memtree/CDATASectionImpl.java +++ b/exist-core/src/main/java/org/exist/dom/memtree/CDATASectionImpl.java @@ -1,4 +1,28 @@ /* + * Elemental + * Copyright (C) 2024, Evolved Binary Ltd + * + * admin@evolvedbinary.com + * https://www.evolvedbinary.com | https://www.elemental.xyz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * NOTE: Parts of this file contain code from 'The eXist-db Authors'. + * The original license header is included below. + * + * ===================================================================== + * * eXist-db Open Source Native XML Database * Copyright (C) 2001 The eXist-db Authors * @@ -25,8 +49,11 @@ import org.exist.xquery.value.Type; import org.w3c.dom.CDATASection; import org.w3c.dom.DOMException; +import org.w3c.dom.Node; import org.w3c.dom.Text; +import javax.annotation.Nullable; + /** * Represents a CDATA section. @@ -48,6 +75,15 @@ public int getItemType() { return Type.CDATA_SECTION; } + @Override + public @Nullable String getBaseURI() { + @Nullable final Node parent = getParentNode(); + if (parent == null) { + return null; + } + return parent.getBaseURI(); + } + @Override public Text splitText(final int offset) throws DOMException { return null; diff --git a/exist-core/src/main/java/org/exist/dom/persistent/AttrImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/AttrImpl.java index 6f2dff640c..a476298fad 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/AttrImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/AttrImpl.java @@ -57,6 +57,7 @@ import org.exist.util.XMLString; import org.exist.util.pool.NodePool; import org.exist.util.serializer.AttrList; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.Expression; import org.w3c.dom.*; @@ -425,7 +426,12 @@ public boolean isId() { public @Nullable String getBaseURI() { @Nullable final Element e = getOwnerElement(); if (e != null) { - return e.getBaseURI(); + @Nullable String strBaseUri = e.getBaseURI(); + if (strBaseUri != null && strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + strBaseUri; + } + return strBaseUri; } return null; } diff --git a/exist-core/src/main/java/org/exist/dom/persistent/CDATASectionImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/CDATASectionImpl.java index e89bbbba47..6ddede486c 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/CDATASectionImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/CDATASectionImpl.java @@ -1,4 +1,28 @@ /* + * Elemental + * Copyright (C) 2024, Evolved Binary Ltd + * + * admin@evolvedbinary.com + * https://www.evolvedbinary.com | https://www.elemental.xyz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * NOTE: Parts of this file contain code from 'The eXist-db Authors'. + * The original license header is included below. + * + * ===================================================================== + * * eXist-db Open Source Native XML Database * Copyright (C) 2001 The eXist-db Authors * @@ -27,12 +51,15 @@ import org.exist.util.ByteConversion; import org.exist.util.UTF8; import org.exist.util.XMLString; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.Expression; import org.w3c.dom.CDATASection; import org.w3c.dom.DOMException; import org.w3c.dom.Node; import org.w3c.dom.Text; +import javax.annotation.Nullable; + public class CDATASectionImpl extends AbstractCharacterData implements CDATASection { public CDATASectionImpl() { @@ -77,6 +104,20 @@ public CDATASectionImpl(final Expression expression, final String data) { } @Override + public @Nullable String getBaseURI() { + @Nullable final Node parent = getParentNode(); + if (parent != null) { + @Nullable String strBaseUri = parent.getBaseURI(); + if (strBaseUri != null && strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + strBaseUri; + } + return strBaseUri; + } else { + return null; + } + } + /** * Serializes a (persistent DOM) CDATA Section to a byte array * @@ -94,6 +135,7 @@ public CDATASectionImpl(final Expression expression, final String data) { * @return the returned byte array after use must be returned to the ByteArrayPool * by calling {@link ByteArrayPool#releaseByteArray(byte[])} */ + @Override public byte[] serialize() { final int nodeIdLen = nodeId.size(); final byte[] data = ByteArrayPool.getByteArray(LENGTH_SIGNATURE_LENGTH + NodeId.LENGTH_NODE_ID_UNITS + diff --git a/exist-core/src/main/java/org/exist/dom/persistent/CommentImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/CommentImpl.java index 9ad5f108cb..2fb227708c 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/CommentImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/CommentImpl.java @@ -49,6 +49,7 @@ import org.exist.storage.Signatures; import org.exist.util.ByteConversion; import org.exist.util.pool.NodePool; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.Expression; import org.w3c.dom.Comment; import org.w3c.dom.Node; @@ -92,7 +93,12 @@ public String toString() { public @Nullable String getBaseURI() { @Nullable final Node parent = getParentNode(); if (parent != null) { - return parent.getBaseURI(); + @Nullable String strBaseUri = parent.getBaseURI(); + if (strBaseUri != null && strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + strBaseUri; + } + return strBaseUri; } else { return null; } diff --git a/exist-core/src/main/java/org/exist/dom/persistent/DocumentImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/DocumentImpl.java index f8f55beffe..736a1e57df 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/DocumentImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/DocumentImpl.java @@ -1681,7 +1681,7 @@ public Node renameNode(final Node n, final String namespaceURI, final String qua @Override public String getBaseURI() { - return getURI().toString(); + return XmldbURI.EMBEDDED_SERVER_URI_PREFIX + getURI().getCollectionPath(); } @Override diff --git a/exist-core/src/main/java/org/exist/dom/persistent/ElementImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/ElementImpl.java index 24e3f489d4..fbb961e7b3 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/ElementImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/ElementImpl.java @@ -2003,7 +2003,12 @@ public void setIdAttributeNode(final Attr idAttr, final boolean isId) throws DOM public @Nullable String getBaseURI() { @Nullable final XmldbURI baseURI = calculateBaseURI(); if (baseURI != null) { - return baseURI.toString(); + String strBaseUri = baseURI.toString(); + if (strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + baseURI; + } + return strBaseUri; } return null; diff --git a/exist-core/src/main/java/org/exist/dom/persistent/ProcessingInstructionImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/ProcessingInstructionImpl.java index 6a42f27b0e..5c2d8124bb 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/ProcessingInstructionImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/ProcessingInstructionImpl.java @@ -50,6 +50,7 @@ import org.exist.storage.Signatures; import org.exist.util.ByteConversion; import org.exist.util.pool.NodePool; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.Expression; import org.w3c.dom.DOMException; import org.w3c.dom.Node; @@ -154,7 +155,12 @@ public void setData(final String data) { public @Nullable String getBaseURI() { @Nullable final StoredNode parent = getParentStoredNode(); if (parent != null) { - return parent.getBaseURI(); + @Nullable String strBaseUri = parent.getBaseURI(); + if (strBaseUri != null && strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + strBaseUri; + } + return strBaseUri; } else { return getOwnerDocument().getBaseURI(); } diff --git a/exist-core/src/main/java/org/exist/dom/persistent/TextImpl.java b/exist-core/src/main/java/org/exist/dom/persistent/TextImpl.java index 6c9d9c8971..a0ba26b9ce 100644 --- a/exist-core/src/main/java/org/exist/dom/persistent/TextImpl.java +++ b/exist-core/src/main/java/org/exist/dom/persistent/TextImpl.java @@ -52,6 +52,7 @@ import org.exist.util.ByteConversion; import org.exist.util.UTF8; import org.exist.util.pool.NodePool; +import org.exist.xmldb.XmldbURI; import org.exist.xquery.Expression; import org.w3c.dom.DOMException; import org.w3c.dom.Node; @@ -180,7 +181,12 @@ public Text splitText(final int offset) throws DOMException { public @Nullable String getBaseURI() { @Nullable final Node parent = getParentNode(); if (parent != null) { - return parent.getBaseURI(); + @Nullable String strBaseUri = parent.getBaseURI(); + if (strBaseUri != null && strBaseUri.startsWith("/db/")) { + // Must be a URI! + strBaseUri = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + strBaseUri; + } + return strBaseUri; } else { return null; } diff --git a/exist-core/src/main/java/org/exist/util/EXistURISchemeURIResolver.java b/exist-core/src/main/java/org/exist/util/EXistURISchemeURIResolver.java index f378dee2ef..41628cc190 100644 --- a/exist-core/src/main/java/org/exist/util/EXistURISchemeURIResolver.java +++ b/exist-core/src/main/java/org/exist/util/EXistURISchemeURIResolver.java @@ -1,4 +1,28 @@ /* + * Elemental + * Copyright (C) 2024, Evolved Binary Ltd + * + * admin@evolvedbinary.com + * https://www.evolvedbinary.com | https://www.elemental.xyz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * NOTE: Parts of this file contain code from 'The eXist-db Authors'. + * The original license header is included below. + * + * ===================================================================== + * * eXist-db Open Source Native XML Database * Copyright (C) 2001 The eXist-db Authors * @@ -56,6 +80,8 @@ private String rewriteScheme(String uri) { uri = uri.replace("exist://localhost/db", "/db"); } else if (uri.startsWith("exist://")) { uri = uri.replace("exist://", "xmldb:exist://"); + } else if (uri.startsWith("exist:/")) { + uri = uri.replace("exist:/", "xmldb:exist:///"); } } diff --git a/exist-core/src/main/java/org/exist/xquery/functions/validation/Shared.java b/exist-core/src/main/java/org/exist/xquery/functions/validation/Shared.java index 6f71d160db..31fa84a39a 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/validation/Shared.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/validation/Shared.java @@ -1,4 +1,28 @@ /* + * Elemental + * Copyright (C) 2024, Evolved Binary Ltd + * + * admin@evolvedbinary.com + * https://www.evolvedbinary.com | https://www.elemental.xyz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * NOTE: Parts of this file contain code from 'The eXist-db Authors'. + * The original license header is included below. + * + * ===================================================================== + * * eXist-db Open Source Native XML Database * Copyright (C) 2001 The eXist-db Authors * @@ -145,7 +169,7 @@ public static StreamSource getStreamSource(Item item, XQueryContext context) thr LOG.debug("Streaming element or document node"); if (item instanceof NodeProxy np) { - final String url = "xmldb:exist://" + np.getOwnerDocument().getBaseURI(); + final String url = np.getOwnerDocument().getBaseURI(); LOG.debug("Document detected, adding URL {}", url); streamSource.setSystemId(url); } diff --git a/exist-core/src/test/java/org/exist/xquery/XQueryTest.java b/exist-core/src/test/java/org/exist/xquery/XQueryTest.java index 09fb4b3c16..3eaba62ff0 100644 --- a/exist-core/src/test/java/org/exist/xquery/XQueryTest.java +++ b/exist-core/src/test/java/org/exist/xquery/XQueryTest.java @@ -2578,14 +2578,14 @@ public void resolveBaseURI_1841635() throws XMLDBException { ResourceSet result = service.query(query); assertEquals(1, result.getSize()); - assertEquals("/db/test/baseuri.xml", result.getResource(0).getContent().toString()); + assertEquals("xmldb:exist:///db/test/baseuri.xml", result.getResource(0).getContent().toString()); query = "doc('/db/test/baseuri.xml')/Root/Node1/base-uri()"; result = service.query(query); assertEquals(1, result.getSize()); - assertEquals("/db/test/baseuri.xml", result.getResource(0).getContent().toString()); + assertEquals("xmldb:exist:///db/test/baseuri.xml", result.getResource(0).getContent().toString()); query = "doc('/db/test/baseuri.xml')/Root/Node1/Node2/base-uri()"; @@ -2593,14 +2593,14 @@ public void resolveBaseURI_1841635() throws XMLDBException { result = service.query(query); assertEquals(1, result.getSize()); - assertEquals("/db/test/baseuri.xml", result.getResource(0).getContent().toString()); + assertEquals("xmldb:exist:///db/test/baseuri.xml", result.getResource(0).getContent().toString()); query = "doc('/db/test/baseuri.xml')/Root/Node1/Node2/Node3/base-uri()"; result = service.query(query); assertEquals(1, result.getSize()); - assertEquals("/db/test/baseuri.xml", result.getResource(0).getContent().toString()); + assertEquals("xmldb:exist:///db/test/baseuri.xml", result.getResource(0).getContent().toString()); } /** diff --git a/exist-core/src/test/xquery/base-uri.xql b/exist-core/src/test/xquery/base-uri.xql index 3aff020995..6c36ce40b7 100644 --- a/exist-core/src/test/xquery/base-uri.xql +++ b/exist-core/src/test/xquery/base-uri.xql @@ -118,7 +118,7 @@ function baseuri:cleanup() { declare - %test:assertEquals("-/db/base-uri/test.xml") + %test:assertEquals("-xmldb:exist:///db/base-uri/test.xml") function baseuri:root() { $baseuri:DOCUMENT/base-uri() || "-" || base-uri(doc($baseuri:full_path_document)) }; @@ -131,7 +131,7 @@ function baseuri:sub1() { }; declare - %test:assertEquals("http://example.org/a/","http://example.org/b/","http://example.org/c/","/yy","/db/base-uri/zz") + %test:assertEquals("http://example.org/a/","http://example.org/b/","http://example.org/c/","/yy","xmldb:exist:///db/base-uri/zz") function baseuri:sub2() { for $sub in doc($baseuri:full_path_document)//sub return base-uri($sub) diff --git a/exist-core/src/test/xquery/indexing/indexes-base-uri.xq b/exist-core/src/test/xquery/indexing/indexes-base-uri.xq index 52acaea094..3921c7b2df 100644 --- a/exist-core/src/test/xquery/indexing/indexes-base-uri.xq +++ b/exist-core/src/test/xquery/indexing/indexes-base-uri.xq @@ -1,4 +1,28 @@ (: + : Elemental + : Copyright (C) 2024, Evolved Binary Ltd + : + : admin@evolvedbinary.com + : https://www.evolvedbinary.com | https://www.elemental.xyz + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; version 2.1. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + : + : NOTE: Parts of this file contain code from 'The eXist-db Authors'. + : The original license header is included below. + : + : ===================================================================== + : : eXist-db Open Source Native XML Database : Copyright (C) 2001 The eXist-db Authors : @@ -50,7 +74,7 @@ function but:tearDown() { declare %test:pending("Each test interferes with each other test, we need to figure out how to have but:tearDown called after every test") - %test:assertEquals("/db/but/a/data/test.xml", "/db/but/a/data/test.xml", "/db/but/b/data/test.xml") + %test:assertEquals("xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/b/data/test.xml") function but:base-uri-after-collection-copy() { let $test-col-a-data-uri := xmldb:create-collection($but:test-col-a-uri, "data") let $doc-path := xmldb:store($test-col-a-data-uri, "test.xml", $but:XML) @@ -66,7 +90,7 @@ function but:base-uri-after-collection-copy() { }; declare - %test:assertEquals("/db/but/a/data/test.xml", "/db/but/b/data/test.xml") + %test:assertEquals("xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/b/data/test.xml") function but:base-uri-after-collection-move() { let $test-col-a-data-uri := xmldb:create-collection($but:test-col-a-uri, "data") let $doc-path := xmldb:store($test-col-a-data-uri, "test.xml", $but:XML) @@ -83,7 +107,7 @@ function but:base-uri-after-collection-move() { declare %test:pending("Each test interferes with each other test, we need to figure out how to have but:tearDown called after every test") - %test:assertEquals("/db/but/a/data/test.xml", "/db/but/a/data/test.xml", "/db/but/b/test.xml") + %test:assertEquals("xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/b/test.xml") function but:base-uri-after-resource-copy() { let $test-col-a-data-uri := xmldb:create-collection($but:test-col-a-uri, "data") let $doc-path := xmldb:store($test-col-a-data-uri, "test.xml", $but:XML) @@ -100,7 +124,7 @@ function but:base-uri-after-resource-copy() { declare %test:pending("Each test interferes with each other test, we need to figure out how to have but:tearDown called after every test") - %test:assertEquals("/db/but/a/data/test.xml", "/db/but/b/test.xml") + %test:assertEquals("xmldb:exist:///db/but/a/data/test.xml", "xmldb:exist:///db/but/b/test.xml") function but:base-uri-after-resource-move() { let $test-col-a-data-uri := xmldb:create-collection($but:test-col-a-uri, "data") let $doc-path := xmldb:store($test-col-a-data-uri, "test.xml", $but:XML) diff --git a/extensions/indexes/lucene/src/main/java/org/exist/indexing/lucene/AbstractFieldConfig.java b/extensions/indexes/lucene/src/main/java/org/exist/indexing/lucene/AbstractFieldConfig.java index 42c264fdce..76003da71f 100644 --- a/extensions/indexes/lucene/src/main/java/org/exist/indexing/lucene/AbstractFieldConfig.java +++ b/extensions/indexes/lucene/src/main/java/org/exist/indexing/lucene/AbstractFieldConfig.java @@ -172,20 +172,30 @@ protected CompiledXQuery compile(DBBroker broker, String code) { } } - private String resolveURI(String baseURI, String location) { + private String resolveURI(@Nullable final String strBaseUri, final String strLocation) { try { - final URI uri = new URI(location); - if (!uri.isAbsolute() && baseURI != null && baseURI.startsWith(CollectionConfigurationManager.CONFIG_COLLECTION)) { - String base = baseURI.substring(CollectionConfigurationManager.CONFIG_COLLECTION.length()); - final int lastSlash = base.lastIndexOf('/'); - if (lastSlash > -1) { - base = base.substring(0, lastSlash); + final URI uriLocation = new URI(strLocation); + if (!uriLocation.isAbsolute() && strBaseUri != null) { + if (strBaseUri.startsWith(CollectionConfigurationManager.CONFIG_COLLECTION)) { + String base = strBaseUri.substring(CollectionConfigurationManager.CONFIG_COLLECTION.length()); + final int lastSlash = base.lastIndexOf('/'); + if (lastSlash > -1) { + base = base.substring(0, lastSlash); + } + final String resolved = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + base + '/' + strLocation; + return resolved; + } + + if (strBaseUri.startsWith(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + CollectionConfigurationManager.CONFIG_COLLECTION)) { + final String basePath = strBaseUri.substring((XmldbURI.EMBEDDED_SERVER_URI_PREFIX + CollectionConfigurationManager.CONFIG_COLLECTION).length()); + final String locationPath = new URI(basePath).resolve(uriLocation).toString(); + final String resolved = XmldbURI.EMBEDDED_SERVER_URI_PREFIX + locationPath; + return resolved; } - return XmldbURI.EMBEDDED_SERVER_URI_PREFIX + base + '/' + location; } - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { // ignore and return location } - return location; + return strLocation; } } diff --git a/extensions/indexes/lucene/src/main/java/org/exist/xquery/modules/lucene/Query.java b/extensions/indexes/lucene/src/main/java/org/exist/xquery/modules/lucene/Query.java index 8d3d019b7a..f20460e987 100644 --- a/extensions/indexes/lucene/src/main/java/org/exist/xquery/modules/lucene/Query.java +++ b/extensions/indexes/lucene/src/main/java/org/exist/xquery/modules/lucene/Query.java @@ -258,7 +258,7 @@ public NodeSet preSelect(final Sequence contextSequence, final boolean useContex final LuceneIndexWorker index = (LuceneIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(LuceneIndex.ID); final DocumentSet docs = contextSequence.getDocumentSet(); - final Item key = getKey(contextSequence, null); + @Nullable final Item key = getKey(contextSequence, null); @Nullable final List qnames = contextQNames != null ? Arrays.asList(contextQNames) : null; final QueryOptions options = parseOptions(this, contextSequence, null, 3); try {