4848import org .exist .dom .QName ;
4949import org .exist .dom .memtree .*;
5050import org .exist .storage .DBBroker ;
51+ import org .exist .storage .ElementValue ;
5152import org .exist .xquery .Expression ;
5253import org .exist .xquery .NameTest ;
5354import org .exist .xquery .XPathException ;
5455import org .exist .xquery .value .*;
55- import org .w3c .dom .Attr ;
5656import org .w3c .dom .Comment ;
5757import org .w3c .dom .Document ;
5858import org .w3c .dom .Element ;
6363import org .xml .sax .SAXException ;
6464import org .xml .sax .helpers .AttributesImpl ;
6565
66+ import javax .annotation .Nullable ;
67+ import javax .xml .XMLConstants ;
6668import javax .xml .stream .XMLInputFactory ;
6769import javax .xml .stream .XMLStreamConstants ;
6870import javax .xml .stream .XMLStreamException ;
7476import java .io .StringReader ;
7577import 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 );
0 commit comments