Skip to content

Commit e182fad

Browse files
committed
[feature] Add support for setting XQuery external variables of type array(*) via the eXist-db XML:DB API
1 parent dd793f4 commit e182fad

File tree

6 files changed

+536
-16
lines changed

6 files changed

+536
-16
lines changed

exist-core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@
753753
<include>src/main/java/org/exist/util/StringUtil.java</include>
754754
<include>src/test/java/org/exist/util/serializer/SAXSerializerTest.java</include>
755755
<include>src/test/java/org/exist/xmldb/XMLDBExternalVariableTest.java</include>
756+
<include>src/main/java/org/exist/xmlrpc/ArrayWrapperParser.java</include>
757+
<include>src/main/java/org/exist/xmlrpc/ArrayWrapperSerializer.java</include>
758+
<include>src/main/java/org/exist/xmlrpc/XmlRpcExtensionConstants.java</include>
756759
<include>src/test/resources-filtered/org/exist/xquery/import-from-pkg-test.conf.xml</include>
757760
<include>src/test/java/org/exist/xquery/ImportFromPkgTest.java</include>
758761
<include>src/main/java/org/exist/xquery/JavaBinding.java</include>
@@ -1708,9 +1711,12 @@
17081711
<exclude>src/test/java/org/exist/xmldb/concurrent/action/MultiResourcesAction.java</exclude>
17091712
<exclude>src/main/java/org/exist/xmlrpc/ACEAiderParser.java</exclude>
17101713
<exclude>src/main/java/org/exist/xmlrpc/ACEAiderSerializer.java</exclude>
1714+
<exclude>src/main/java/org/exist/xmlrpc/ArrayWrapperParser.java</exclude>
1715+
<exclude>src/main/java/org/exist/xmlrpc/ArrayWrapperSerializer.java</exclude>
17111716
<exclude>src/main/java/org/exist/xmlrpc/ExistRpcTypeFactory.java</exclude>
17121717
<exclude>src/main/java/org/exist/xmlrpc/RpcAPI.java</exclude>
17131718
<exclude>src/main/java/org/exist/xmlrpc/RpcConnection.java</exclude>
1719+
<exclude>src/main/java/org/exist/xmlrpc/XmlRpcExtensionConstants.java</exclude>
17141720
<exclude>src/main/java/org/exist/xqj/Marshaller.java</exclude>
17151721
<exclude>src/test/java/org/exist/xqj/MarshallerTest.java</exclude>
17161722
<exclude>src/main/java/org/exist/xquery/AbstractInternalModule.java</exclude>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
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+
package org.exist.xmlrpc;
22+
23+
import org.apache.ws.commons.util.NamespaceContextImpl;
24+
import org.apache.xmlrpc.common.TypeFactory;
25+
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
26+
import org.apache.xmlrpc.parser.RecursiveTypeParserImpl;
27+
import org.exist.xquery.value.ArrayWrapper;
28+
import org.xml.sax.Attributes;
29+
import org.xml.sax.SAXException;
30+
import org.xml.sax.SAXParseException;
31+
32+
import javax.xml.XMLConstants;
33+
import javax.xml.namespace.QName;
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
37+
import static org.apache.xmlrpc.serializer.TypeSerializerImpl.VALUE_TAG;
38+
import static org.exist.xmlrpc.ArrayWrapperSerializer.*;
39+
import static org.exist.xmlrpc.XmlRpcExtensionConstants.NS_XDM;
40+
41+
/**
42+
* XML-RPC type parser for objects of
43+
* {@link org.exist.xquery.value.ArrayWrapper}.
44+
*
45+
* @author <a href="mailto:[email protected]">Adam Retter</a>
46+
*/
47+
class ArrayWrapperParser extends RecursiveTypeParserImpl {
48+
49+
private int level = 0;
50+
private List<Object> list;
51+
52+
ArrayWrapperParser(final XmlRpcStreamConfig config, final NamespaceContextImpl context, final TypeFactory factory) {
53+
super(config, context, factory);
54+
}
55+
56+
/**
57+
* Return true if this parser can parse an Element with the given namespace and name.
58+
*
59+
* @param namespaceUri the URI of the Element's namespace.
60+
* @param localName the local name of the Element.
61+
*
62+
* @return true if this parser can parse the Element, false otherwise.
63+
*/
64+
public static boolean canParseElement(final String namespaceUri, final String localName) {
65+
return NS_XDM.equals(namespaceUri) && ARRAY_ELEM_NAME.equals(localName);
66+
}
67+
68+
@Override
69+
public void startDocument() throws SAXException {
70+
this.level = 0;
71+
this.list = new ArrayList<>();
72+
super.startDocument();
73+
}
74+
75+
@Override
76+
protected void addResult(final Object pValue) {
77+
list.add(pValue);
78+
}
79+
80+
@Override
81+
public void endElement(final String pURI, final String pLocalName, final String pQName) throws SAXException {
82+
switch (--level) {
83+
case 0:
84+
setResult(new ArrayWrapper<>(list.toArray()));
85+
break;
86+
case 1:
87+
break;
88+
case 2:
89+
endValueTag();
90+
break;
91+
default:
92+
super.endElement(pURI, pLocalName, pQName);
93+
}
94+
}
95+
96+
@Override
97+
public void startElement(final String pURI, final String pLocalName, final String pQName, final Attributes pAttrs) throws SAXException {
98+
switch (level++) {
99+
case 0:
100+
if (!NS_XDM.equals(pURI) || !ARRAY_ELEM_NAME.equals(pLocalName)) {
101+
throw new SAXParseException("Expected " + ARRAY_TAG + " element, got " + new QName(pURI, pLocalName), getDocumentLocator());
102+
}
103+
break;
104+
case 1:
105+
if (!XMLConstants.NULL_NS_URI.equals(pURI) || !DATA_TAG.equals(pLocalName)) {
106+
throw new SAXParseException("Expected data element, got "
107+
+ new QName(pURI, pLocalName),
108+
getDocumentLocator());
109+
}
110+
break;
111+
case 2:
112+
if (!XMLConstants.NULL_NS_URI.equals(pURI) || !VALUE_TAG.equals(pLocalName)) {
113+
throw new SAXParseException("Expected data element, got "
114+
+ new QName(pURI, pLocalName),
115+
getDocumentLocator());
116+
}
117+
startValueTag();
118+
break;
119+
default:
120+
super.startElement(pURI, pLocalName, pQName, pAttrs);
121+
break;
122+
}
123+
}
124+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
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+
package org.exist.xmlrpc;
22+
23+
import org.apache.xmlrpc.common.TypeFactory;
24+
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
25+
import org.apache.xmlrpc.serializer.SerializerHandler;
26+
import org.apache.xmlrpc.serializer.TypeSerializer;
27+
import org.apache.xmlrpc.serializer.TypeSerializerImpl;
28+
import org.exist.xquery.value.ArrayWrapper;
29+
import org.xml.sax.SAXException;
30+
31+
import static org.exist.xmlrpc.XmlRpcExtensionConstants.NS_XDM;
32+
import static org.exist.xmlrpc.XmlRpcExtensionConstants.PREFIX_XDM;
33+
34+
/**
35+
* XML-RPC type serializer for objects of
36+
* {@link org.exist.xquery.value.ArrayWrapper}.
37+
*
38+
* @author <a href="mailto:[email protected]">Adam Retter</a>
39+
*/
40+
class ArrayWrapperSerializer extends TypeSerializerImpl {
41+
42+
public static final String ARRAY_ELEM_NAME = "array";
43+
public static final String ARRAY_TAG = PREFIX_XDM + ':' + ARRAY_ELEM_NAME;
44+
public static final String DATA_TAG = "data";
45+
46+
private final TypeFactory typeFactory;
47+
private final XmlRpcStreamConfig config;
48+
49+
ArrayWrapperSerializer(final TypeFactory typeFactory, final XmlRpcStreamConfig config) {
50+
this.typeFactory = typeFactory;
51+
this.config = config;
52+
}
53+
54+
private void writeObject(final SerializerHandler pHandler, final Object pObject) throws SAXException {
55+
final TypeSerializer ts = typeFactory.getSerializer(config, pObject);
56+
if (ts == null) {
57+
throw new SAXException("Unsupported Java type: " + pObject.getClass().getName());
58+
}
59+
ts.write(pHandler, pObject);
60+
}
61+
62+
private void writeData(final SerializerHandler pHandler, final Object pObject) throws SAXException {
63+
final Object[] data = ((ArrayWrapper) pObject).array;
64+
for (int i = 0; i < data.length; i++) {
65+
writeObject(pHandler, data[i]);
66+
}
67+
}
68+
69+
@Override
70+
public void write(final SerializerHandler pHandler, final Object pObject) throws SAXException {
71+
pHandler.startElement("", VALUE_TAG, VALUE_TAG, ZERO_ATTRIBUTES);
72+
pHandler.startPrefixMapping(PREFIX_XDM, NS_XDM);
73+
pHandler.startElement(NS_XDM, ARRAY_ELEM_NAME, ARRAY_TAG, ZERO_ATTRIBUTES);
74+
pHandler.startElement("", DATA_TAG, DATA_TAG, ZERO_ATTRIBUTES);
75+
writeData(pHandler, pObject);
76+
pHandler.endElement("", DATA_TAG, DATA_TAG);
77+
pHandler.endElement(NS_XDM, ARRAY_ELEM_NAME, ARRAY_TAG);
78+
pHandler.endPrefixMapping(PREFIX_XDM);
79+
pHandler.endElement("", VALUE_TAG, VALUE_TAG);
80+
}
81+
}

exist-core/src/main/java/org/exist/xmlrpc/ExistRpcTypeFactory.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.apache.xmlrpc.parser.TypeParser;
5454
import org.apache.xmlrpc.serializer.TypeSerializer;
5555
import org.exist.security.internal.aider.ACEAider;
56+
import org.exist.xquery.value.ArrayWrapper;
5657
import org.xml.sax.SAXException;
5758

5859
/**
@@ -74,6 +75,9 @@ public TypeParser getParser(final XmlRpcStreamConfig config, final NamespaceCont
7475
} else if (ACEAiderSerializer.ACEAIDER_TAG.equals(localName)) {
7576
return new ACEAiderParser(config, context, this);
7677

78+
} else if (ArrayWrapperParser.canParseElement(uri, localName)) {
79+
return new ArrayWrapperParser(config, context, this);
80+
7781
} else {
7882
return super.getParser(config, context, uri, localName);
7983
}
@@ -87,6 +91,9 @@ public TypeSerializer getSerializer(final XmlRpcStreamConfig config, final Objec
8791
} else if (object instanceof ACEAider) {
8892
return new ACEAiderSerializer(this, config);
8993

94+
} else if (object instanceof ArrayWrapper) {
95+
return new ArrayWrapperSerializer(this, config);
96+
9097
} else {
9198
return super.getSerializer(config, object);
9299
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
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+
package org.exist.xmlrpc;
22+
23+
public interface XmlRpcExtensionConstants {
24+
String NS_XDM = "http://ns.elemental.xyz/xmlrpc/extensions/xdm";
25+
String PREFIX_XDM = "xdm";
26+
}

0 commit comments

Comments
 (0)