jscript: Added VBArray.dimensions() implementation.
[wine] / dlls / msxml3 / schema.c
1 /*
2  * Schema cache implementation
3  *
4  * Copyright 2007 Huw Davies
5  * Copyright 2010 Adam Martinson for CodeWeavers
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 <assert.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "msxml6.h"
33
34 #include "wine/debug.h"
35
36 #include "msxml_private.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
39
40 /* We use a chained hashtable, which can hold any number of schemas
41  * TODO: XDR schema support
42  * TODO: grow/shrink hashtable depending on load factor
43  * TODO: implement read-only where appropriate
44  */
45
46 /* This is just the number of buckets, should be prime */
47 #define DEFAULT_HASHTABLE_SIZE 31
48
49 #ifdef HAVE_LIBXML2
50
51 #include <libxml/tree.h>
52 #include <libxml/xmlschemas.h>
53 #include <libxml/schemasInternals.h>
54 #include <libxml/hash.h>
55
56 static const xmlChar XSD_schema[] = "schema";
57 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
58 static const xmlChar XDR_schema[] = "Schema";
59 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
60
61 /* Supported Types:
62  * msxml3 - XDR only
63  * msxml4 - XDR & XSD
64  * msxml5 - XDR & XSD
65  * mxsml6 - XSD only
66  */
67 typedef enum _SCHEMA_TYPE {
68     SCHEMA_TYPE_INVALID,
69     SCHEMA_TYPE_XDR,
70     SCHEMA_TYPE_XSD
71 } SCHEMA_TYPE;
72
73 typedef struct _schema_cache
74 {
75     const struct IXMLDOMSchemaCollectionVtbl *lpVtbl;
76     xmlHashTablePtr cache;
77     LONG ref;
78 } schema_cache;
79
80 typedef struct _cache_entry
81 {
82     SCHEMA_TYPE type;
83     xmlSchemaPtr schema;
84     xmlDocPtr doc;
85     LONG ref;
86 } cache_entry;
87
88 static LONG cache_entry_add_ref(cache_entry* entry)
89 {
90     LONG ref = InterlockedIncrement(&entry->ref);
91     TRACE("%p new ref %d\n", entry, ref);
92     return ref;
93 }
94
95 static LONG cache_entry_release(cache_entry* entry)
96 {
97     LONG ref = InterlockedDecrement(&entry->ref);
98     TRACE("%p new ref %d\n", entry, ref);
99
100     if (ref == 0)
101     {
102         if (entry->type == SCHEMA_TYPE_XSD)
103         {
104             xmldoc_release(entry->doc);
105             entry->schema->doc = NULL;
106             xmlSchemaFree(entry->schema);
107             heap_free(entry);
108         }
109         else /* SCHEMA_TYPE_XDR */
110         {
111             xmldoc_release(entry->doc);
112             heap_free(entry);
113         }
114     }
115     return ref;
116 }
117
118 static inline schema_cache *impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
119 {
120     return (schema_cache *)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
121 }
122
123 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
124 {
125     xmlNodePtr root;
126     if (schema)
127         root = xmlDocGetRootElement(schema);
128     if (root && root->ns)
129     {
130
131         if (xmlStrEqual(root->name, XDR_schema) &&
132             xmlStrEqual(root->ns->href, XDR_nsURI))
133         {
134             return SCHEMA_TYPE_XDR;
135         }
136         else if (xmlStrEqual(root->name, XSD_schema) &&
137                  xmlStrEqual(root->ns->href, XSD_nsURI))
138         {
139             return SCHEMA_TYPE_XSD;
140         }
141     }
142     return SCHEMA_TYPE_INVALID;
143 }
144
145 static cache_entry* cache_entry_from_url(char const* url)
146 {
147     cache_entry* entry = heap_alloc(sizeof(cache_entry));
148     xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
149     entry->type = SCHEMA_TYPE_XSD;
150     entry->ref = 0;
151     if (spctx)
152     {
153         if((entry->schema = xmlSchemaParse(spctx)))
154         {
155             xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
156             entry->doc = entry->schema->doc;
157             xmldoc_add_ref(entry->doc);
158         }
159         else
160         {
161             heap_free(entry);
162             entry = NULL;
163         }
164         xmlSchemaFreeParserCtxt(spctx);
165     }
166     else
167     {
168         FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
169         heap_free(entry);
170         entry = NULL;
171     }
172     return entry;
173 }
174
175 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc)
176 {
177     cache_entry* entry = heap_alloc(sizeof(cache_entry));
178     xmlSchemaParserCtxtPtr spctx;
179     xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
180
181     entry->type = SCHEMA_TYPE_XSD;
182     entry->ref = 0;
183     spctx = xmlSchemaNewDocParserCtxt(new_doc);
184
185     if ((entry->schema = xmlSchemaParse(spctx)))
186     {
187         xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
188         entry->doc = entry->schema->doc;
189         xmldoc_add_ref(entry->doc);
190     }
191     else
192     {
193         FIXME("failed to parse doc\n");
194         xmlFreeDoc(new_doc);
195         heap_free(entry);
196         entry = NULL;
197     }
198     xmlSchemaFreeParserCtxt(spctx);
199     return entry;
200 }
201
202 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc)
203 {
204     cache_entry* entry = heap_alloc(sizeof(cache_entry));
205     xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
206
207     FIXME("XDR schema support not implemented\n");
208     entry->type = SCHEMA_TYPE_XDR;
209     entry->ref = 0;
210     entry->schema = NULL;
211     entry->doc = new_doc;
212     xmldoc_init(entry->doc, &CLSID_DOMDocument30);
213     xmldoc_add_ref(entry->doc);
214
215     return entry;
216 }
217
218 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection *iface, REFIID riid, void** ppvObject)
219 {
220     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
221
222     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
223
224     if ( IsEqualIID(riid, &IID_IUnknown) ||
225          IsEqualIID(riid, &IID_IDispatch) ||
226          IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) )
227     {
228         *ppvObject = iface;
229     }
230     else
231     {
232         FIXME("interface %s not implemented\n", debugstr_guid(riid));
233         return E_NOINTERFACE;
234     }
235
236     IXMLDOMSchemaCollection_AddRef(iface);
237
238     return S_OK;
239 }
240
241 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection *iface)
242 {
243     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
244     LONG ref = InterlockedIncrement(&This->ref);
245     TRACE("%p new ref %d\n", This, ref);
246     return ref;
247 }
248
249 static void cache_free(void* data, xmlChar* name /* ignored */)
250 {
251     cache_entry_release((cache_entry*)data);
252 }
253
254 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection *iface)
255 {
256     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
257     LONG ref = InterlockedDecrement(&This->ref);
258     TRACE("%p new ref %d\n", This, ref);
259
260     if (ref == 0)
261     {
262         xmlHashFree(This->cache, cache_free);
263         heap_free(This);
264     }
265
266     return ref;
267 }
268
269 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection *iface, UINT* pctinfo)
270 {
271     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
272
273     TRACE("(%p)->(%p)\n", This, pctinfo);
274
275     *pctinfo = 1;
276
277     return S_OK;
278 }
279
280 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection *iface,
281                                                UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
282 {
283     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
284     HRESULT hr;
285
286     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
287
288     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
289
290     return hr;
291 }
292
293 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection *iface,
294                                                  REFIID riid,
295                                                  LPOLESTR* rgszNames,
296                                                  UINT cNames,
297                                                  LCID lcid,
298                                                  DISPID* rgDispId)
299 {
300     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
301     ITypeInfo *typeinfo;
302     HRESULT hr;
303
304     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
305           lcid, rgDispId);
306
307     if(!rgszNames || cNames == 0 || !rgDispId)
308         return E_INVALIDARG;
309
310     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
311     if(SUCCEEDED(hr))
312     {
313         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
314         ITypeInfo_Release(typeinfo);
315     }
316
317     return hr;
318 }
319
320 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection *iface,
321                                           DISPID dispIdMember,
322                                           REFIID riid,
323                                           LCID lcid,
324                                           WORD wFlags,
325                                           DISPPARAMS* pDispParams,
326                                           VARIANT* pVarResult,
327                                           EXCEPINFO* pExcepInfo,
328                                           UINT* puArgErr)
329 {
330     schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
331     ITypeInfo *typeinfo;
332     HRESULT hr;
333
334     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
335           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
336
337     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
338     if(SUCCEEDED(hr))
339     {
340         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
341                 pVarResult, pExcepInfo, puArgErr);
342         ITypeInfo_Release(typeinfo);
343     }
344
345     return hr;
346 }
347
348 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection *iface, BSTR uri, VARIANT var)
349 {
350     schema_cache *This = impl_from_IXMLDOMSchemaCollection( iface );
351     xmlChar* name = xmlChar_from_wchar(uri);
352     TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
353
354     switch (V_VT(&var))
355     {
356         case VT_NULL:
357             {
358                 xmlHashRemoveEntry(This->cache, name, cache_free);
359             }
360             break;
361
362         case VT_BSTR:
363             {
364                 xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
365                 cache_entry* entry = cache_entry_from_url((char const*)url);
366                 heap_free(url);
367
368                 if (entry)
369                 {
370                     cache_entry_add_ref(entry);
371                 }
372                 else
373                 {
374                     heap_free(name);
375                     return E_FAIL;
376                 }
377
378                 xmlHashRemoveEntry(This->cache, name, cache_free);
379                 xmlHashAddEntry(This->cache, name, entry);
380             }
381             break;
382
383         case VT_DISPATCH:
384             {
385                 xmlDocPtr doc = NULL;
386                 cache_entry* entry;
387                 SCHEMA_TYPE type;
388                 IXMLDOMNode* domnode = NULL;
389                 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
390
391                 if (domnode)
392                     doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
393
394                 if (!doc)
395                 {
396                     IXMLDOMNode_Release(domnode);
397                     heap_free(name);
398                     return E_INVALIDARG;
399                 }
400                 type = schema_type_from_xmlDocPtr(doc);
401
402                 if (type == SCHEMA_TYPE_XSD)
403                 {
404                     entry = cache_entry_from_xsd_doc(doc);
405                 }
406                 else if (type == SCHEMA_TYPE_XDR)
407                 {
408                     entry = cache_entry_from_xdr_doc(doc);
409                 }
410                 else
411                 {
412                     WARN("invalid schema!\n");
413                     entry = NULL;
414                 }
415
416                 IXMLDOMNode_Release(domnode);
417
418                 if (entry)
419                 {
420                     cache_entry_add_ref(entry);
421                 }
422                 else
423                 {
424                     heap_free(name);
425                     return E_FAIL;
426                 }
427
428                 xmlHashRemoveEntry(This->cache, name, cache_free);
429                 xmlHashAddEntry(This->cache, name, entry);
430             }
431             break;
432
433         default:
434             {
435                 heap_free(name);
436                 return E_INVALIDARG;
437             }
438     }
439     heap_free(name);
440     return S_OK;
441 }
442
443 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection *iface, BSTR uri, IXMLDOMNode **node)
444 {
445     FIXME("stub\n");
446     return E_NOTIMPL;
447 }
448
449 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection *iface, BSTR uri)
450 {
451     FIXME("stub\n");
452     return E_NOTIMPL;
453 }
454
455 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection *iface, LONG *length)
456 {
457     FIXME("stub\n");
458     return E_NOTIMPL;
459 }
460
461 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection *iface, LONG index, BSTR *len)
462 {
463     FIXME("stub\n");
464     return E_NOTIMPL;
465 }
466
467 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection *iface, IXMLDOMSchemaCollection *otherCollection)
468 {
469     FIXME("stub\n");
470     return E_NOTIMPL;
471 }
472
473 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection *iface, IUnknown **ppUnk)
474 {
475     FIXME("stub\n");
476     return E_NOTIMPL;
477 }
478
479 /* TODO: validate? validateOnLoad property? */
480 static const struct IXMLDOMSchemaCollectionVtbl schema_vtbl =
481 {
482     schema_cache_QueryInterface,
483     schema_cache_AddRef,
484     schema_cache_Release,
485     schema_cache_GetTypeInfoCount,
486     schema_cache_GetTypeInfo,
487     schema_cache_GetIDsOfNames,
488     schema_cache_Invoke,
489     schema_cache_add,
490     schema_cache_get,
491     schema_cache_remove,
492     schema_cache_get_length,
493     schema_cache_get_namespaceURI,
494     schema_cache_addCollection,
495     schema_cache_get__newEnum
496 };
497
498 HRESULT SchemaCache_create(IUnknown *pUnkOuter, LPVOID *ppObj)
499 {
500     schema_cache *schema = heap_alloc( sizeof (*schema) );
501     if( !schema )
502         return E_OUTOFMEMORY;
503
504     schema->lpVtbl = &schema_vtbl;
505     schema->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
506     schema->ref = 1;
507
508     *ppObj = &schema->lpVtbl;
509     return S_OK;
510 }
511
512 #else
513
514 HRESULT SchemaCache_create(IUnknown *pUnkOuter, LPVOID *ppObj)
515 {
516     MESSAGE("This program tried to use a SchemaCache object, but\n"
517             "libxml2 support was not present at compile time.\n");
518     return E_NOTIMPL;
519 }
520
521 #endif