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 );
173 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
176 static HRESULT WINAPI domselection_GetTypeInfo(
177 IXMLDOMSelection *iface,
180 ITypeInfo** ppTInfo )
182 domselection *This = impl_from_IXMLDOMSelection( iface );
183 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
184 iTInfo, lcid, ppTInfo);
187 static HRESULT WINAPI domselection_GetIDsOfNames(
188 IXMLDOMSelection *iface,
195 domselection *This = impl_from_IXMLDOMSelection( iface );
196 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
197 riid, rgszNames, cNames, lcid, rgDispId);
200 static HRESULT WINAPI domselection_Invoke(
201 IXMLDOMSelection *iface,
206 DISPPARAMS* pDispParams,
208 EXCEPINFO* pExcepInfo,
211 domselection *This = impl_from_IXMLDOMSelection( iface );
212 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
213 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
216 static HRESULT WINAPI domselection_get_item(
217 IXMLDOMSelection* iface,
219 IXMLDOMNode** listItem)
221 domselection *This = impl_from_IXMLDOMSelection( iface );
223 TRACE("(%p)->(%d %p)\n", This, index, listItem);
230 if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
233 *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
234 This->resultPos = index + 1;
239 static HRESULT WINAPI domselection_get_length(
240 IXMLDOMSelection* iface,
243 domselection *This = impl_from_IXMLDOMSelection( iface );
245 TRACE("(%p)->(%p)\n", This, listLength);
250 *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
254 static HRESULT WINAPI domselection_nextNode(
255 IXMLDOMSelection* iface,
256 IXMLDOMNode** nextItem)
258 domselection *This = impl_from_IXMLDOMSelection( iface );
260 TRACE("(%p)->(%p)\n", This, nextItem );
267 if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
270 *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
275 static HRESULT WINAPI domselection_reset(
276 IXMLDOMSelection* iface)
278 domselection *This = impl_from_IXMLDOMSelection( iface );
285 static HRESULT WINAPI domselection_get__newEnum(
286 IXMLDOMSelection* iface,
289 domselection *This = impl_from_IXMLDOMSelection( iface );
291 TRACE("(%p)->(%p)\n", This, ppUnk);
293 return create_enumvariant(iface, TRUE, ppUnk);
296 static HRESULT WINAPI domselection_get_expr(
297 IXMLDOMSelection* iface,
300 domselection *This = impl_from_IXMLDOMSelection( iface );
301 FIXME("(%p)->(%p)\n", This, p);
305 static HRESULT WINAPI domselection_put_expr(
306 IXMLDOMSelection* iface,
309 domselection *This = impl_from_IXMLDOMSelection( iface );
310 FIXME("(%p)->(%s)\n", This, debugstr_w(p));
314 static HRESULT WINAPI domselection_get_context(
315 IXMLDOMSelection* iface,
318 domselection *This = impl_from_IXMLDOMSelection( iface );
319 FIXME("(%p)->(%p)\n", This, node);
323 static HRESULT WINAPI domselection_putref_context(
324 IXMLDOMSelection* iface,
327 domselection *This = impl_from_IXMLDOMSelection( iface );
328 FIXME("(%p)->(%p)\n", This, node);
332 static HRESULT WINAPI domselection_peekNode(
333 IXMLDOMSelection* iface,
336 domselection *This = impl_from_IXMLDOMSelection( iface );
337 FIXME("(%p)->(%p)\n", This, node);
341 static HRESULT WINAPI domselection_matches(
342 IXMLDOMSelection* iface,
344 IXMLDOMNode **out_node)
346 domselection *This = impl_from_IXMLDOMSelection( iface );
347 FIXME("(%p)->(%p %p)\n", This, node, out_node);
351 static HRESULT WINAPI domselection_removeNext(
352 IXMLDOMSelection* iface,
355 domselection *This = impl_from_IXMLDOMSelection( iface );
356 FIXME("(%p)->(%p)\n", This, node);
360 static HRESULT WINAPI domselection_removeAll(
361 IXMLDOMSelection* iface)
363 domselection *This = impl_from_IXMLDOMSelection( iface );
364 FIXME("(%p)\n", This);
368 static HRESULT WINAPI domselection_clone(
369 IXMLDOMSelection* iface,
370 IXMLDOMSelection **node)
372 domselection *This = impl_from_IXMLDOMSelection( iface );
373 FIXME("(%p)->(%p)\n", This, node);
377 static HRESULT WINAPI domselection_getProperty(
378 IXMLDOMSelection* iface,
382 domselection *This = impl_from_IXMLDOMSelection( iface );
383 FIXME("(%p)->(%s %p)\n", This, debugstr_w(p), var);
387 static HRESULT WINAPI domselection_setProperty(
388 IXMLDOMSelection* iface,
392 domselection *This = impl_from_IXMLDOMSelection( iface );
393 FIXME("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&var));
397 static const struct IXMLDOMSelectionVtbl domselection_vtbl =
399 domselection_QueryInterface,
401 domselection_Release,
402 domselection_GetTypeInfoCount,
403 domselection_GetTypeInfo,
404 domselection_GetIDsOfNames,
406 domselection_get_item,
407 domselection_get_length,
408 domselection_nextNode,
410 domselection_get__newEnum,
411 domselection_get_expr,
412 domselection_put_expr,
413 domselection_get_context,
414 domselection_putref_context,
415 domselection_peekNode,
416 domselection_matches,
417 domselection_removeNext,
418 domselection_removeAll,
420 domselection_getProperty,
421 domselection_setProperty
424 /* IEnumVARIANT support */
425 static HRESULT WINAPI enumvariant_QueryInterface(
430 enumvariant *This = impl_from_IEnumVARIANT( iface );
432 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
436 if (IsEqualGUID( riid, &IID_IUnknown ))
439 *ppvObject = &This->IEnumVARIANT_iface;
441 return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
443 else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
445 *ppvObject = &This->IEnumVARIANT_iface;
448 return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
450 IEnumVARIANT_AddRef( iface );
455 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface )
457 enumvariant *This = impl_from_IEnumVARIANT( iface );
458 ULONG ref = InterlockedIncrement( &This->ref );
459 TRACE("(%p)->(%d)\n", This, ref);
463 static ULONG WINAPI enumvariant_Release(IEnumVARIANT *iface )
465 enumvariant *This = impl_from_IEnumVARIANT( iface );
466 ULONG ref = InterlockedDecrement(&This->ref);
468 TRACE("(%p)->(%d)\n", This, ref);
471 if (This->own) IXMLDOMSelection_Release(This->selection);
478 static HRESULT WINAPI enumvariant_Next(
484 enumvariant *This = impl_from_IEnumVARIANT( iface );
488 TRACE("(%p)->(%u %p %p)\n", This, celt, var, fetched);
490 if (fetched) *fetched = 0;
492 if (celt && !var) return E_INVALIDARG;
494 for (; celt > 0; celt--, var++, This->pos++)
496 IDispatch *disp = NULL;
500 hr = IXMLDOMSelection_get_item(This->selection, This->pos, &node);
501 if (hr != S_OK) break;
503 IXMLDOMNode_QueryInterface(node, &IID_IDispatch, (void**)&disp);
504 IXMLDOMNode_Release(node);
506 V_VT(var) = VT_DISPATCH;
507 V_DISPATCH(var) = disp;
512 if (fetched) (*fetched)++;
514 /* we need to advance one step more for some reason */
518 IXMLDOMSelection_nextNode(This->selection, &node);
519 if (node) IXMLDOMNode_Release(node);
522 return celt == 0 ? S_OK : S_FALSE;
525 static HRESULT WINAPI enumvariant_Skip(
529 enumvariant *This = impl_from_IEnumVARIANT( iface );
530 FIXME("(%p)->(%u): stub\n", This, celt);
534 static HRESULT WINAPI enumvariant_Reset(IEnumVARIANT *iface)
536 enumvariant *This = impl_from_IEnumVARIANT( iface );
537 FIXME("(%p): stub\n", This);
541 static HRESULT WINAPI enumvariant_Clone(
542 IEnumVARIANT *iface, IEnumVARIANT **ppenum)
544 enumvariant *This = impl_from_IEnumVARIANT( iface );
545 FIXME("(%p)->(%p): stub\n", This, ppenum);
549 static const struct IEnumVARIANTVtbl EnumVARIANTVtbl =
551 enumvariant_QueryInterface,
560 static HRESULT create_enumvariant(IXMLDOMSelection *selection, BOOL own, IUnknown **penum)
564 This = heap_alloc(sizeof(enumvariant));
565 if (!This) return E_OUTOFMEMORY;
567 This->IEnumVARIANT_iface.lpVtbl = &EnumVARIANTVtbl;
569 This->selection = selection;
574 IXMLDOMSelection_AddRef(selection);
576 *penum = (IUnknown*)&This->IEnumVARIANT_iface;
577 IUnknown_AddRef(*penum);
581 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
586 for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
587 idx = idx*10 + (*ptr-'0');
589 return DISP_E_UNKNOWNNAME;
591 *dispid = DISPID_DOM_COLLECTION_BASE + idx;
592 TRACE("ret %x\n", *dispid);
596 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
597 VARIANT *res, EXCEPINFO *ei)
599 domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
601 TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
603 V_VT(res) = VT_DISPATCH;
604 V_DISPATCH(res) = NULL;
606 if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
607 return DISP_E_UNKNOWNNAME;
611 case INVOKE_PROPERTYGET:
613 IXMLDOMNode *disp = NULL;
615 IXMLDOMSelection_get_item(&This->IXMLDOMSelection_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
616 V_DISPATCH(res) = (IDispatch*)disp;
621 FIXME("unimplemented flags %x\n", flags);
626 TRACE("ret %p\n", V_DISPATCH(res));
631 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
632 domselection_get_dispid,
636 static const tid_t domselection_iface_tids[] = {
637 IXMLDOMSelection_tid,
640 static dispex_static_data_t domselection_dispex = {
641 &domselection_dispex_vtbl,
642 IXMLDOMSelection_tid,
644 domselection_iface_tids
647 #define XSLPATTERN_CHECK_ARGS(n) \
649 FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
650 xmlXPathSetArityError(pctx); \
655 static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
657 XSLPATTERN_CHECK_ARGS(0);
659 xmlXPathPositionFunction(pctx, 0);
660 xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
663 static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
666 XSLPATTERN_CHECK_ARGS(0);
668 xmlXPathPositionFunction(pctx, 0);
669 pos = xmlXPathPopNumber(pctx);
670 xmlXPathLastFunction(pctx, 0);
671 last = xmlXPathPopNumber(pctx);
672 xmlXPathReturnBoolean(pctx, pos == last);
675 static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
677 XSLPATTERN_CHECK_ARGS(0);
678 xmlXPathReturnNumber(pctx, pctx->context->node->type);
681 static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
683 xmlChar *arg1, *arg2;
684 XSLPATTERN_CHECK_ARGS(2);
686 arg2 = xmlXPathPopString(pctx);
687 arg1 = xmlXPathPopString(pctx);
688 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
693 static void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
695 xmlChar *arg1, *arg2;
696 XSLPATTERN_CHECK_ARGS(2);
698 arg2 = xmlXPathPopString(pctx);
699 arg1 = xmlXPathPopString(pctx);
700 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
705 static void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
707 xmlChar *arg1, *arg2;
708 XSLPATTERN_CHECK_ARGS(2);
710 arg2 = xmlXPathPopString(pctx);
711 arg1 = xmlXPathPopString(pctx);
712 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
717 static void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
719 xmlChar *arg1, *arg2;
720 XSLPATTERN_CHECK_ARGS(2);
722 arg2 = xmlXPathPopString(pctx);
723 arg1 = xmlXPathPopString(pctx);
724 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
729 static void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
731 xmlChar *arg1, *arg2;
732 XSLPATTERN_CHECK_ARGS(2);
734 arg2 = xmlXPathPopString(pctx);
735 arg1 = xmlXPathPopString(pctx);
736 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
741 static void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
743 xmlChar *arg1, *arg2;
744 XSLPATTERN_CHECK_ARGS(2);
746 arg2 = xmlXPathPopString(pctx);
747 arg1 = xmlXPathPopString(pctx);
748 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
753 static void query_serror(void* ctx, xmlErrorPtr err)
755 LIBXML2_CALLBACK_SERROR(domselection_create, err);
758 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
760 domselection *This = heap_alloc(sizeof(domselection));
761 xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
764 TRACE("(%p, %s, %p)\n", node, debugstr_a((char const*)query), out);
767 if (!This || !ctxt || !query)
769 xmlXPathFreeContext(ctxt);
771 return E_OUTOFMEMORY;
774 This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
778 This->enumvariant = NULL;
779 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
780 xmldoc_add_ref(This->node->doc);
782 ctxt->error = query_serror;
784 registerNamespaces(ctxt);
786 if (is_xpathmode(This->node->doc))
788 xmlXPathRegisterAllFunctions(ctxt);
789 This->result = xmlXPathEvalExpression(query, ctxt);
793 xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
795 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
796 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
798 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
799 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
800 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
802 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
803 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
804 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
805 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
806 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
807 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
809 This->result = xmlXPathEvalExpression(pattern_query, ctxt);
810 xmlFree(pattern_query);
813 if (!This->result || This->result->type != XPATH_NODESET)
819 *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
821 TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
824 if (This && FAILED(hr))
825 IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
826 xmlXPathFreeContext(ctxt);