Skip to content

Commit d04c0a4

Browse files
committed
[feature] Add new system:get-main-module-load-path() XQuery function (required by TEI Publisher)
1 parent 9faaec1 commit d04c0a4

4 files changed

Lines changed: 270 additions & 3 deletions

File tree

exist-core/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@
778778
<include>src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java</include>
779779
<include>src/test/java/org/exist/xquery/functions/securitymanager/AccountMetadataFunctionsTest.java</include>
780780
<include>src/test/java/org/exist/xquery/functions/securitymanager/SecurityManagerTestUtil.java</include>
781+
<include>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</include>
781782
<include>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</include>
782783
<include>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</include>
783784
<include>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</include>
@@ -2447,6 +2448,7 @@
24472448
<exclude>src/test/java/org/exist/xquery/functions/session/AbstractSessionTest.java</exclude>
24482449
<exclude>src/test/java/org/exist/xquery/functions/session/AttributeTest.java</exclude>
24492450
<exclude>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</exclude>
2451+
<exclude>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</exclude>
24502452
<exclude>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</exclude>
24512453
<exclude>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</exclude>
24522454
<exclude>src/test/java/org/exist/xquery/functions/system/GetRunningXQueriesTest.java</exclude>

exist-core/src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.exist.xquery.value.StringValue;
2929
import org.exist.xquery.value.Type;
3030

31+
import javax.annotation.Nullable;
32+
3133
import static org.exist.xquery.FunctionDSL.returns;
3234
import static org.exist.xquery.functions.system.SystemModule.functionSignature;
3335

@@ -40,12 +42,30 @@ public class GetModuleLoadPath extends BasicFunction {
4042
returns(Type.STRING, "The path from which the module was loaded")
4143
);
4244

43-
public GetModuleLoadPath(final XQueryContext context) {
44-
super(context, FS_GET_MODULE_LOAD_PATH_);
45+
private static final String FS_GET_MAIN_MODULE_LOAD_PATH_NAME = "get-main-module-load-path";
46+
public static final FunctionSignature FS_GET_MAIN_MODULE_LOAD_PATH = functionSignature(
47+
FS_GET_MAIN_MODULE_LOAD_PATH_NAME,
48+
"Returns the path from which the main module was loaded. If called from a library module, it finds the path of the ancestor main module. Either a filesystem directory, or database collection. If the path cannot be determined, for example because the query module is in-memory then '.' is returned.",
49+
returns(Type.STRING, "The path from which the main module was loaded")
50+
);
51+
52+
public GetModuleLoadPath(final XQueryContext context, final FunctionSignature signature) {
53+
super(context, signature);
4554
}
4655

4756
@Override
4857
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
49-
return new StringValue(this, context.getModuleLoadPath());
58+
final String moduleLoadPath;
59+
if (isCalledAs(FS_GET_MODULE_LOAD_PATH_NAME)) {
60+
moduleLoadPath = context.getModuleLoadPath();
61+
} else {
62+
@Nullable final XQueryContext rootContext = context.getRootContext();
63+
if (rootContext == null) {
64+
// there may not be a main module, e.g. RESTXQ resource functions
65+
throw new XPathException(this, "There is no root context as the library module was executed via a named function");
66+
}
67+
moduleLoadPath = rootContext.getModuleLoadPath();
68+
}
69+
return new StringValue(this, moduleLoadPath);
5070
}
5171
}

exist-core/src/main/java/org/exist/xquery/functions/system/SystemModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public class SystemModule extends AbstractInternalModule {
8989
new FunctionDef(Shutdown.signatures[0], Shutdown.class),
9090
new FunctionDef(Shutdown.signatures[1], Shutdown.class),
9191
new FunctionDef(GetModuleLoadPath.FS_GET_MODULE_LOAD_PATH, GetModuleLoadPath.class),
92+
new FunctionDef(GetModuleLoadPath.FS_GET_MAIN_MODULE_LOAD_PATH, GetModuleLoadPath.class),
9293
new FunctionDef(TriggerSystemTask.signature, TriggerSystemTask.class),
9394
new FunctionDef(AsUser.FS_AS_USER, AsUser.class),
9495
new FunctionDef(AsUser.FS_FUNCTION_AS_USER, AsUser.class),
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
* admin@evolvedbinary.com
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
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; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
package org.exist.xquery.functions.system;
22+
23+
import com.evolvedbinary.j8fu.function.ConsumerE;
24+
import org.exist.EXistException;
25+
import org.exist.collections.Collection;
26+
import org.exist.security.PermissionDeniedException;
27+
import org.exist.source.DbUriSource;
28+
import org.exist.source.StringSource;
29+
import org.exist.storage.BrokerPool;
30+
import org.exist.storage.DBBroker;
31+
import org.exist.storage.txn.Txn;
32+
import org.exist.test.ExistEmbeddedServer;
33+
import org.exist.util.LockException;
34+
import org.exist.util.StringInputSource;
35+
import org.exist.xmldb.XmldbURI;
36+
import org.exist.xquery.XPathException;
37+
import org.exist.xquery.XQueryContext;
38+
import org.exist.xquery.XQueryUtil;
39+
import org.exist.xquery.value.Item;
40+
import org.exist.xquery.value.Type;
41+
import org.junit.BeforeClass;
42+
import org.junit.ClassRule;
43+
import org.junit.Test;
44+
import org.w3c.dom.Document;
45+
import org.xml.sax.SAXException;
46+
import org.xmlunit.builder.DiffBuilder;
47+
import org.xmlunit.builder.Input;
48+
import org.xmlunit.diff.Diff;
49+
50+
import javax.xml.transform.Source;
51+
import java.io.IOException;
52+
import java.util.Optional;
53+
54+
import static java.nio.charset.StandardCharsets.UTF_8;
55+
import static org.exist.test.Util.storeQuery;
56+
import static org.junit.Assert.assertEquals;
57+
import static org.junit.Assert.assertFalse;
58+
import static org.junit.Assert.assertNotNull;
59+
60+
public class GetMainModuleLoadPathTest {
61+
62+
private static XmldbURI TEST_COLLECTION_URI = XmldbURI.create("/db/get-main-module-load-path-test");
63+
private static XmldbURI TEST_SUB_COLLECTION_URI = TEST_COLLECTION_URI.append("sub1");
64+
65+
private static XmldbURI STANDALONE_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("standalone-main.xq");
66+
private static String STANDALONE_MAIN_MODULE_XQ =
67+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
68+
"document {\n" +
69+
" <path>{system:get-main-module-load-path()}</path>\n" +
70+
"}";
71+
72+
private static XmldbURI IMPORTED_LIBRARY_MODULE_URI = TEST_SUB_COLLECTION_URI.append("imported-library.xqm");
73+
private static String IMPORTED_LIBRARY_MODULE_XQ =
74+
"module namespace ilm = \"http://ilm\";\n" +
75+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
76+
"declare function ilm:main-module-load-path() {\n" +
77+
" system:get-main-module-load-path()\n" +
78+
"};";
79+
80+
private static XmldbURI IMPORTING_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("importing-main.xq");
81+
private static String IMPORTING_MAIN_MODULE_XQ =
82+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
83+
"import module namespace ilm = \"http://ilm\" at \"xmldb:exist://" + IMPORTED_LIBRARY_MODULE_URI.getXmldbURI().toString() + "\";\n" +
84+
"document {\n" +
85+
" <paths>\n" +
86+
" <path>{system:get-main-module-load-path()}</path>\n" +
87+
" <library-path>{ilm:main-module-load-path()}</library-path>\n" +
88+
" </paths>\n" +
89+
"}";
90+
91+
@ClassRule
92+
public static ExistEmbeddedServer EXIST_EMBEDDED_SERVER = new ExistEmbeddedServer(true, true);
93+
94+
@BeforeClass
95+
public static void setup() throws EXistException, PermissionDeniedException, IOException, SAXException, LockException {
96+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
97+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
98+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
99+
100+
// store xquery documents
101+
try (final Collection testCollection = broker.getOrCreateCollection(transaction, TEST_COLLECTION_URI)) {
102+
storeQuery(broker, transaction, new StringInputSource(STANDALONE_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, STANDALONE_MAIN_MODULE_URI);
103+
104+
try (final Collection testSubCollection = broker.getOrCreateCollection(transaction, TEST_SUB_COLLECTION_URI)) {
105+
storeQuery(broker, transaction, new StringInputSource(IMPORTED_LIBRARY_MODULE_XQ.getBytes(UTF_8)), testSubCollection, IMPORTED_LIBRARY_MODULE_URI);
106+
}
107+
108+
storeQuery(broker, transaction, new StringInputSource(IMPORTING_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, IMPORTING_MAIN_MODULE_URI);
109+
}
110+
111+
transaction.commit();
112+
}
113+
}
114+
115+
@Test
116+
public void standaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
117+
final String expected = "<path>.</path>";
118+
119+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
120+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
121+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
122+
123+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(STANDALONE_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
124+
assertNotNull(queryResult.result);
125+
assertEquals(1, queryResult.result.getItemCount());
126+
final Item item = queryResult.result.itemAt(0);
127+
assertEquals(Type.DOCUMENT, item.getType());
128+
129+
final Source expectedSrc = Input.fromString(expected).build();
130+
final Source actualSrc = Input.fromNode((Document) item).build();
131+
132+
final Diff diff = DiffBuilder.compare(expectedSrc)
133+
.withTest(actualSrc)
134+
.checkForSimilar()
135+
.build();
136+
137+
assertFalse(diff.toString(), diff.hasDifferences());
138+
}
139+
140+
transaction.commit();
141+
}
142+
}
143+
144+
@Test
145+
public void importingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
146+
final String expected =
147+
"<paths>" +
148+
"<path>.</path>" +
149+
"<library-path>.</library-path>" +
150+
"</paths>";
151+
152+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
153+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
154+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
155+
156+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(IMPORTING_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
157+
assertNotNull(queryResult.result);
158+
assertEquals(1, queryResult.result.getItemCount());
159+
final Item item = queryResult.result.itemAt(0);
160+
assertEquals(Type.DOCUMENT, item.getType());
161+
162+
final Source expectedSrc = Input.fromString(expected).build();
163+
final Source actualSrc = Input.fromNode((Document) item).build();
164+
165+
final Diff diff = DiffBuilder.compare(expectedSrc)
166+
.withTest(actualSrc)
167+
.checkForSimilar()
168+
.build();
169+
170+
assertFalse(diff.toString(), diff.hasDifferences());
171+
}
172+
173+
transaction.commit();
174+
}
175+
}
176+
177+
178+
@Test
179+
public void storedStandaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
180+
final String expected = "<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>";
181+
182+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
183+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
184+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
185+
186+
final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(STANDALONE_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());
187+
188+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, STANDALONE_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
189+
assertNotNull(queryResult.result);
190+
assertEquals(1, queryResult.result.getItemCount());
191+
final Item item = queryResult.result.itemAt(0);
192+
assertEquals(Type.DOCUMENT, item.getType());
193+
194+
final Source expectedSrc = Input.fromString(expected).build();
195+
final Source actualSrc = Input.fromNode((Document) item).build();
196+
197+
final Diff diff = DiffBuilder.compare(expectedSrc)
198+
.withTest(actualSrc)
199+
.checkForSimilar()
200+
.build();
201+
202+
assertFalse(diff.toString(), diff.hasDifferences());
203+
}
204+
205+
transaction.commit();
206+
}
207+
}
208+
209+
@Test
210+
public void storedImportingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
211+
final String expected =
212+
"<paths>" +
213+
"<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>" +
214+
"<library-path>" + TEST_COLLECTION_URI.getCollectionPath() + "</library-path>" +
215+
"</paths>";
216+
217+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
218+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
219+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
220+
221+
final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(IMPORTING_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());
222+
223+
224+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, IMPORTING_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
225+
assertNotNull(queryResult.result);
226+
assertEquals(1, queryResult.result.getItemCount());
227+
final Item item = queryResult.result.itemAt(0);
228+
assertEquals(Type.DOCUMENT, item.getType());
229+
230+
final Source expectedSrc = Input.fromString(expected).build();
231+
final Source actualSrc = Input.fromNode((Document) item).build();
232+
233+
final Diff diff = DiffBuilder.compare(expectedSrc)
234+
.withTest(actualSrc)
235+
.checkForSimilar()
236+
.build();
237+
238+
assertFalse(diff.toString(), diff.hasDifferences());
239+
}
240+
241+
transaction.commit();
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)