Skip to content

Commit 7044baa

Browse files
committed
[feature] Add support for setting XQuery external variables of type document(), comment(), processing-instruction(), element(), attribute(), and text() via the eXist-db XML:DB API
1 parent 73f0161 commit 7044baa

File tree

17 files changed

+1522
-207
lines changed

17 files changed

+1522
-207
lines changed

exist-core/pom.xml

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -315,16 +315,9 @@
315315
<artifactId>xml-apis</artifactId>
316316
</dependency>
317317
<dependency>
318-
<groupId>org.apache.ws.commons.util</groupId>
318+
<groupId>com.evolvedbinary.thirdparty.org.apache.ws.commons.util</groupId>
319319
<artifactId>ws-commons-util</artifactId>
320-
<version>1.0.2</version>
321-
<exclusions>
322-
<exclusion>
323-
<!-- conflicts with Xerces dependency on xml-apis -->
324-
<groupId>xml-apis</groupId>
325-
<artifactId>xml-apis</artifactId>
326-
</exclusion>
327-
</exclusions>
320+
<version>1.1.0</version>
328321
</dependency>
329322

330323
<!-- xml-resolver is needed at runtime because xercesImpl declares this as optional,
@@ -412,12 +405,6 @@
412405
<dependency>
413406
<groupId>com.evolvedbinary.thirdparty.org.apache.xmlrpc</groupId>
414407
<artifactId>xmlrpc-common</artifactId>
415-
<exclusions>
416-
<exclusion> <!-- conflicts with Xerces dependency on xml-apis -->
417-
<groupId>xml-apis</groupId>
418-
<artifactId>xml-apis</artifactId>
419-
</exclusion>
420-
</exclusions>
421408
</dependency>
422409
<dependency>
423410
<groupId>com.evolvedbinary.thirdparty.org.apache.xmlrpc</groupId>
@@ -814,6 +801,7 @@
814801
<include>src/main/java/org/exist/dom/QName.java</include>
815802
<exclude>src/main/java/org/exist/dom/memtree/AbstractCharacterData.java</exclude>
816803
<include>src/main/java/org/exist/dom/memtree/AttrImpl.java</include>
804+
<include>src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java</include>
817805
<include>src/main/java/org/exist/dom/memtree/DocumentImpl.java</include>
818806
<include>src/test/java/org/exist/dom/memtree/DocumentImplTest.java</include>
819807
<include>src/main/java/org/exist/dom/memtree/DOMIndexer.java</include>
@@ -1355,6 +1343,7 @@
13551343
<exclude>src/main/java/org/exist/dom/QName.java</exclude>
13561344
<exclude>src/main/java/org/exist/dom/memtree/AbstractCharacterData.java</exclude>
13571345
<exclude>src/main/java/org/exist/dom/memtree/AttrImpl.java</exclude>
1346+
<exclude>src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java</exclude>
13581347
<exclude>src/main/java/org/exist/dom/memtree/DocumentImpl.java</exclude>
13591348
<exclude>src/test/java/org/exist/dom/memtree/DocumentImplTest.java</exclude>
13601349
<exclude>src/main/java/org/exist/dom/memtree/DocumentTypeImpl.java</exclude>

exist-core/src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
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+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -56,8 +80,11 @@ public class DocumentBuilderReceiver implements ContentHandler, LexicalHandler,
5680

5781
private boolean suppressWhitespace = true;
5882

83+
private StringBuilder cdataBuffer;
84+
5985
private final Expression expression;
6086

87+
6188
public DocumentBuilderReceiver() {
6289
this((Expression) null);
6390
}
@@ -190,12 +217,20 @@ public void addNamespaceNode(final QName qname) throws SAXException {
190217

191218
@Override
192219
public void characters(final CharSequence seq) throws SAXException {
193-
builder.characters(seq);
220+
if (cdataBuffer != null) {
221+
cdataBuffer.append(seq);
222+
} else {
223+
builder.characters(seq);
224+
}
194225
}
195226

196227
@Override
197228
public void characters(final char[] ch, final int start, final int len) throws SAXException {
198-
builder.characters(ch, start, len);
229+
if (cdataBuffer != null) {
230+
cdataBuffer.append(ch, start, len);
231+
} else {
232+
builder.characters(ch, start, len);
233+
}
199234
}
200235

201236
@Override
@@ -229,15 +264,14 @@ public void skippedEntity(final String name) throws SAXException {
229264
}
230265

231266
@Override
232-
public void endCDATA() throws SAXException {
233-
}
234-
235-
@Override
236-
public void endDTD() throws SAXException {
267+
public void startCDATA() throws SAXException {
268+
this.cdataBuffer = new StringBuilder();
237269
}
238270

239271
@Override
240-
public void startCDATA() throws SAXException {
272+
public void endCDATA() throws SAXException {
273+
builder.cdataSection(this.cdataBuffer);
274+
this.cdataBuffer = null;
241275
}
242276

243277
@Override
@@ -251,17 +285,21 @@ public void comment(final char[] ch, final int start, final int length) throws S
251285
}
252286

253287
@Override
254-
public void endEntity(final String name) throws SAXException {
288+
public void startEntity(final String name) throws SAXException {
255289
}
256290

257291
@Override
258-
public void startEntity(final String name) throws SAXException {
292+
public void endEntity(final String name) throws SAXException {
259293
}
260294

261295
@Override
262296
public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
263297
}
264298

299+
@Override
300+
public void endDTD() throws SAXException {
301+
}
302+
265303
@Override
266304
public void highlightText(final CharSequence seq) {
267305
// not supported with this receiver

exist-core/src/main/java/org/exist/dom/memtree/DocumentImpl.java

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,21 @@
9797
* for example {@code int nextNodeNum = next[nodeNum]}.
9898
*
9999
* The following arrays hold the data of the nodes themselves:
100-
* * {@link #namespaceParent}
101-
* * {@link #namespaceCode}
102-
* * {@link #nodeName}
103-
* * {@link #alpha}
104-
* * {@link #alphaLen}
105-
* * {@link #characters}
106-
* * {@link #nodeId}
107-
* * {@link #attrName}
108-
* * {@link #attrType}
109-
* * {@link #attrNodeId}
110-
* * {@link #attrParent}
111-
* * {@link #attrValue}
112-
* * {@link #references}
100+
* <ul>
101+
* <li>{@link #namespaceParent}</li>
102+
* <li>{@link #namespaceCode}</li>
103+
* <li>{@link #nodeName}</li>
104+
* <li>{@link #alpha}</li>
105+
* <li>{@link #alphaLen} For Element nodes, this is the ID of the first namespace for that Element. For Text, CData Section, Comment, and Processing Instruction nodes this is the lengh of their character data.</li>
106+
* <li>{@link #characters}</li>
107+
* <li>{@link #nodeId}</li>
108+
* <li>{@link #attrName}</li>
109+
* <li>{@link #attrType}</li>
110+
* <li>{@link #attrNodeId}</li>
111+
* <li>{@link #attrParent}</li>
112+
* <li>{@link #attrValue}</li>
113+
* <li>{@link #references}</li>
114+
* </ul>
113115
*
114116
* This implementation stores all node data in the document object. Nodes from another document, i.e. a persistent document in the database, can be
115117
* stored as reference nodes, i.e. the nodes are not copied into this document object. Instead a reference is inserted which will only be expanded
@@ -1751,4 +1753,45 @@ public String lookupNamespaceURI(final String prefix) {
17511753

17521754
return null;
17531755
}
1756+
1757+
/**
1758+
* Get the in-scope namespace URI for the namespace prefix.
1759+
*
1760+
* @param prefix the namespace prefix to lookup.
1761+
* @param nodeNumber the node to retrieve the in-scope namespace URI for.
1762+
*
1763+
* @return the namespace URI bound to the namespace prefix, or null if there is no such binding.
1764+
*/
1765+
public @Nullable String getInScopePrefix(String prefix, final int nodeNumber) {
1766+
if (prefix == null) {
1767+
prefix = XMLConstants.DEFAULT_NS_PREFIX;
1768+
}
1769+
1770+
// First, look at the Namespaces on the current node
1771+
if (alphaLen != null) {
1772+
int ns = alphaLen[nodeNumber];
1773+
if (ns != -1) {
1774+
while (ns < nextNamespace && namespaceParent[ns] == nodeNumber) {
1775+
final QName nsQName = namespaceCode[ns];
1776+
if (prefix.equals(nsQName.getPrefix())) {
1777+
return nsQName.getNamespaceURI();
1778+
}
1779+
++ns;
1780+
}
1781+
}
1782+
}
1783+
1784+
// Second, look at the parent, and so on...
1785+
if (next != null) {
1786+
int parent = next[nodeNumber];
1787+
while (parent > nodeNumber) {
1788+
parent = next[parent];
1789+
}
1790+
if (parent != -1) {
1791+
return getInScopePrefix(prefix, parent);
1792+
}
1793+
}
1794+
1795+
return null;
1796+
}
17541797
}

exist-core/src/main/java/org/exist/dom/memtree/MemTreeBuilder.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
import javax.xml.XMLConstants;
6464
import java.util.Arrays;
6565

66+
import static org.exist.util.StringUtil.nullIfEmpty;
67+
6668

6769
/**
6870
* Use this class to build a new in-memory DOM document.
@@ -515,14 +517,16 @@ public int namespaceNode(final QName qname) {
515517
}
516518

517519
public int namespaceNode(final QName qname, final boolean checkNS) {
520+
final String qnPrefix = qname.getPrefix() == null ? XMLConstants.DEFAULT_NS_PREFIX : qname.getPrefix();
521+
final String qnNs = qname.getNamespaceURI() == null ? XMLConstants.NULL_NS_URI : qname.getNamespaceURI();
522+
@Nullable final String qnLocalPart = nullIfEmpty(qname.getLocalPart());
523+
518524
final int lastNode = doc.getLastNode();
519-
boolean addNode = true;
520525
if(doc.nodeName != null) {
521526
final QName elemQN = doc.nodeName[lastNode];
522527
if(elemQN != null) {
523-
final String elemPrefix = (elemQN.getPrefix() == null) ? XMLConstants.DEFAULT_NS_PREFIX : elemQN.getPrefix();
524-
final String elemNs = (elemQN.getNamespaceURI() == null) ? XMLConstants.NULL_NS_URI : elemQN.getNamespaceURI();
525-
final String qnPrefix = (qname.getPrefix() == null) ? XMLConstants.DEFAULT_NS_PREFIX : qname.getPrefix();
528+
final String elemPrefix = elemQN.getPrefix() == null ? XMLConstants.DEFAULT_NS_PREFIX : elemQN.getPrefix();
529+
final String elemNs = elemQN.getNamespaceURI() == null ? XMLConstants.NULL_NS_URI : elemQN.getNamespaceURI();
526530
if (checkNS
527531
&& XMLConstants.DEFAULT_NS_PREFIX.equals(elemPrefix)
528532
&& XMLConstants.NULL_NS_URI.equals(elemNs)
@@ -534,12 +538,23 @@ public int namespaceNode(final QName qname, final boolean checkNS) {
534538
"Cannot output a namespace node for the default namespace when the element is in no namespace."
535539
);
536540
}
537-
if(elemPrefix.equals(qname.getLocalPart()) && (elemQN.getNamespaceURI() != null)) {
538-
addNode = false;
539-
}
540541
}
541542
}
542-
return (addNode ? doc.addNamespace(lastNode, qname) : -1);
543+
544+
final String prefix;
545+
if (XMLConstants.XMLNS_ATTRIBUTE.equals(qnPrefix) && qnLocalPart != null) {
546+
prefix = qnLocalPart;
547+
} else {
548+
prefix = XMLConstants.DEFAULT_NS_PREFIX;
549+
}
550+
551+
@Nullable final String existingNs = doc.getInScopePrefix(prefix, lastNode);
552+
if (!qnNs.equals(existingNs)) {
553+
return doc.addNamespace(lastNode, qname);
554+
555+
} else {
556+
return -1;
557+
}
543558
}
544559

545560

exist-core/src/main/java/org/exist/xmldb/AbstractRemoteResource.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ public abstract class AbstractRemoteResource extends AbstractRemote
8181
private String mimeType;
8282
protected final Optional<String> type;
8383

84+
// those are the different types of content this resource may have to deal with
8485
protected Path file = null;
85-
private ContentFile contentFile = null;
86+
protected ContentFile contentFile = null;
8687
protected InputSource inputSource = null;
88+
8789
private long contentLen = -1L;
8890
private Permission permissions = null;
8991
private boolean closed;
@@ -578,7 +580,7 @@ public final boolean isClosed() {
578580
}
579581

580582
@Override
581-
public final void close() {
583+
public void close() {
582584
if (!isClosed()) {
583585
try {
584586
file = null;

exist-core/src/main/java/org/exist/xmldb/LocalXMLResource.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ public class LocalXMLResource extends AbstractEXistResource implements XMLResour
103103
private Properties outputProperties;
104104
private LexicalHandler lexicalHandler = null;
105105

106-
// those are the different types of content this resource
107-
// may have to deal with
106+
// those are the different types of content this resource may have to deal with
108107
protected String content = null;
109108
protected Path file = null;
110109
protected InputSource inputSource = null;

0 commit comments

Comments
 (0)