Release 1.3.7.
[wine/gsoc-2012-control.git] / dlls / msxml3 / schema.c
blobc9016cec249ca8bef8257fac1290816e02dbd618
1 /*
2 * Schema cache implementation
4 * Copyright 2007 Huw Davies
5 * Copyright 2010 Adam Martinson for CodeWeavers
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.
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.
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
22 #define COBJMACROS
24 #include "config.h"
26 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "msxml6.h"
33 #include "wine/debug.h"
35 #include "msxml_private.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
39 /* We use a chained hashtable, which can hold any number of schemas
40 * TODO: XDR schema support
41 * TODO: grow/shrink hashtable depending on load factor
42 * TODO: implement read-only where appropriate
45 /* This is just the number of buckets, should be prime */
46 #define DEFAULT_HASHTABLE_SIZE 31
48 #ifdef HAVE_LIBXML2
50 #include <libxml/tree.h>
51 #include <libxml/xmlschemas.h>
52 #include <libxml/schemasInternals.h>
53 #include <libxml/hash.h>
55 static const xmlChar XSD_schema[] = "schema";
56 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
57 static const xmlChar XDR_schema[] = "Schema";
58 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
60 /* Supported Types:
61 * msxml3 - XDR only
62 * msxml4 - XDR & XSD
63 * msxml5 - XDR & XSD
64 * mxsml6 - XSD only
66 typedef enum _SCHEMA_TYPE {
67 SCHEMA_TYPE_INVALID,
68 SCHEMA_TYPE_XDR,
69 SCHEMA_TYPE_XSD
70 } SCHEMA_TYPE;
72 typedef struct _schema_cache
74 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
75 xmlHashTablePtr cache;
76 LONG ref;
77 } schema_cache;
79 typedef struct _cache_entry
81 SCHEMA_TYPE type;
82 xmlSchemaPtr schema;
83 xmlDocPtr doc;
84 LONG ref;
85 } cache_entry;
87 typedef struct _cache_index_data
89 LONG index;
90 BSTR* out;
91 } cache_index_data;
93 static LONG cache_entry_add_ref(cache_entry* entry)
95 LONG ref = InterlockedIncrement(&entry->ref);
96 TRACE("%p new ref %d\n", entry, ref);
97 return ref;
100 static LONG cache_entry_release(cache_entry* entry)
102 LONG ref = InterlockedDecrement(&entry->ref);
103 TRACE("%p new ref %d\n", entry, ref);
105 if (ref == 0)
107 if (entry->type == SCHEMA_TYPE_XSD)
109 xmldoc_release(entry->doc);
110 entry->schema->doc = NULL;
111 xmlSchemaFree(entry->schema);
112 heap_free(entry);
114 else /* SCHEMA_TYPE_XDR */
116 xmldoc_release(entry->doc);
117 heap_free(entry);
120 return ref;
123 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
125 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
128 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
130 xmlNodePtr root;
131 if (schema)
132 root = xmlDocGetRootElement(schema);
133 if (root && root->ns)
136 if (xmlStrEqual(root->name, XDR_schema) &&
137 xmlStrEqual(root->ns->href, XDR_nsURI))
139 return SCHEMA_TYPE_XDR;
141 else if (xmlStrEqual(root->name, XSD_schema) &&
142 xmlStrEqual(root->ns->href, XSD_nsURI))
144 return SCHEMA_TYPE_XSD;
147 return SCHEMA_TYPE_INVALID;
150 static cache_entry* cache_entry_from_url(char const* url)
152 cache_entry* entry = heap_alloc(sizeof(cache_entry));
153 xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
154 entry->type = SCHEMA_TYPE_XSD;
155 entry->ref = 0;
156 if (spctx)
158 if((entry->schema = xmlSchemaParse(spctx)))
160 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
161 entry->doc = entry->schema->doc;
162 xmldoc_add_ref(entry->doc);
164 else
166 heap_free(entry);
167 entry = NULL;
169 xmlSchemaFreeParserCtxt(spctx);
171 else
173 FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
174 heap_free(entry);
175 entry = NULL;
177 return entry;
180 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc)
182 cache_entry* entry = heap_alloc(sizeof(cache_entry));
183 xmlSchemaParserCtxtPtr spctx;
184 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
186 entry->type = SCHEMA_TYPE_XSD;
187 entry->ref = 0;
188 spctx = xmlSchemaNewDocParserCtxt(new_doc);
190 if ((entry->schema = xmlSchemaParse(spctx)))
192 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
193 entry->doc = entry->schema->doc;
194 xmldoc_add_ref(entry->doc);
196 else
198 FIXME("failed to parse doc\n");
199 xmlFreeDoc(new_doc);
200 heap_free(entry);
201 entry = NULL;
203 xmlSchemaFreeParserCtxt(spctx);
204 return entry;
207 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc)
209 cache_entry* entry = heap_alloc(sizeof(cache_entry));
210 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
212 FIXME("XDR schema support not implemented\n");
213 entry->type = SCHEMA_TYPE_XDR;
214 entry->ref = 0;
215 entry->schema = NULL;
216 entry->doc = new_doc;
217 xmldoc_init(entry->doc, &CLSID_DOMDocument30);
218 xmldoc_add_ref(entry->doc);
220 return entry;
223 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
224 REFIID riid, void** ppvObject)
226 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
228 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
230 if ( IsEqualIID(riid, &IID_IUnknown) ||
231 IsEqualIID(riid, &IID_IDispatch) ||
232 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
233 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
235 *ppvObject = iface;
237 else
239 FIXME("interface %s not implemented\n", debugstr_guid(riid));
240 return E_NOINTERFACE;
243 IXMLDOMSchemaCollection2_AddRef(iface);
245 return S_OK;
248 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
250 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
251 LONG ref = InterlockedIncrement(&This->ref);
252 TRACE("%p new ref %d\n", This, ref);
253 return ref;
256 static void cache_free(void* data, xmlChar* name /* ignored */)
258 cache_entry_release((cache_entry*)data);
261 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
263 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
264 LONG ref = InterlockedDecrement(&This->ref);
265 TRACE("%p new ref %d\n", This, ref);
267 if (ref == 0)
269 xmlHashFree(This->cache, cache_free);
270 heap_free(This);
273 return ref;
276 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
277 UINT* pctinfo)
279 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
281 TRACE("(%p)->(%p)\n", This, pctinfo);
283 *pctinfo = 1;
285 return S_OK;
288 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
289 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
291 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
292 HRESULT hr;
294 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
296 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
298 return hr;
301 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
302 REFIID riid, LPOLESTR* rgszNames,
303 UINT cNames, LCID lcid, DISPID* rgDispId)
305 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
306 ITypeInfo* typeinfo;
307 HRESULT hr;
309 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
310 lcid, rgDispId);
312 if(!rgszNames || cNames == 0 || !rgDispId)
313 return E_INVALIDARG;
315 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
316 if(SUCCEEDED(hr))
318 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
319 ITypeInfo_Release(typeinfo);
322 return hr;
325 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
326 DISPID dispIdMember, REFIID riid, LCID lcid,
327 WORD wFlags, DISPPARAMS* pDispParams,
328 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
329 UINT* puArgErr)
331 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
332 ITypeInfo* typeinfo;
333 HRESULT hr;
335 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
336 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
338 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
339 if(SUCCEEDED(hr))
341 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
342 pVarResult, pExcepInfo, puArgErr);
343 ITypeInfo_Release(typeinfo);
346 return hr;
349 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
351 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
352 xmlChar* name = xmlChar_from_wchar(uri);
353 TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
355 switch (V_VT(&var))
357 case VT_NULL:
359 xmlHashRemoveEntry(This->cache, name, cache_free);
361 break;
363 case VT_BSTR:
365 xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
366 cache_entry* entry = cache_entry_from_url((char const*)url);
367 heap_free(url);
369 if (entry)
371 cache_entry_add_ref(entry);
373 else
375 heap_free(name);
376 return E_FAIL;
379 xmlHashRemoveEntry(This->cache, name, cache_free);
380 xmlHashAddEntry(This->cache, name, entry);
382 break;
384 case VT_DISPATCH:
386 xmlDocPtr doc = NULL;
387 cache_entry* entry;
388 SCHEMA_TYPE type;
389 IXMLDOMNode* domnode = NULL;
390 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
392 if (domnode)
393 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
395 if (!doc)
397 IXMLDOMNode_Release(domnode);
398 heap_free(name);
399 return E_INVALIDARG;
401 type = schema_type_from_xmlDocPtr(doc);
403 if (type == SCHEMA_TYPE_XSD)
405 entry = cache_entry_from_xsd_doc(doc);
407 else if (type == SCHEMA_TYPE_XDR)
409 entry = cache_entry_from_xdr_doc(doc);
411 else
413 WARN("invalid schema!\n");
414 entry = NULL;
417 IXMLDOMNode_Release(domnode);
419 if (entry)
421 cache_entry_add_ref(entry);
423 else
425 heap_free(name);
426 return E_FAIL;
429 xmlHashRemoveEntry(This->cache, name, cache_free);
430 xmlHashAddEntry(This->cache, name, entry);
432 break;
434 default:
436 heap_free(name);
437 return E_INVALIDARG;
440 heap_free(name);
441 return S_OK;
444 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
445 IXMLDOMNode** node)
447 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
448 xmlChar* name;
449 cache_entry* entry;
450 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
452 if (!node)
453 return E_POINTER;
455 name = xmlChar_from_wchar(uri);
456 entry = (cache_entry*) xmlHashLookup(This->cache, name);
457 heap_free(name);
459 /* TODO: this should be read-only */
460 if (entry)
461 return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
463 *node = NULL;
464 return S_OK;
467 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
469 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
470 xmlChar* name = xmlChar_from_wchar(uri);
471 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
473 xmlHashRemoveEntry(This->cache, name, cache_free);
474 heap_free(name);
475 return S_OK;
478 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
480 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
481 TRACE("(%p)->(%p)\n", This, length);
483 if (!length)
484 return E_POINTER;
485 *length = xmlHashSize(This->cache);
486 return S_OK;
489 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
491 cache_index_data* index_data = (cache_index_data*)index;
493 if (index_data->index-- == 0)
494 *index_data->out = bstr_from_xmlChar(name);
497 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
498 LONG index, BSTR* len)
500 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
501 cache_index_data data = {index,len};
502 TRACE("(%p)->(%i, %p)\n", This, index, len);
504 if (!len)
505 return E_POINTER;
506 *len = NULL;
508 if (index >= xmlHashSize(This->cache))
509 return E_FAIL;
511 xmlHashScan(This->cache, cache_index, &data);
512 return S_OK;
515 static void cache_copy(void* data, void* dest, xmlChar* name)
517 schema_cache* This = (schema_cache*) dest;
518 cache_entry* entry = (cache_entry*) data;
520 if (xmlHashLookup(This->cache, name) == NULL)
522 cache_entry_add_ref(entry);
523 xmlHashAddEntry(This->cache, name, entry);
527 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
528 IXMLDOMSchemaCollection* otherCollection)
530 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
531 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
532 TRACE("(%p)->(%p)\n", This, That);
534 if (!otherCollection)
535 return E_POINTER;
537 /* TODO: detect errors while copying & return E_FAIL */
538 xmlHashScan(That->cache, cache_copy, This);
540 return S_OK;
543 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
544 IUnknown** ppUnk)
546 FIXME("stub\n");
547 if (ppUnk)
548 *ppUnk = NULL;
549 return E_NOTIMPL;
552 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
554 FIXME("stub\n");
555 return E_NOTIMPL;
558 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
559 VARIANT_BOOL validateOnLoad)
561 FIXME("stub\n");
562 return E_NOTIMPL;
565 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
566 VARIANT_BOOL* validateOnLoad)
568 FIXME("stub\n");
569 return E_NOTIMPL;
572 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
573 BSTR namespaceURI, ISchema** schema)
575 FIXME("stub\n");
576 if (schema)
577 *schema = NULL;
578 return E_NOTIMPL;
581 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
582 IXMLDOMNode* node, ISchemaItem** item)
584 FIXME("stub\n");
585 if (item)
586 *item = NULL;
587 return E_NOTIMPL;
590 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
592 schema_cache_QueryInterface,
593 schema_cache_AddRef,
594 schema_cache_Release,
595 schema_cache_GetTypeInfoCount,
596 schema_cache_GetTypeInfo,
597 schema_cache_GetIDsOfNames,
598 schema_cache_Invoke,
599 schema_cache_add,
600 schema_cache_get,
601 schema_cache_remove,
602 schema_cache_get_length,
603 schema_cache_get_namespaceURI,
604 schema_cache_addCollection,
605 schema_cache_get__newEnum,
606 schema_cache_validate,
607 schema_cache_put_validateOnLoad,
608 schema_cache_get_validateOnLoad,
609 schema_cache_getSchema,
610 schema_cache_getDeclaration
613 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
615 va_list ap;
616 va_start(ap, msg);
617 LIBXML2_CALLBACK_ERR(SchemaCache_validate_tree, msg, ap);
618 va_end(ap);
621 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
623 va_list ap;
624 va_start(ap, msg);
625 LIBXML2_CALLBACK_WARN(SchemaCache_validate_tree, msg, ap);
626 va_end(ap);
629 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
630 static void validate_serror(void* ctx, xmlErrorPtr err)
632 LIBXML2_CALLBACK_SERROR(SchemaCache_validate_tree, err);
634 #endif
636 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
638 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
639 cache_entry* entry;
640 xmlChar const* ns = NULL;
641 TRACE("(%p, %p)\n", This, tree);
643 if (!tree)
644 return E_POINTER;
646 if ((xmlNodePtr)tree->doc == tree)
648 xmlNodePtr root = xmlDocGetRootElement(tree->doc);
649 if (root && root->ns)
650 ns = root->ns->href;
652 else if (tree->ns)
654 ns = tree->ns->href;
657 entry = xmlHashLookup(This->cache, ns);
658 /* TODO: if the ns is not in the cache, and it's a URL,
659 * do we try to load from that? */
660 if (entry)
662 if (entry->type == SCHEMA_TYPE_XDR)
664 FIXME("partial stub: XDR schema support not implemented\n");
665 return S_OK;
667 else if (entry->type == SCHEMA_TYPE_XSD)
669 xmlSchemaValidCtxtPtr svctx;
670 int err;
671 /* TODO: if validateOnLoad property is false,
672 * we probably need to validate the schema here. */
673 svctx = xmlSchemaNewValidCtxt(entry->schema);
674 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
675 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
676 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
677 #endif
679 if ((xmlNodePtr)tree->doc == tree)
680 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
681 else
682 err = xmlSchemaValidateOneElement(svctx, tree);
684 xmlSchemaFreeValidCtxt(svctx);
685 return err? S_FALSE : S_OK;
689 return E_FAIL;
692 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
694 schema_cache* This = heap_alloc(sizeof(schema_cache));
695 if (!This)
696 return E_OUTOFMEMORY;
698 This->lpVtbl = &schema_cache_vtbl;
699 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
700 This->ref = 1;
702 *ppObj = &This->lpVtbl;
703 return S_OK;
706 #else
708 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
710 MESSAGE("This program tried to use a SchemaCache object, but\n"
711 "libxml2 support was not present at compile time.\n");
712 return E_NOTIMPL;
715 #endif