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