msxml3: Reimplement IXMLDOMDocument::createTextNode() on top of ::createNode().
[wine] / dlls / msxml3 / queryresult.c
1 /*
2  *    XPath query result node list implementation (TODO: XSLPattern support)
3  *
4  * Copyright 2005 Mike McCormack
5  * Copyright 2007 Mikolaj Zalewski
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include "config.h"
25
26 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "msxml2.h"
32
33 #include "msxml_private.h"
34
35 #include "wine/debug.h"
36
37 /* This file implements the object returned by a XPath query. Note that this is
38  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
39  * They are different because the list returned by XPath queries:
40  *  - is static - gives the results for the XML tree as it existed during the
41  *    execution of the query
42  *  - supports IXMLDOMSelection (TODO)
43  *
44  * TODO: XSLPattern support
45  */
46
47 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
48
49 #ifdef HAVE_LIBXML2
50
51 #include <libxml/xpath.h>
52
53 typedef struct _queryresult
54 {
55     DispatchEx dispex;
56     const struct IXMLDOMNodeListVtbl *lpVtbl;
57     LONG ref;
58     xmlNodePtr node;
59     xmlXPathObjectPtr result;
60     int resultPos;
61 } queryresult;
62
63 static inline queryresult *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
64 {
65     return (queryresult *)((char*)iface - FIELD_OFFSET(queryresult, lpVtbl));
66 }
67
68 #define XMLQUERYRES(x)  ((IXMLDOMNodeList*)&(x)->lpVtbl)
69
70 static HRESULT WINAPI queryresult_QueryInterface(
71     IXMLDOMNodeList *iface,
72     REFIID riid,
73     void** ppvObject )
74 {
75     queryresult *This = impl_from_IXMLDOMNodeList( iface );
76
77     TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
78
79     if(!ppvObject)
80         return E_INVALIDARG;
81
82     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
83          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
84     {
85         *ppvObject = iface;
86     }
87     else if(dispex_query_interface(&This->dispex, riid, ppvObject))
88     {
89         return *ppvObject ? S_OK : E_NOINTERFACE;
90     }
91     else
92     {
93         FIXME("interface %s not implemented\n", debugstr_guid(riid));
94         *ppvObject = NULL;
95         return E_NOINTERFACE;
96     }
97
98     IXMLDOMNodeList_AddRef( iface );
99
100     return S_OK;
101 }
102
103 static ULONG WINAPI queryresult_AddRef(
104     IXMLDOMNodeList *iface )
105 {
106     queryresult *This = impl_from_IXMLDOMNodeList( iface );
107     return InterlockedIncrement( &This->ref );
108 }
109
110 static ULONG WINAPI queryresult_Release(
111     IXMLDOMNodeList *iface )
112 {
113     queryresult *This = impl_from_IXMLDOMNodeList( iface );
114     ULONG ref;
115
116     ref = InterlockedDecrement(&This->ref);
117     if ( ref == 0 )
118     {
119         xmlXPathFreeObject(This->result);
120         xmldoc_release(This->node->doc);
121         heap_free(This);
122     }
123
124     return ref;
125 }
126
127 static HRESULT WINAPI queryresult_GetTypeInfoCount(
128     IXMLDOMNodeList *iface,
129     UINT* pctinfo )
130 {
131     queryresult *This = impl_from_IXMLDOMNodeList( iface );
132
133     TRACE("(%p)->(%p)\n", This, pctinfo);
134
135     *pctinfo = 1;
136
137     return S_OK;
138 }
139
140 static HRESULT WINAPI queryresult_GetTypeInfo(
141     IXMLDOMNodeList *iface,
142     UINT iTInfo,
143     LCID lcid,
144     ITypeInfo** ppTInfo )
145 {
146     queryresult *This = impl_from_IXMLDOMNodeList( iface );
147     HRESULT hr;
148
149     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
150
151     hr = get_typeinfo(IXMLDOMNodeList_tid, ppTInfo);
152
153     return hr;
154 }
155
156 static HRESULT WINAPI queryresult_GetIDsOfNames(
157     IXMLDOMNodeList *iface,
158     REFIID riid,
159     LPOLESTR* rgszNames,
160     UINT cNames,
161     LCID lcid,
162     DISPID* rgDispId )
163 {
164     queryresult *This = impl_from_IXMLDOMNodeList( iface );
165     ITypeInfo *typeinfo;
166     HRESULT hr;
167
168     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
169           lcid, rgDispId);
170
171     if(!rgszNames || cNames == 0 || !rgDispId)
172         return E_INVALIDARG;
173
174     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
175     if(SUCCEEDED(hr))
176     {
177         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
178         ITypeInfo_Release(typeinfo);
179     }
180
181     return hr;
182 }
183
184 static HRESULT WINAPI queryresult_Invoke(
185     IXMLDOMNodeList *iface,
186     DISPID dispIdMember,
187     REFIID riid,
188     LCID lcid,
189     WORD wFlags,
190     DISPPARAMS* pDispParams,
191     VARIANT* pVarResult,
192     EXCEPINFO* pExcepInfo,
193     UINT* puArgErr )
194 {
195     queryresult *This = impl_from_IXMLDOMNodeList( iface );
196     ITypeInfo *typeinfo;
197     HRESULT hr;
198
199     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
200           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
201
202     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
203     if(SUCCEEDED(hr))
204     {
205         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
206                 pVarResult, pExcepInfo, puArgErr);
207         ITypeInfo_Release(typeinfo);
208     }
209
210     return hr;
211 }
212
213 static HRESULT WINAPI queryresult_get_item(
214         IXMLDOMNodeList* iface,
215         LONG index,
216         IXMLDOMNode** listItem)
217 {
218     queryresult *This = impl_from_IXMLDOMNodeList( iface );
219
220     TRACE("%p %d\n", This, index);
221
222     if(!listItem)
223         return E_INVALIDARG;
224
225     *listItem = NULL;
226
227     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
228         return S_FALSE;
229
230     *listItem = create_node(This->result->nodesetval->nodeTab[index]);
231     This->resultPos = index + 1;
232
233     return S_OK;
234 }
235
236 static HRESULT WINAPI queryresult_get_length(
237         IXMLDOMNodeList* iface,
238         LONG* listLength)
239 {
240     queryresult *This = impl_from_IXMLDOMNodeList( iface );
241
242     TRACE("%p\n", This);
243
244     if(!listLength)
245         return E_INVALIDARG;
246
247     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
248     return S_OK;
249 }
250
251 static HRESULT WINAPI queryresult_nextNode(
252         IXMLDOMNodeList* iface,
253         IXMLDOMNode** nextItem)
254 {
255     queryresult *This = impl_from_IXMLDOMNodeList( iface );
256
257     TRACE("%p %p\n", This, nextItem );
258
259     if(!nextItem)
260         return E_INVALIDARG;
261
262     *nextItem = NULL;
263
264     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
265         return S_FALSE;
266
267     *nextItem = create_node(This->result->nodesetval->nodeTab[This->resultPos]);
268     This->resultPos++;
269     return S_OK;
270 }
271
272 static HRESULT WINAPI queryresult_reset(
273         IXMLDOMNodeList* iface)
274 {
275     queryresult *This = impl_from_IXMLDOMNodeList( iface );
276
277     TRACE("%p\n", This);
278     This->resultPos = 0;
279     return S_OK;
280 }
281
282 static HRESULT WINAPI queryresult__newEnum(
283         IXMLDOMNodeList* iface,
284         IUnknown** ppUnk)
285 {
286     FIXME("\n");
287     return E_NOTIMPL;
288 }
289
290
291 static const struct IXMLDOMNodeListVtbl queryresult_vtbl =
292 {
293     queryresult_QueryInterface,
294     queryresult_AddRef,
295     queryresult_Release,
296     queryresult_GetTypeInfoCount,
297     queryresult_GetTypeInfo,
298     queryresult_GetIDsOfNames,
299     queryresult_Invoke,
300     queryresult_get_item,
301     queryresult_get_length,
302     queryresult_nextNode,
303     queryresult_reset,
304     queryresult__newEnum,
305 };
306
307 static HRESULT queryresult_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
308 {
309     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
310     WCHAR *ptr;
311     int idx = 0;
312
313     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
314         idx = idx*10 + (*ptr-'0');
315     if(*ptr)
316         return DISP_E_UNKNOWNNAME;
317
318     if(idx >= xmlXPathNodeSetGetLength(This->result->nodesetval))
319         return DISP_E_UNKNOWNNAME;
320
321     *dispid = MSXML_DISPID_CUSTOM_MIN + idx;
322     TRACE("ret %x\n", *dispid);
323     return S_OK;
324 }
325
326 static HRESULT queryresult_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
327         VARIANT *res, EXCEPINFO *ei)
328 {
329     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
330
331     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
332
333     V_VT(res) = VT_DISPATCH;
334     V_DISPATCH(res) = NULL;
335
336     switch(flags)
337     {
338         case INVOKE_PROPERTYGET:
339         {
340             IXMLDOMNode *disp = NULL;
341
342             queryresult_get_item(XMLQUERYRES(This), id - MSXML_DISPID_CUSTOM_MIN, &disp);
343             V_DISPATCH(res) = (IDispatch*)disp;
344             break;
345         }
346         default:
347         {
348             FIXME("unimplemented flags %x\n", flags);
349             break;
350         }
351     }
352
353     TRACE("ret %p\n", V_DISPATCH(res));
354
355     return S_OK;
356 }
357
358 static const dispex_static_data_vtbl_t queryresult_dispex_vtbl = {
359     queryresult_get_dispid,
360     queryresult_invoke
361 };
362
363 static const tid_t queryresult_iface_tids[] = {
364     IXMLDOMNodeList_tid,
365     0
366 };
367 static dispex_static_data_t queryresult_dispex = {
368     &queryresult_dispex_vtbl,
369     IXMLDOMSelection_tid,
370     NULL,
371     queryresult_iface_tids
372 };
373
374 HRESULT queryresult_create(xmlNodePtr node, LPWSTR szQuery, IXMLDOMNodeList **out)
375 {
376     queryresult *This = heap_alloc_zero(sizeof(queryresult));
377     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
378     xmlChar *str = xmlChar_from_wchar(szQuery);
379     HRESULT hr;
380
381
382     TRACE("(%p, %s, %p)\n", node, wine_dbgstr_w(szQuery), out);
383
384     *out = NULL;
385     if (This == NULL || ctxt == NULL || str == NULL)
386     {
387         hr = E_OUTOFMEMORY;
388         goto cleanup;
389     }
390
391     This->lpVtbl = &queryresult_vtbl;
392     This->ref = 1;
393     This->resultPos = 0;
394     This->node = node;
395     xmldoc_add_ref(This->node->doc);
396
397     ctxt->node = node;
398     This->result = xmlXPathEval(str, ctxt);
399     if (!This->result || This->result->type != XPATH_NODESET)
400     {
401         hr = E_FAIL;
402         goto cleanup;
403     }
404
405     init_dispex(&This->dispex, (IUnknown*)&This->lpVtbl, &queryresult_dispex);
406
407     *out = (IXMLDOMNodeList *) &This->lpVtbl;
408     hr = S_OK;
409     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
410
411 cleanup:
412     if (This != NULL && FAILED(hr))
413         IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl );
414     if (ctxt != NULL)
415         xmlXPathFreeContext(ctxt);
416     heap_free(str);
417     return hr;
418 }
419
420 #endif