Skip to content

Commit a5a363c

Browse files
committed
[feature] Add support for setting XQuery external variables of type attribute() via the eXist-db REST API
1 parent 6a31fd8 commit a5a363c

File tree

6 files changed

+283
-24
lines changed

6 files changed

+283
-24
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,4 +1735,20 @@ public Node appendChild(final Node newChild) throws DOMException {
17351735

17361736
throw unsupported();
17371737
}
1738+
1739+
@Override
1740+
public String lookupNamespaceURI(final String prefix) {
1741+
if (prefix == null || prefix == XMLConstants.DEFAULT_NS_PREFIX) {
1742+
return XMLConstants.NULL_NS_URI;
1743+
}
1744+
1745+
for (int i = nextNamespace - 1; i >= 0; i--) {
1746+
final QName namespaceMapping = namespaceCode[i];
1747+
if (prefix.equals(namespaceMapping.getLocalPart())) {
1748+
return namespaceMapping.getNamespaceURI();
1749+
}
1750+
}
1751+
1752+
return null;
1753+
}
17381754
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ public boolean isDefaultNamespace(final String namespaceURI) {
921921

922922
@Override
923923
public String lookupNamespaceURI(final String prefix) {
924-
throw unsupported();
924+
return document.lookupNamespaceURI(prefix);
925925
}
926926

927927
@Override

exist-core/src/main/java/org/exist/xqj/Marshaller.java

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@
4848
import org.exist.dom.QName;
4949
import org.exist.dom.memtree.*;
5050
import org.exist.storage.DBBroker;
51+
import org.exist.storage.ElementValue;
5152
import org.exist.xquery.Expression;
5253
import org.exist.xquery.NameTest;
5354
import org.exist.xquery.XPathException;
5455
import org.exist.xquery.value.*;
55-
import org.w3c.dom.Attr;
5656
import org.w3c.dom.Comment;
5757
import org.w3c.dom.Document;
5858
import org.w3c.dom.Element;
@@ -63,6 +63,8 @@
6363
import org.xml.sax.SAXException;
6464
import org.xml.sax.helpers.AttributesImpl;
6565

66+
import javax.annotation.Nullable;
67+
import javax.xml.XMLConstants;
6668
import javax.xml.stream.XMLInputFactory;
6769
import javax.xml.stream.XMLStreamConstants;
6870
import javax.xml.stream.XMLStreamException;
@@ -74,6 +76,8 @@
7476
import java.io.StringReader;
7577
import java.util.Properties;
7678

79+
import static org.exist.util.StringUtil.nullIfEmpty;
80+
7781

7882
/**
7983
* A utility class that provides marshalling services for external variables and methods
@@ -100,6 +104,8 @@ public class Marshaller {
100104
private final static String ATTR_TYPE = "type";
101105
private final static String ATTR_ITEM_TYPE = "item-type";
102106

107+
private final static String ATTR_NAME = "name";
108+
103109
public final static QName ROOT_ELEMENT_QNAME = new QName(SEQ_ELEMENT, NAMESPACE, PREFIX);
104110

105111
/**
@@ -267,8 +273,45 @@ public static Sequence demarshall(final NodeImpl node) throws XMLStreamException
267273
type = Type.getType(typeName);
268274
}
269275

276+
// TODO(AR) coerce the item type to the type desired for the variable binding
277+
278+
@Nullable String attrNameString = null;
279+
if (sxValue instanceof Element) {
280+
attrNameString = nullIfEmpty(sxValue.getAttribute(ATTR_NAME));
281+
}
270282
Node item = sxValue.getFirstChild();
271-
if (Type.subTypeOf(type, Type.NODE)) {
283+
284+
if (type == Type.ATTRIBUTE || (type == Type.ITEM && attrNameString != null)) {
285+
if (attrNameString.isEmpty()) {
286+
throw new XMLStreamException("sx:value must contain a name attribute if type is " + typeName);
287+
}
288+
289+
final String attrPrefix;
290+
final String attrNamespace;
291+
final String attrLocalName;
292+
final int colonSep = attrNameString.indexOf(':');
293+
if (colonSep > -1) {
294+
attrPrefix = attrNameString.substring(0, colonSep);
295+
attrNamespace = item.lookupNamespaceURI(attrPrefix);
296+
if (attrNamespace == null) {
297+
throw new XMLStreamException("sx:value's name attribute contains the QName prefix '" + attrPrefix + "' for which the namespace has not been declared if type is " + typeName);
298+
}
299+
attrLocalName = attrNameString.substring(colonSep + 1);
300+
} else {
301+
attrPrefix = XMLConstants.DEFAULT_NS_PREFIX;
302+
attrNamespace = XMLConstants.NULL_NS_URI;
303+
attrLocalName = attrNameString;
304+
}
305+
306+
final QName attrName = new QName(attrLocalName, attrNamespace, attrPrefix, ElementValue.ATTRIBUTE);
307+
final MemTreeBuilder builder = new MemTreeBuilder();
308+
builder.startDocument();
309+
final int attrNodeNumber = builder.addAttribute(attrName, sxValue.getTextContent());
310+
builder.endDocument();
311+
final AttrImpl attr = (AttrImpl) builder.getDocument().getAttribute(attrNodeNumber);
312+
result.add(attr);
313+
314+
} else if (Type.subTypeOf(type, Type.NODE)) {
272315

273316
switch (type) {
274317
case Type.ELEMENT:
@@ -281,13 +324,6 @@ public static Sequence demarshall(final NodeImpl node) throws XMLStreamException
281324
}
282325
break;
283326

284-
case Type.ATTRIBUTE:
285-
if (!(item instanceof Attr)) {
286-
throw new XMLStreamException("sx:value should only contain an Attribute if type is " + typeName);
287-
}
288-
result.add((AttrImpl) item);
289-
break;
290-
291327
case Type.COMMENT:
292328
if (!(item instanceof Comment)) {
293329
throw new XMLStreamException("sx:value should only contain a Comment node if type is " + typeName);

exist-core/src/main/xsd/rest-serialized-sequence.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<xs:element name="value" minOccurs="0" maxOccurs="unbounded">
3434
<xs:complexType mixed="true">
3535
<xs:attribute name="type" type="xs:string" default="item()"/>
36+
<xs:attribute name="name" type="xs:string" use="optional"/>
3637
</xs:complexType>
3738
</xs:element>
3839
</xs:sequence>

0 commit comments

Comments
 (0)