* Sync up to trunk HEAD (r62975).
[reactos.git] / dll / win32 / msxml3 / nodelist.c
1 /*
2 * Node list implementation
3 *
4 * Copyright 2005 Mike McCormack
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22
23 #include <msxml2did.h>
24
25 /* This file implements the object returned by childNodes property. Note that this is
26 * not the IXMLDOMNodeList returned by XPath queries - it's implemented in selection.c.
27 * They are different because the list returned by childNodes:
28 * - is "live" - changes to the XML tree are automatically reflected in the list
29 * - doesn't supports IXMLDOMSelection
30 * - note that an attribute node have a text child in DOM but not in the XPath data model
31 * thus the child is inaccessible by an XPath query
32 */
33
34 #ifdef HAVE_LIBXML2
35
36 typedef struct
37 {
38 DispatchEx dispex;
39 IXMLDOMNodeList IXMLDOMNodeList_iface;
40 LONG ref;
41 xmlNodePtr parent;
42 xmlNodePtr current;
43 IEnumVARIANT *enumvariant;
44 } xmlnodelist;
45
46 static HRESULT nodelist_get_item(IUnknown *iface, LONG index, VARIANT *item)
47 {
48 V_VT(item) = VT_DISPATCH;
49 return IXMLDOMNodeList_get_item((IXMLDOMNodeList*)iface, index, (IXMLDOMNode**)&V_DISPATCH(item));
50 }
51
52 static const struct enumvariant_funcs nodelist_enumvariant = {
53 nodelist_get_item,
54 NULL
55 };
56
57 static inline xmlnodelist *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
58 {
59 return CONTAINING_RECORD(iface, xmlnodelist, IXMLDOMNodeList_iface);
60 }
61
62 static HRESULT WINAPI xmlnodelist_QueryInterface(
63 IXMLDOMNodeList *iface,
64 REFIID riid,
65 void** ppvObject )
66 {
67 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
68
69 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
70
71 #ifdef __REACTOS__
72 if (!ppvObject)
73 {
74 /* NOTE: Interface documentation for IUnknown explicitly states
75 * this case should return E_POINTER. Empirical data proves
76 * MS violates this contract and instead return E_INVALIDARG.
77 */
78 return E_INVALIDARG;
79 }
80 #endif
81
82 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
83 IsEqualGUID( riid, &IID_IDispatch ) ||
84 IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
85 {
86 *ppvObject = iface;
87 }
88 else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
89 {
90 if (!This->enumvariant)
91 {
92 HRESULT hr = create_enumvariant((IUnknown*)iface, FALSE, &nodelist_enumvariant, &This->enumvariant);
93 if (FAILED(hr)) return hr;
94 }
95
96 return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
97 }
98 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
99 {
100 return *ppvObject ? S_OK : E_NOINTERFACE;
101 }
102 else
103 {
104 TRACE("interface %s not implemented\n", debugstr_guid(riid));
105 *ppvObject = NULL;
106 return E_NOINTERFACE;
107 }
108
109 IXMLDOMNodeList_AddRef( iface );
110
111 return S_OK;
112 }
113
114 static ULONG WINAPI xmlnodelist_AddRef(
115 IXMLDOMNodeList *iface )
116 {
117 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
118 ULONG ref = InterlockedIncrement( &This->ref );
119 TRACE("(%p)->(%d)\n", This, ref);
120 return ref;
121 }
122
123 static ULONG WINAPI xmlnodelist_Release(
124 IXMLDOMNodeList *iface )
125 {
126 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
127 ULONG ref = InterlockedDecrement( &This->ref );
128
129 TRACE("(%p)->(%d)\n", This, ref);
130 if ( ref == 0 )
131 {
132 xmldoc_release( This->parent->doc );
133 if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
134 heap_free( This );
135 }
136
137 return ref;
138 }
139
140 static HRESULT WINAPI xmlnodelist_GetTypeInfoCount(
141 IXMLDOMNodeList *iface,
142 UINT* pctinfo )
143 {
144 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
145 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
146 }
147
148 static HRESULT WINAPI xmlnodelist_GetTypeInfo(
149 IXMLDOMNodeList *iface,
150 UINT iTInfo,
151 LCID lcid,
152 ITypeInfo** ppTInfo )
153 {
154 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
155 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
156 iTInfo, lcid, ppTInfo);
157 }
158
159 static HRESULT WINAPI xmlnodelist_GetIDsOfNames(
160 IXMLDOMNodeList *iface,
161 REFIID riid,
162 LPOLESTR* rgszNames,
163 UINT cNames,
164 LCID lcid,
165 DISPID* rgDispId )
166 {
167 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
168 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
169 riid, rgszNames, cNames, lcid, rgDispId);
170 }
171
172 static HRESULT WINAPI xmlnodelist_Invoke(
173 IXMLDOMNodeList *iface,
174 DISPID dispIdMember,
175 REFIID riid,
176 LCID lcid,
177 WORD wFlags,
178 DISPPARAMS* pDispParams,
179 VARIANT* pVarResult,
180 EXCEPINFO* pExcepInfo,
181 UINT* puArgErr )
182 {
183 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
184 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
185 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
186 }
187
188 static HRESULT WINAPI xmlnodelist_get_item(
189 IXMLDOMNodeList* iface,
190 LONG index,
191 IXMLDOMNode** listItem)
192 {
193 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
194 xmlNodePtr curr;
195 LONG nodeIndex = 0;
196
197 TRACE("(%p)->(%d %p)\n", This, index, listItem);
198
199 if(!listItem)
200 return E_INVALIDARG;
201
202 *listItem = NULL;
203
204 if (index < 0)
205 return S_FALSE;
206
207 curr = This->parent->children;
208 while(curr)
209 {
210 if(nodeIndex++ == index) break;
211 curr = curr->next;
212 }
213 if(!curr) return S_FALSE;
214
215 *listItem = create_node( curr );
216
217 return S_OK;
218 }
219
220 static HRESULT WINAPI xmlnodelist_get_length(
221 IXMLDOMNodeList* iface,
222 LONG* listLength)
223 {
224
225 xmlNodePtr curr;
226 LONG nodeCount = 0;
227
228 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
229
230 TRACE("(%p)->(%p)\n", This, listLength);
231
232 if(!listLength)
233 return E_INVALIDARG;
234
235 curr = This->parent->children;
236 while (curr)
237 {
238 nodeCount++;
239 curr = curr->next;
240 }
241
242 *listLength = nodeCount;
243 return S_OK;
244 }
245
246 static HRESULT WINAPI xmlnodelist_nextNode(
247 IXMLDOMNodeList* iface,
248 IXMLDOMNode** nextItem)
249 {
250 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
251
252 TRACE("(%p)->(%p)\n", This, nextItem );
253
254 if(!nextItem)
255 return E_INVALIDARG;
256
257 *nextItem = NULL;
258
259 if (!This->current)
260 return S_FALSE;
261
262 *nextItem = create_node( This->current );
263 This->current = This->current->next;
264 return S_OK;
265 }
266
267 static HRESULT WINAPI xmlnodelist_reset(
268 IXMLDOMNodeList* iface)
269 {
270 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
271
272 TRACE("%p\n", This);
273 This->current = This->parent->children;
274 return S_OK;
275 }
276
277 static HRESULT WINAPI xmlnodelist__newEnum(
278 IXMLDOMNodeList* iface,
279 IUnknown** enumv)
280 {
281 xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
282 TRACE("(%p)->(%p)\n", This, enumv);
283 return create_enumvariant((IUnknown*)iface, TRUE, &nodelist_enumvariant, (IEnumVARIANT**)enumv);
284 }
285
286 static const struct IXMLDOMNodeListVtbl xmlnodelist_vtbl =
287 {
288 xmlnodelist_QueryInterface,
289 xmlnodelist_AddRef,
290 xmlnodelist_Release,
291 xmlnodelist_GetTypeInfoCount,
292 xmlnodelist_GetTypeInfo,
293 xmlnodelist_GetIDsOfNames,
294 xmlnodelist_Invoke,
295 xmlnodelist_get_item,
296 xmlnodelist_get_length,
297 xmlnodelist_nextNode,
298 xmlnodelist_reset,
299 xmlnodelist__newEnum,
300 };
301
302 static HRESULT xmlnodelist_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
303 {
304 WCHAR *ptr;
305 int idx = 0;
306
307 for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
308 idx = idx*10 + (*ptr-'0');
309 if(*ptr)
310 return DISP_E_UNKNOWNNAME;
311
312 *dispid = DISPID_DOM_COLLECTION_BASE + idx;
313 TRACE("ret %x\n", *dispid);
314 return S_OK;
315 }
316
317 static HRESULT xmlnodelist_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
318 VARIANT *res, EXCEPINFO *ei)
319 {
320 xmlnodelist *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
321
322 TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
323
324 if (id >= DISPID_DOM_COLLECTION_BASE && id <= DISPID_DOM_COLLECTION_MAX)
325 {
326 switch(flags)
327 {
328 case DISPATCH_PROPERTYGET:
329 {
330 IXMLDOMNode *disp = NULL;
331
332 V_VT(res) = VT_DISPATCH;
333 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
334 V_DISPATCH(res) = (IDispatch*)disp;
335 break;
336 }
337 default:
338 {
339 FIXME("unimplemented flags %x\n", flags);
340 break;
341 }
342 }
343 }
344 else if (id == DISPID_VALUE)
345 {
346 switch(flags)
347 {
348 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
349 case DISPATCH_PROPERTYGET:
350 case DISPATCH_METHOD:
351 {
352 IXMLDOMNode *item;
353 VARIANT index;
354 HRESULT hr;
355
356 if (params->cArgs - params->cNamedArgs != 1) return DISP_E_BADPARAMCOUNT;
357
358 VariantInit(&index);
359 hr = VariantChangeType(&index, params->rgvarg, 0, VT_I4);
360 if(FAILED(hr))
361 {
362 FIXME("failed to convert arg, %s\n", debugstr_variant(params->rgvarg));
363 return hr;
364 }
365
366 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, V_I4(&index), &item);
367 V_VT(res) = VT_DISPATCH;
368 V_DISPATCH(res) = (IDispatch*)item;
369 break;
370 }
371 default:
372 {
373 FIXME("DISPID_VALUE: unimplemented flags %x\n", flags);
374 break;
375 }
376 }
377 }
378 else
379 return DISP_E_UNKNOWNNAME;
380
381 TRACE("ret %p\n", V_DISPATCH(res));
382
383 return S_OK;
384 }
385
386 static const dispex_static_data_vtbl_t xmlnodelist_dispex_vtbl = {
387 xmlnodelist_get_dispid,
388 xmlnodelist_invoke
389 };
390
391 static const tid_t xmlnodelist_iface_tids[] = {
392 IXMLDOMNodeList_tid,
393 0
394 };
395 static dispex_static_data_t xmlnodelist_dispex = {
396 &xmlnodelist_dispex_vtbl,
397 IXMLDOMNodeList_tid,
398 NULL,
399 xmlnodelist_iface_tids
400 };
401
402 IXMLDOMNodeList* create_children_nodelist( xmlNodePtr node )
403 {
404 xmlnodelist *This;
405
406 This = heap_alloc( sizeof *This );
407 if ( !This )
408 return NULL;
409
410 This->IXMLDOMNodeList_iface.lpVtbl = &xmlnodelist_vtbl;
411 This->ref = 1;
412 This->parent = node;
413 This->current = node->children;
414 This->enumvariant = NULL;
415 xmldoc_add_ref( node->doc );
416
417 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMNodeList_iface, &xmlnodelist_dispex);
418
419 return &This->IXMLDOMNodeList_iface;
420 }
421
422 #endif