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 ) ||
437 IsEqualGUID( riid, &IID_IEnumVARIANT ))
439 *ppvObject = &This->IEnumVARIANT_iface;
442 return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
444 IEnumVARIANT_AddRef( iface );
449 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface )
451 enumvariant *This = impl_from_IEnumVARIANT( iface );
452 ULONG ref = InterlockedIncrement( &This->ref );
453 TRACE("(%p)->(%d)\n", This, ref);
457 static ULONG WINAPI enumvariant_Release(IEnumVARIANT *iface )
459 enumvariant *This = impl_from_IEnumVARIANT( iface );
460 ULONG ref = InterlockedDecrement(&This->ref);
462 TRACE("(%p)->(%d)\n", This, ref);
465 if (This->own) IXMLDOMSelection_Release(This->selection);
472 static HRESULT WINAPI enumvariant_Next(
478 enumvariant *This = impl_from_IEnumVARIANT( iface );
482 TRACE("(%p)->(%u %p %p)\n", This, celt, var, fetched);
484 if (fetched) *fetched = 0;
486 if (celt && !var) return E_INVALIDARG;
488 for (; celt > 0; celt--, var++, This->pos++)
490 IDispatch *disp = NULL;
494 hr = IXMLDOMSelection_get_item(This->selection, This->pos, &node);
495 if (hr != S_OK) break;
497 IXMLDOMNode_QueryInterface(node, &IID_IDispatch, (void**)&disp);
498 IXMLDOMNode_Release(node);
500 V_VT(var) = VT_DISPATCH;
501 V_DISPATCH(var) = disp;
506 if (fetched) (*fetched)++;
508 /* we need to advance one step more for some reason */
510 IXMLDOMSelection_nextNode(This->selection, &node);
512 return celt == 0 ? S_OK : S_FALSE;
515 static HRESULT WINAPI enumvariant_Skip(
519 enumvariant *This = impl_from_IEnumVARIANT( iface );
520 FIXME("(%p)->(%u): stub\n", This, celt);
524 static HRESULT WINAPI enumvariant_Reset(IEnumVARIANT *iface)
526 enumvariant *This = impl_from_IEnumVARIANT( iface );
527 FIXME("(%p): stub\n", This);
531 static HRESULT WINAPI enumvariant_Clone(
532 IEnumVARIANT *iface, IEnumVARIANT **ppenum)
534 enumvariant *This = impl_from_IEnumVARIANT( iface );
535 FIXME("(%p)->(%p): stub\n", This, ppenum);
539 static const struct IEnumVARIANTVtbl EnumVARIANTVtbl =
541 enumvariant_QueryInterface,
550 static HRESULT create_enumvariant(IXMLDOMSelection *selection, BOOL own, IUnknown **penum)
554 This = heap_alloc(sizeof(enumvariant));
555 if (!This) return E_OUTOFMEMORY;
557 This->IEnumVARIANT_iface.lpVtbl = &EnumVARIANTVtbl;
559 This->selection = selection;
564 IXMLDOMSelection_AddRef(selection);
566 return IEnumVARIANT_QueryInterface(&This->IEnumVARIANT_iface, &IID_IUnknown, (void**)penum);
569 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
574 for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
575 idx = idx*10 + (*ptr-'0');
577 return DISP_E_UNKNOWNNAME;
579 *dispid = DISPID_DOM_COLLECTION_BASE + idx;
580 TRACE("ret %x\n", *dispid);
584 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
585 VARIANT *res, EXCEPINFO *ei)
587 domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
589 TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
591 V_VT(res) = VT_DISPATCH;
592 V_DISPATCH(res) = NULL;
594 if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
595 return DISP_E_UNKNOWNNAME;
599 case INVOKE_PROPERTYGET:
601 IXMLDOMNode *disp = NULL;
603 IXMLDOMSelection_get_item(&This->IXMLDOMSelection_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
604 V_DISPATCH(res) = (IDispatch*)disp;
609 FIXME("unimplemented flags %x\n", flags);
614 TRACE("ret %p\n", V_DISPATCH(res));
619 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
620 domselection_get_dispid,
624 static const tid_t domselection_iface_tids[] = {
625 IXMLDOMSelection_tid,
628 static dispex_static_data_t domselection_dispex = {
629 &domselection_dispex_vtbl,
630 IXMLDOMSelection_tid,
632 domselection_iface_tids
635 #define XSLPATTERN_CHECK_ARGS(n) \
637 FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
638 xmlXPathSetArityError(pctx); \
643 static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
645 XSLPATTERN_CHECK_ARGS(0);
647 xmlXPathPositionFunction(pctx, 0);
648 xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
651 static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
654 XSLPATTERN_CHECK_ARGS(0);
656 xmlXPathPositionFunction(pctx, 0);
657 pos = xmlXPathPopNumber(pctx);
658 xmlXPathLastFunction(pctx, 0);
659 last = xmlXPathPopNumber(pctx);
660 xmlXPathReturnBoolean(pctx, pos == last);
663 static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
665 XSLPATTERN_CHECK_ARGS(0);
666 xmlXPathReturnNumber(pctx, pctx->context->node->type);
669 static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
671 xmlChar *arg1, *arg2;
672 XSLPATTERN_CHECK_ARGS(2);
674 arg2 = xmlXPathPopString(pctx);
675 arg1 = xmlXPathPopString(pctx);
676 xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
681 static void XSLPattern_OP_INEq(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_ILt(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_ILEq(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_IGt(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_IGEq(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 query_serror(void* ctx, xmlErrorPtr err)
743 LIBXML2_CALLBACK_SERROR(domselection_create, err);
746 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
748 domselection *This = heap_alloc(sizeof(domselection));
749 xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
752 TRACE("(%p, %s, %p)\n", node, wine_dbgstr_a((char const*)query), out);
755 if (!This || !ctxt || !query)
757 xmlXPathFreeContext(ctxt);
759 return E_OUTOFMEMORY;
762 This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
766 This->enumvariant = NULL;
767 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
768 xmldoc_add_ref(This->node->doc);
770 ctxt->error = query_serror;
772 registerNamespaces(ctxt);
774 if (is_xpathmode(This->node->doc))
776 xmlXPathRegisterAllFunctions(ctxt);
777 This->result = xmlXPathEvalExpression(query, ctxt);
781 xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
783 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
784 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
786 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
787 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
788 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
790 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
791 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
792 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
793 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
794 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
795 xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
797 This->result = xmlXPathEvalExpression(pattern_query, ctxt);
798 xmlFree(pattern_query);
801 if (!This->result || This->result->type != XPATH_NODESET)
807 *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
809 TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
812 if (This && FAILED(hr))
813 IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
814 xmlXPathFreeContext(ctxt);