2 * XPath/XSLPattern query result node list implementation
4 * Copyright 2005 Mike McCormack
5 * Copyright 2007 Mikolaj Zalewski
6 * Copyright 2010 Adam Martinson for CodeWeavers
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; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 # include <libxml/parser.h>
30 # include <libxml/xmlerror.h>
31 # include <libxml/xpath.h>
32 # include <libxml/xpathInternals.h>
40 #include "msxml2did.h"
42 #include "msxml_private.h"
44 #include "wine/debug.h"
46 /* This file implements the object returned by a XPath query. Note that this is
47 * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
48 * They are different because the list returned by XPath queries:
49 * - is static - gives the results for the XML tree as it existed during the
50 * execution of the query
51 * - supports IXMLDOMSelection
55 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
59 int registerNamespaces(xmlXPathContextPtr ctxt);
60 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
62 typedef struct _enumvariant
64 IEnumVARIANT IEnumVARIANT_iface;
67 IXMLDOMSelection *selection;
73 typedef struct _domselection
76 IXMLDOMSelection IXMLDOMSelection_iface;
79 xmlXPathObjectPtr result;
81 IEnumVARIANT *enumvariant;
84 static inline domselection *impl_from_IXMLDOMSelection( IXMLDOMSelection *iface )
86 return CONTAINING_RECORD(iface, domselection, IXMLDOMSelection_iface);
89 static inline enumvariant *impl_from_IEnumVARIANT( IEnumVARIANT *iface )
91 return CONTAINING_RECORD(iface, enumvariant, IEnumVARIANT_iface);
94 static HRESULT create_enumvariant(IXMLDOMSelection*, BOOL, IUnknown**);
96 static HRESULT WINAPI domselection_QueryInterface(
97 IXMLDOMSelection *iface,
101 domselection *This = impl_from_IXMLDOMSelection( iface );
103 TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
108 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
109 IsEqualGUID( riid, &IID_IXMLDOMNodeList ) ||
110 IsEqualGUID( riid, &IID_IXMLDOMSelection ))
112 *ppvObject = &This->IXMLDOMSelection_iface;
114 else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
116 if (!This->enumvariant)
118 HRESULT hr = create_enumvariant(iface, FALSE, (IUnknown**)&This->enumvariant);
119 if (FAILED(hr)) return hr;
122 return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
124 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
126 return *ppvObject ? S_OK : E_NOINTERFACE;
130 TRACE("interface %s not implemented\n", debugstr_guid(riid));
132 return E_NOINTERFACE;
135 IXMLDOMSelection_AddRef( iface );
140 static ULONG WINAPI domselection_AddRef(
141 IXMLDOMSelection *iface )
143 domselection *This = impl_from_IXMLDOMSelection( iface );
144 ULONG ref = InterlockedIncrement( &This->ref );
145 TRACE("(%p)->(%d)\n", This, ref);
149 static ULONG WINAPI domselection_Release(
150 IXMLDOMSelection *iface )
152 domselection *This = impl_from_IXMLDOMSelection( iface );
153 ULONG ref = InterlockedDecrement(&This->ref);
155 TRACE("(%p)->(%d)\n", This, ref);
158 xmlXPathFreeObject(This->result);
159 xmldoc_release(This->node->doc);
160 if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
161 release_dispex(&This->dispex);
168 static HRESULT WINAPI domselection_GetTypeInfoCount(
169 IXMLDOMSelection *iface,
172 domselection *This = impl_from_IXMLDOMSelection( iface );
174 TRACE("(%p)->(%p)\n", This, pctinfo);
181 static HRESULT WINAPI domselection_GetTypeInfo(
182 IXMLDOMSelection *iface,
185 ITypeInfo** ppTInfo )
187 domselection *This = impl_from_IXMLDOMSelection( iface );
189 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
191 return get_typeinfo(IXMLDOMSelection_tid, ppTInfo);
194 static HRESULT WINAPI domselection_GetIDsOfNames(
195 IXMLDOMSelection *iface,
202 domselection *This = impl_from_IXMLDOMSelection( iface );
206 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
209 if(!rgszNames || cNames == 0 || !rgDispId)
212 hr = get_typeinfo(IXMLDOMSelection_tid, &typeinfo);
215 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
216 ITypeInfo_Release(typeinfo);
222 static HRESULT WINAPI domselection_Invoke(
223 IXMLDOMSelection *iface,
228 DISPPARAMS* pDispParams,
230 EXCEPINFO* pExcepInfo,
233 domselection *This = impl_from_IXMLDOMSelection( iface );
237 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
238 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
240 hr = get_typeinfo(IXMLDOMSelection_tid, &typeinfo);
243 hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDOMSelection_iface, dispIdMember, wFlags, pDispParams,
244 pVarResult, pExcepInfo, puArgErr);
245 ITypeInfo_Release(typeinfo);
251 static HRESULT WINAPI domselection_get_item(
252 IXMLDOMSelection* iface,
254 IXMLDOMNode** listItem)
256 domselection *This = impl_from_IXMLDOMSelection( iface );
258 TRACE("(%p)->(%d %p)\n", This, index, listItem);
265 if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
268 *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
269 This->resultPos = index + 1;
274 static HRESULT WINAPI domselection_get_length(
275 IXMLDOMSelection* iface,
278 domselection *This = impl_from_IXMLDOMSelection( iface );
280 TRACE("(%p)->(%p)\n", This, listLength);
285 *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
289 static HRESULT WINAPI domselection_nextNode(
290 IXMLDOMSelection* iface,
291 IXMLDOMNode** nextItem)
293 domselection *This = impl_from_IXMLDOMSelection( iface );
295 TRACE("(%p)->(%p)\n", This, nextItem );
302 if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
305 *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
310 static HRESULT WINAPI domselection_reset(
311 IXMLDOMSelection* iface)
313 domselection *This = impl_from_IXMLDOMSelection( iface );
320 static HRESULT WINAPI domselection_get__newEnum(
321 IXMLDOMSelection* iface,
324 domselection *This = impl_from_IXMLDOMSelection( iface );
326 TRACE("(%p)->(%p)\n", This, ppUnk);
328 return create_enumvariant(iface, TRUE, ppUnk);
331 static HRESULT WINAPI domselection_get_expr(
332 IXMLDOMSelection* iface,
335 domselection *This = impl_from_IXMLDOMSelection( iface );
336 FIXME("(%p)->(%p)\n", This, p);
340 static HRESULT WINAPI domselection_put_expr(
341 IXMLDOMSelection* iface,
344 domselection *This = impl_from_IXMLDOMSelection( iface );
345 FIXME("(%p)->(%s)\n", This, debugstr_w(p));
349 static HRESULT WINAPI domselection_get_context(
350 IXMLDOMSelection* iface,
353 domselection *This = impl_from_IXMLDOMSelection( iface );
354 FIXME("(%p)->(%p)\n", This, node);
358 static HRESULT WINAPI domselection_putref_context(
359 IXMLDOMSelection* iface,
362 domselection *This = impl_from_IXMLDOMSelection( iface );
363 FIXME("(%p)->(%p)\n", This, node);
367 static HRESULT WINAPI domselection_peekNode(
368 IXMLDOMSelection* iface,
371 domselection *This = impl_from_IXMLDOMSelection( iface );
372 FIXME("(%p)->(%p)\n", This, node);
376 static HRESULT WINAPI domselection_matches(
377 IXMLDOMSelection* iface,
379 IXMLDOMNode **out_node)
381 domselection *This = impl_from_IXMLDOMSelection( iface );
382 FIXME("(%p)->(%p %p)\n", This, node, out_node);
386 static HRESULT WINAPI domselection_removeNext(
387 IXMLDOMSelection* iface,
390 domselection *This = impl_from_IXMLDOMSelection( iface );
391 FIXME("(%p)->(%p)\n", This, node);
395 static HRESULT WINAPI domselection_removeAll(
396 IXMLDOMSelection* iface)
398 domselection *This = impl_from_IXMLDOMSelection( iface );
399 FIXME("(%p)\n", This);
403 static HRESULT WINAPI domselection_clone(
404 IXMLDOMSelection* iface,
405 IXMLDOMSelection **node)
407 domselection *This = impl_from_IXMLDOMSelection( iface );
408 FIXME("(%p)->(%p)\n", This, node);
412 static HRESULT WINAPI domselection_getProperty(
413 IXMLDOMSelection* iface,
417 domselection *This = impl_from_IXMLDOMSelection( iface );
418 FIXME("(%p)->(%s %p)\n", This, debugstr_w(p), var);
422 static HRESULT WINAPI domselection_setProperty(
423 IXMLDOMSelection* iface,
427 domselection *This = impl_from_IXMLDOMSelection( iface );
428 FIXME("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&var));
432 static const struct IXMLDOMSelectionVtbl domselection_vtbl =
434 domselection_QueryInterface,
436 domselection_Release,
437 domselection_GetTypeInfoCount,
438 domselection_GetTypeInfo,
439 domselection_GetIDsOfNames,
441 domselection_get_item,
442 domselection_get_length,
443 domselection_nextNode,
445 domselection_get__newEnum,
446 domselection_get_expr,
447 domselection_put_expr,
448 domselection_get_context,
449 domselection_putref_context,
450 domselection_peekNode,
451 domselection_matches,
452 domselection_removeNext,
453 domselection_removeAll,
455 domselection_getProperty,
456 domselection_setProperty
459 /* IEnumVARIANT support */
460 static HRESULT WINAPI enumvariant_QueryInterface(
465 enumvariant *This = impl_from_IEnumVARIANT( iface );
467 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
471 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
472 IsEqualGUID( riid, &IID_IEnumVARIANT ))
474 *ppvObject = &This->IEnumVARIANT_iface;
477 return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
479 IEnumVARIANT_AddRef( iface );
484 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface )
486 enumvariant *This = impl_from_IEnumVARIANT( iface );
487 ULONG ref = InterlockedIncrement( &This->ref );
488 TRACE("(%p)->(%d)\n", This, ref);
492 static ULONG WINAPI enumvariant_Release(IEnumVARIANT *iface )
494 enumvariant *This = impl_from_IEnumVARIANT( iface );
495 ULONG ref = InterlockedDecrement(&This->ref);
497 TRACE("(%p)->(%d)\n", This, ref);
500 if (This->own) IXMLDOMSelection_Release(This->selection);
507 static HRESULT WINAPI enumvariant_Next(
513 enumvariant *This = impl_from_IEnumVARIANT( iface );
517 TRACE("(%p)->(%u %p %p)\n", This, celt, var, fetched);
519 if (fetched) *fetched = 0;
521 if (celt && !var) return E_INVALIDARG;
523 for (; celt > 0; celt--, var++, This->pos++)
525 IDispatch *disp = NULL;
529 hr = IXMLDOMSelection_get_item(This->selection, This->pos, &node);
530 if (hr != S_OK) break;
532 IXMLDOMNode_QueryInterface(node, &IID_IDispatch, (void**)&disp);
533 IXMLDOMNode_Release(node);
535 V_VT(var) = VT_DISPATCH;
536 V_DISPATCH(var) = disp;
541 if (fetched) (*fetched)++;
543 /* we need to advance one step more for some reason */
545 IXMLDOMSelection_nextNode(This->selection, &node);
547 return celt == 0 ? S_OK : S_FALSE;
550 static HRESULT WINAPI enumvariant_Skip(
554 enumvariant *This = impl_from_IEnumVARIANT( iface );
555 FIXME("(%p)->(%u): stub\n", This, celt);
559 static HRESULT WINAPI enumvariant_Reset(IEnumVARIANT *iface)
561 enumvariant *This = impl_from_IEnumVARIANT( iface );
562 FIXME("(%p): stub\n", This);
566 static HRESULT WINAPI enumvariant_Clone(
567 IEnumVARIANT *iface, IEnumVARIANT **ppenum)
569 enumvariant *This = impl_from_IEnumVARIANT( iface );
570 FIXME("(%p)->(%p): stub\n", This, ppenum);
574 static const struct IEnumVARIANTVtbl EnumVARIANTVtbl =
576 enumvariant_QueryInterface,
585 static HRESULT create_enumvariant(IXMLDOMSelection *selection, BOOL own, IUnknown **penum)
589 This = heap_alloc(sizeof(enumvariant));
590 if (!This) return E_OUTOFMEMORY;
592 This->IEnumVARIANT_iface.lpVtbl = &EnumVARIANTVtbl;
594 This->selection = selection;
599 IXMLDOMSelection_AddRef(selection);
601 return IEnumVARIANT_QueryInterface(&This->IEnumVARIANT_iface, &IID_IUnknown, (void**)penum);
604 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
609 for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
610 idx = idx*10 + (*ptr-'0');
612 return DISP_E_UNKNOWNNAME;
614 *dispid = DISPID_DOM_COLLECTION_BASE + idx;
615 TRACE("ret %x\n", *dispid);
619 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
620 VARIANT *res, EXCEPINFO *ei)
622 domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
624 TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
626 V_VT(res) = VT_DISPATCH;
627 V_DISPATCH(res) = NULL;
629 if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
630 return DISP_E_UNKNOWNNAME;
634 case INVOKE_PROPERTYGET:
636 IXMLDOMNode *disp = NULL;
638 domselection_get_item(&This->IXMLDOMSelection_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
639 V_DISPATCH(res) = (IDispatch*)disp;
644 FIXME("unimplemented flags %x\n", flags);
649 TRACE("ret %p\n", V_DISPATCH(res));
654 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
655 domselection_get_dispid,
659 static const tid_t domselection_iface_tids[] = {
660 IXMLDOMSelection_tid,
663 static dispex_static_data_t domselection_dispex = {
664 &domselection_dispex_vtbl,
665 IXMLDOMSelection_tid,
667 domselection_iface_tids
670 #define XSLPATTERN_CHECK_ARGS(n) \
672 FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
673 xmlXPathSetArityError(pctx); \
678 static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
680 XSLPATTERN_CHECK_ARGS(0);
682 xmlXPathPositionFunction(pctx, 0);
683 xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
686 static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
689 XSLPATTERN_CHECK_ARGS(0);
691 xmlXPathPositionFunction(pctx, 0);
692 pos = xmlXPathPopNumber(pctx);
693 xmlXPathLastFunction(pctx, 0);
694 last = xmlXPathPopNumber(pctx);
695 xmlXPathReturnBoolean(pctx, pos == last);
698 static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
700 XSLPATTERN_CHECK_ARGS(0);
701 xmlXPathReturnNumber(pctx, pctx->context->node->type);
704 static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
706 xmlChar *arg1, *arg2;
707 XSLPATTERN_CHECK_ARGS(2);
709 arg2 = xmlXPathPopString(pctx);
710 arg1 = xmlXPathPopString(pctx);
711 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
716 static void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
718 xmlChar *arg1, *arg2;
719 XSLPATTERN_CHECK_ARGS(2);
721 arg2 = xmlXPathPopString(pctx);
722 arg1 = xmlXPathPopString(pctx);
723 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
728 static void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
730 xmlChar *arg1, *arg2;
731 XSLPATTERN_CHECK_ARGS(2);
733 arg2 = xmlXPathPopString(pctx);
734 arg1 = xmlXPathPopString(pctx);
735 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
740 static void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
742 xmlChar *arg1, *arg2;
743 XSLPATTERN_CHECK_ARGS(2);
745 arg2 = xmlXPathPopString(pctx);
746 arg1 = xmlXPathPopString(pctx);
747 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
752 static void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
754 xmlChar *arg1, *arg2;
755 XSLPATTERN_CHECK_ARGS(2);
757 arg2 = xmlXPathPopString(pctx);
758 arg1 = xmlXPathPopString(pctx);
759 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
764 static void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
766 xmlChar *arg1, *arg2;
767 XSLPATTERN_CHECK_ARGS(2);
769 arg2 = xmlXPathPopString(pctx);
770 arg1 = xmlXPathPopString(pctx);
771 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
776 static void query_serror(void* ctx, xmlErrorPtr err)
778 LIBXML2_CALLBACK_SERROR(domselection_create, err);
781 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
783 domselection *This = heap_alloc(sizeof(domselection));
784 xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
787 TRACE("(%p, %s, %p)\n", node, wine_dbgstr_a((char const*)query), out);
790 if (!This || !ctxt || !query)
792 xmlXPathFreeContext(ctxt);
794 return E_OUTOFMEMORY;
797 This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
801 This->enumvariant = NULL;
802 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
803 xmldoc_add_ref(This->node->doc);
805 ctxt->error = query_serror;
807 registerNamespaces(ctxt);
809 if (is_xpathmode(This->node->doc))
811 xmlXPathRegisterAllFunctions(ctxt);
812 This->result = xmlXPathEvalExpression(query, ctxt);
816 xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
818 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
819 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
821 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
822 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
823 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
825 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
826 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
827 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
828 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
829 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
830 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
832 This->result = xmlXPathEvalExpression(pattern_query, ctxt);
833 xmlFree(pattern_query);
836 if (!This->result || This->result->type != XPATH_NODESET)
842 *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
844 TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
847 if (This && FAILED(hr))
848 IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
849 xmlXPathFreeContext(ctxt);