msxml3: Use an iface instead of a vtbl pointer in domelem.
[wine] / dlls / msxml3 / queryresult.c
1 /*
2  *    XPath/XSLPattern query result node list implementation
3  *
4  * Copyright 2005 Mike McCormack
5  * Copyright 2007 Mikolaj Zalewski
6  * Copyright 2010 Adam Martinson for CodeWeavers
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #define COBJMACROS
24
25 #include "config.h"
26
27 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "msxml6.h"
33
34 #include "msxml_private.h"
35
36 #include "wine/debug.h"
37
38 /* This file implements the object returned by a XPath query. Note that this is
39  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
40  * They are different because the list returned by XPath queries:
41  *  - is static - gives the results for the XML tree as it existed during the
42  *    execution of the query
43  *  - supports IXMLDOMSelection (TODO)
44  *
45  */
46
47 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
48
49 #ifdef HAVE_LIBXML2
50
51 #include <libxml/xpath.h>
52 #include <libxml/xpathInternals.h>
53
54 int registerNamespaces(xmlXPathContextPtr ctxt);
55 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
56
57 typedef struct _queryresult
58 {
59     DispatchEx dispex;
60     const struct IXMLDOMNodeListVtbl *lpVtbl;
61     LONG ref;
62     xmlNodePtr node;
63     xmlXPathObjectPtr result;
64     int resultPos;
65 } queryresult;
66
67 static inline queryresult *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
68 {
69     return (queryresult *)((char*)iface - FIELD_OFFSET(queryresult, lpVtbl));
70 }
71
72 #define XMLQUERYRES(x)  ((IXMLDOMNodeList*)&(x)->lpVtbl)
73
74 static HRESULT WINAPI queryresult_QueryInterface(
75     IXMLDOMNodeList *iface,
76     REFIID riid,
77     void** ppvObject )
78 {
79     queryresult *This = impl_from_IXMLDOMNodeList( iface );
80
81     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
82
83     if(!ppvObject)
84         return E_INVALIDARG;
85
86     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
87          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
88     {
89         *ppvObject = iface;
90     }
91     else if(dispex_query_interface(&This->dispex, riid, ppvObject))
92     {
93         return *ppvObject ? S_OK : E_NOINTERFACE;
94     }
95     else
96     {
97         FIXME("interface %s not implemented\n", debugstr_guid(riid));
98         *ppvObject = NULL;
99         return E_NOINTERFACE;
100     }
101
102     IXMLDOMNodeList_AddRef( iface );
103
104     return S_OK;
105 }
106
107 static ULONG WINAPI queryresult_AddRef(
108     IXMLDOMNodeList *iface )
109 {
110     queryresult *This = impl_from_IXMLDOMNodeList( iface );
111     return InterlockedIncrement( &This->ref );
112 }
113
114 static ULONG WINAPI queryresult_Release(
115     IXMLDOMNodeList *iface )
116 {
117     queryresult *This = impl_from_IXMLDOMNodeList( iface );
118     ULONG ref;
119
120     ref = InterlockedDecrement(&This->ref);
121     if ( ref == 0 )
122     {
123         xmlXPathFreeObject(This->result);
124         xmldoc_release(This->node->doc);
125         heap_free(This);
126     }
127
128     return ref;
129 }
130
131 static HRESULT WINAPI queryresult_GetTypeInfoCount(
132     IXMLDOMNodeList *iface,
133     UINT* pctinfo )
134 {
135     queryresult *This = impl_from_IXMLDOMNodeList( iface );
136
137     TRACE("(%p)->(%p)\n", This, pctinfo);
138
139     *pctinfo = 1;
140
141     return S_OK;
142 }
143
144 static HRESULT WINAPI queryresult_GetTypeInfo(
145     IXMLDOMNodeList *iface,
146     UINT iTInfo,
147     LCID lcid,
148     ITypeInfo** ppTInfo )
149 {
150     queryresult *This = impl_from_IXMLDOMNodeList( iface );
151     HRESULT hr;
152
153     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
154
155     hr = get_typeinfo(IXMLDOMNodeList_tid, ppTInfo);
156
157     return hr;
158 }
159
160 static HRESULT WINAPI queryresult_GetIDsOfNames(
161     IXMLDOMNodeList *iface,
162     REFIID riid,
163     LPOLESTR* rgszNames,
164     UINT cNames,
165     LCID lcid,
166     DISPID* rgDispId )
167 {
168     queryresult *This = impl_from_IXMLDOMNodeList( iface );
169     ITypeInfo *typeinfo;
170     HRESULT hr;
171
172     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
173           lcid, rgDispId);
174
175     if(!rgszNames || cNames == 0 || !rgDispId)
176         return E_INVALIDARG;
177
178     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
179     if(SUCCEEDED(hr))
180     {
181         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
182         ITypeInfo_Release(typeinfo);
183     }
184
185     return hr;
186 }
187
188 static HRESULT WINAPI queryresult_Invoke(
189     IXMLDOMNodeList *iface,
190     DISPID dispIdMember,
191     REFIID riid,
192     LCID lcid,
193     WORD wFlags,
194     DISPPARAMS* pDispParams,
195     VARIANT* pVarResult,
196     EXCEPINFO* pExcepInfo,
197     UINT* puArgErr )
198 {
199     queryresult *This = impl_from_IXMLDOMNodeList( iface );
200     ITypeInfo *typeinfo;
201     HRESULT hr;
202
203     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
204           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
205
206     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
207     if(SUCCEEDED(hr))
208     {
209         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
210                 pVarResult, pExcepInfo, puArgErr);
211         ITypeInfo_Release(typeinfo);
212     }
213
214     return hr;
215 }
216
217 static HRESULT WINAPI queryresult_get_item(
218         IXMLDOMNodeList* iface,
219         LONG index,
220         IXMLDOMNode** listItem)
221 {
222     queryresult *This = impl_from_IXMLDOMNodeList( iface );
223
224     TRACE("(%p)->(%d %p)\n", This, index, listItem);
225
226     if(!listItem)
227         return E_INVALIDARG;
228
229     *listItem = NULL;
230
231     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
232         return S_FALSE;
233
234     *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
235     This->resultPos = index + 1;
236
237     return S_OK;
238 }
239
240 static HRESULT WINAPI queryresult_get_length(
241         IXMLDOMNodeList* iface,
242         LONG* listLength)
243 {
244     queryresult *This = impl_from_IXMLDOMNodeList( iface );
245
246     TRACE("(%p)->(%p)\n", This, listLength);
247
248     if(!listLength)
249         return E_INVALIDARG;
250
251     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
252     return S_OK;
253 }
254
255 static HRESULT WINAPI queryresult_nextNode(
256         IXMLDOMNodeList* iface,
257         IXMLDOMNode** nextItem)
258 {
259     queryresult *This = impl_from_IXMLDOMNodeList( iface );
260
261     TRACE("(%p)->(%p)\n", This, nextItem );
262
263     if(!nextItem)
264         return E_INVALIDARG;
265
266     *nextItem = NULL;
267
268     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
269         return S_FALSE;
270
271     *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
272     This->resultPos++;
273     return S_OK;
274 }
275
276 static HRESULT WINAPI queryresult_reset(
277         IXMLDOMNodeList* iface)
278 {
279     queryresult *This = impl_from_IXMLDOMNodeList( iface );
280
281     TRACE("%p\n", This);
282     This->resultPos = 0;
283     return S_OK;
284 }
285
286 static HRESULT WINAPI queryresult__newEnum(
287         IXMLDOMNodeList* iface,
288         IUnknown** ppUnk)
289 {
290     queryresult *This = impl_from_IXMLDOMNodeList( iface );
291     FIXME("(%p)->(%p)\n", This, ppUnk);
292     return E_NOTIMPL;
293 }
294
295
296 static const struct IXMLDOMNodeListVtbl queryresult_vtbl =
297 {
298     queryresult_QueryInterface,
299     queryresult_AddRef,
300     queryresult_Release,
301     queryresult_GetTypeInfoCount,
302     queryresult_GetTypeInfo,
303     queryresult_GetIDsOfNames,
304     queryresult_Invoke,
305     queryresult_get_item,
306     queryresult_get_length,
307     queryresult_nextNode,
308     queryresult_reset,
309     queryresult__newEnum,
310 };
311
312 static HRESULT queryresult_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
313 {
314     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
315     WCHAR *ptr;
316     int idx = 0;
317
318     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
319         idx = idx*10 + (*ptr-'0');
320     if(*ptr)
321         return DISP_E_UNKNOWNNAME;
322
323     if(idx >= xmlXPathNodeSetGetLength(This->result->nodesetval))
324         return DISP_E_UNKNOWNNAME;
325
326     *dispid = MSXML_DISPID_CUSTOM_MIN + idx;
327     TRACE("ret %x\n", *dispid);
328     return S_OK;
329 }
330
331 static HRESULT queryresult_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
332         VARIANT *res, EXCEPINFO *ei)
333 {
334     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
335
336     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
337
338     V_VT(res) = VT_DISPATCH;
339     V_DISPATCH(res) = NULL;
340
341     switch(flags)
342     {
343         case INVOKE_PROPERTYGET:
344         {
345             IXMLDOMNode *disp = NULL;
346
347             queryresult_get_item(XMLQUERYRES(This), id - MSXML_DISPID_CUSTOM_MIN, &disp);
348             V_DISPATCH(res) = (IDispatch*)disp;
349             break;
350         }
351         default:
352         {
353             FIXME("unimplemented flags %x\n", flags);
354             break;
355         }
356     }
357
358     TRACE("ret %p\n", V_DISPATCH(res));
359
360     return S_OK;
361 }
362
363 static const dispex_static_data_vtbl_t queryresult_dispex_vtbl = {
364     queryresult_get_dispid,
365     queryresult_invoke
366 };
367
368 static const tid_t queryresult_iface_tids[] = {
369     IXMLDOMNodeList_tid,
370     0
371 };
372 static dispex_static_data_t queryresult_dispex = {
373     &queryresult_dispex_vtbl,
374     IXMLDOMSelection_tid,
375     NULL,
376     queryresult_iface_tids
377 };
378
379 #define XSLPATTERN_CHECK_ARGS(n) \
380     if (nargs != n) { \
381         FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
382         xmlXPathSetArityError(pctx); \
383         return; \
384     }
385
386
387 void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
388 {
389     XSLPATTERN_CHECK_ARGS(0);
390
391     xmlXPathPositionFunction(pctx, 0);
392     xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
393 }
394
395 void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
396 {
397     double pos, last;
398     XSLPATTERN_CHECK_ARGS(0);
399
400     xmlXPathPositionFunction(pctx, 0);
401     pos = xmlXPathPopNumber(pctx);
402     xmlXPathLastFunction(pctx, 0);
403     last = xmlXPathPopNumber(pctx);
404     xmlXPathReturnBoolean(pctx, pos == last);
405 }
406
407 void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
408 {
409     XSLPATTERN_CHECK_ARGS(0);
410     xmlXPathReturnNumber(pctx, pctx->context->node->type);
411 }
412
413 void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
414 {
415     xmlChar *arg1, *arg2;
416     XSLPATTERN_CHECK_ARGS(2);
417
418     arg2 = xmlXPathPopString(pctx);
419     arg1 = xmlXPathPopString(pctx);
420     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
421     xmlFree(arg1);
422     xmlFree(arg2);
423 }
424
425 void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
426 {
427     xmlChar *arg1, *arg2;
428     XSLPATTERN_CHECK_ARGS(2);
429
430     arg2 = xmlXPathPopString(pctx);
431     arg1 = xmlXPathPopString(pctx);
432     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
433     xmlFree(arg1);
434     xmlFree(arg2);
435 }
436
437 void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
438 {
439     xmlChar *arg1, *arg2;
440     XSLPATTERN_CHECK_ARGS(2);
441
442     arg2 = xmlXPathPopString(pctx);
443     arg1 = xmlXPathPopString(pctx);
444     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
445     xmlFree(arg1);
446     xmlFree(arg2);
447 }
448
449 void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
450 {
451     xmlChar *arg1, *arg2;
452     XSLPATTERN_CHECK_ARGS(2);
453
454     arg2 = xmlXPathPopString(pctx);
455     arg1 = xmlXPathPopString(pctx);
456     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
457     xmlFree(arg1);
458     xmlFree(arg2);
459 }
460
461 void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
462 {
463     xmlChar *arg1, *arg2;
464     XSLPATTERN_CHECK_ARGS(2);
465
466     arg2 = xmlXPathPopString(pctx);
467     arg1 = xmlXPathPopString(pctx);
468     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
469     xmlFree(arg1);
470     xmlFree(arg2);
471 }
472
473 void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
474 {
475     xmlChar *arg1, *arg2;
476     XSLPATTERN_CHECK_ARGS(2);
477
478     arg2 = xmlXPathPopString(pctx);
479     arg1 = xmlXPathPopString(pctx);
480     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
481     xmlFree(arg1);
482     xmlFree(arg2);
483 }
484
485 static void query_serror(void* ctx, xmlErrorPtr err)
486 {
487     LIBXML2_CALLBACK_SERROR(queryresult_create, err);
488 }
489
490 HRESULT queryresult_create(xmlNodePtr node, xmlChar* szQuery, IXMLDOMNodeList **out)
491 {
492     queryresult *This = heap_alloc_zero(sizeof(queryresult));
493     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
494     HRESULT hr;
495
496     TRACE("(%p, %s, %p)\n", node, wine_dbgstr_a((char const*)szQuery), out);
497
498     *out = NULL;
499     if (This == NULL || ctxt == NULL || szQuery == NULL)
500     {
501         hr = E_OUTOFMEMORY;
502         goto cleanup;
503     }
504
505     This->lpVtbl = &queryresult_vtbl;
506     This->ref = 1;
507     This->resultPos = 0;
508     This->node = node;
509     xmldoc_add_ref(This->node->doc);
510
511     ctxt->error = query_serror;
512     ctxt->node = node;
513     registerNamespaces(ctxt);
514
515     if (is_xpathmode(This->node->doc))
516     {
517         xmlXPathRegisterAllFunctions(ctxt);
518         This->result = xmlXPathEvalExpression(szQuery, ctxt);
519     }
520     else
521     {
522         xmlChar* xslpQuery = XSLPattern_to_XPath(ctxt, szQuery);
523
524         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
525         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
526
527         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
528         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
529         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
530
531         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
532         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
533         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
534         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
535         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
536         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
537
538         This->result = xmlXPathEvalExpression(xslpQuery, ctxt);
539         xmlFree(xslpQuery);
540     }
541
542     if (!This->result || This->result->type != XPATH_NODESET)
543     {
544         hr = E_FAIL;
545         goto cleanup;
546     }
547
548     init_dispex(&This->dispex, (IUnknown*)&This->lpVtbl, &queryresult_dispex);
549
550     *out = (IXMLDOMNodeList *) &This->lpVtbl;
551     hr = S_OK;
552     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
553
554 cleanup:
555     if (This != NULL && FAILED(hr))
556         IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl );
557     xmlXPathFreeContext(ctxt);
558     return hr;
559 }
560
561 #endif