Skip to content

Commit 816a2c6

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 ebdc3d0 commit 816a2c6

File tree

17 files changed

+1513
-206
lines changed

17 files changed

+1513
-206
lines changed

exist-core/pom.xml

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,9 @@
355355
-->
356356

357357
<dependency>
358-
<groupId>org.apache.ws.commons.util</groupId>
358+
<groupId>com.evolvedbinary.thirdparty.org.apache.ws.commons.util</groupId>
359359
<artifactId>ws-commons-util</artifactId>
360-
<version>1.0.2</version>
361-
<exclusions>
362-
<exclusion> <!-- conflicts with Xerces dependency on xml-apis -->
363-
<groupId>xml-apis</groupId>
364-
<artifactId>xml-apis</artifactId>
365-
</exclusion>
366-
</exclusions>
360+
<version>1.1.0</version>
367361
</dependency>
368362

369363
<!-- xml-resolver is needed at runtime because xercesImpl declares this as optional,
@@ -453,12 +447,6 @@
453447
<dependency>
454448
<groupId>com.evolvedbinary.thirdparty.org.apache.xmlrpc</groupId>
455449
<artifactId>xmlrpc-common</artifactId>
456-
<exclusions>
457-
<exclusion> <!-- conflicts with Xerces dependency on xml-apis -->
458-
<groupId>xml-apis</groupId>
459-
<artifactId>xml-apis</artifactId>
460-
</exclusion>
461-
</exclusions>
462450
</dependency>
463451
<dependency>
464452
<groupId>com.evolvedbinary.thirdparty.org.apache.xmlrpc</groupId>
@@ -868,6 +856,7 @@
868856
<include>src/main/java/org/exist/dom/QName.java</include>
869857
<exclude>src/main/java/org/exist/dom/memtree/AbstractCharacterData.java</exclude>
870858
<include>src/main/java/org/exist/dom/memtree/AttrImpl.java</include>
859+
<include>src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java</include>
871860
<include>src/main/java/org/exist/dom/memtree/DocumentImpl.java</include>
872861
<include>src/test/java/org/exist/dom/memtree/DocumentImplTest.java</include>
873862
<include>src/main/java/org/exist/dom/memtree/DOMIndexer.java</include>
@@ -1419,6 +1408,7 @@
14191408
<exclude>src/main/java/org/exist/dom/QName.java</exclude>
14201409
<exclude>src/main/java/org/exist/dom/memtree/AbstractCharacterData.java</exclude>
14211410
<exclude>src/main/java/org/exist/dom/memtree/AttrImpl.java</exclude>
1411+
<exclude>src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java</exclude>
14221412
<exclude>src/main/java/org/exist/dom/memtree/DocumentImpl.java</exclude>
14231413
<exclude>src/test/java/org/exist/dom/memtree/DocumentImplTest.java</exclude>
14241414
<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+
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
}
@@ -195,12 +222,20 @@ public void addNamespaceNode(final QName qname) throws SAXException {
195222

196223
@Override
197224
public void characters(final CharSequence seq) throws SAXException {
198-
builder.characters(seq);
225+
if (cdataBuffer != null) {
226+
cdataBuffer.append(seq);
227+
} else {
228+
builder.characters(seq);
229+
}
199230
}
200231

201232
@Override
202233
public void characters(final char[] ch, final int start, final int len) throws SAXException {
203-
builder.characters(ch, start, len);
234+
if (cdataBuffer != null) {
235+
cdataBuffer.append(ch, start, len);
236+
} else {
237+
builder.characters(ch, start, len);
238+
}
204239
}
205240

206241
@Override
@@ -234,15 +269,14 @@ public void skippedEntity(final String name) throws SAXException {
234269
}
235270

236271
@Override
237-
public void endCDATA() throws SAXException {
238-
}
239-
240-
@Override
241-
public void endDTD() throws SAXException {
272+
public void startCDATA() throws SAXException {
273+
this.cdataBuffer = new StringBuilder();
242274
}
243275

244276
@Override
245-
public void startCDATA() throws SAXException {
277+
public void endCDATA() throws SAXException {
278+
builder.cdataSection(this.cdataBuffer);
279+
this.cdataBuffer = null;
246280
}
247281

248282
@Override
@@ -256,17 +290,21 @@ public void comment(final char[] ch, final int start, final int length) throws S
256290
}
257291

258292
@Override
259-
public void endEntity(final String name) throws SAXException {
293+
public void startEntity(final String name) throws SAXException {
260294
}
261295

262296
@Override
263-
public void startEntity(final String name) throws SAXException {
297+
public void endEntity(final String name) throws SAXException {
264298
}
265299

266300
@Override
267301
public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
268302
}
269303

304+
@Override
305+
public void endDTD() throws SAXException {
306+
}
307+
270308
@Override
271309
public void highlightText(final CharSequence seq) {
272310
// 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
@@ -62,6 +62,8 @@
6262
import javax.xml.XMLConstants;
6363
import java.util.Arrays;
6464

65+
import static org.exist.util.StringUtil.nullIfEmpty;
66+
6567

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

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

544559

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

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

83+
// those are the different types of content this resource may have to deal with
8384
protected Path file = null;
84-
private ContentFile contentFile = null;
85+
protected ContentFile contentFile = null;
8586
protected InputSource inputSource = null;
87+
8688
private long contentLen = -1L;
8789
private Permission permissions = null;
8890
private boolean closed;
@@ -577,7 +579,7 @@ public final boolean isClosed() {
577579
}
578580

579581
@Override
580-
public final void close() {
582+
public void close() {
581583
if (!isClosed()) {
582584
try {
583585
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
@@ -112,8 +112,7 @@ public class LocalXMLResource extends AbstractEXistResource implements XMLResour
112112
private Properties outputProperties;
113113
private LexicalHandler lexicalHandler = null;
114114

115-
// those are the different types of content this resource
116-
// may have to deal with
115+
// those are the different types of content this resource may have to deal with
117116
protected String content = null;
118117
protected Path file = null;
119118
protected InputSource inputSource = null;

0 commit comments

Comments
 (0)