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
34 #include "wine/debug.h"
36 #include "msxml_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
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
46 /* This is just the number of buckets, should be prime */
47 #define DEFAULT_HASHTABLE_SIZE 31
51 #include <libxml/tree.h>
52 #include <libxml/xmlschemas.h>
53 #include <libxml/schemasInternals.h>
54 #include <libxml/hash.h>
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";
67 typedef enum _SCHEMA_TYPE {
73 typedef struct _schema_cache
75 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
76 xmlHashTablePtr cache;
80 typedef struct _cache_entry
88 typedef struct _cache_index_data
94 static LONG cache_entry_add_ref(cache_entry* entry)
96 LONG ref = InterlockedIncrement(&entry->ref);
97 TRACE("%p new ref %d\n", entry, ref);
101 static LONG cache_entry_release(cache_entry* entry)
103 LONG ref = InterlockedDecrement(&entry->ref);
104 TRACE("%p new ref %d\n", entry, ref);
108 if (entry->type == SCHEMA_TYPE_XSD)
110 xmldoc_release(entry->doc);
111 entry->schema->doc = NULL;
112 xmlSchemaFree(entry->schema);
115 else /* SCHEMA_TYPE_XDR */
117 xmldoc_release(entry->doc);
124 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
126 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
129 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
133 root = xmlDocGetRootElement(schema);
134 if (root && root->ns)
137 if (xmlStrEqual(root->name, XDR_schema) &&
138 xmlStrEqual(root->ns->href, XDR_nsURI))
140 return SCHEMA_TYPE_XDR;
142 else if (xmlStrEqual(root->name, XSD_schema) &&
143 xmlStrEqual(root->ns->href, XSD_nsURI))
145 return SCHEMA_TYPE_XSD;
148 return SCHEMA_TYPE_INVALID;
151 static cache_entry* cache_entry_from_url(char const* url)
153 cache_entry* entry = heap_alloc(sizeof(cache_entry));
154 xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
155 entry->type = SCHEMA_TYPE_XSD;
159 if((entry->schema = xmlSchemaParse(spctx)))
161 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
162 entry->doc = entry->schema->doc;
163 xmldoc_add_ref(entry->doc);
170 xmlSchemaFreeParserCtxt(spctx);
174 FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
181 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc)
183 cache_entry* entry = heap_alloc(sizeof(cache_entry));
184 xmlSchemaParserCtxtPtr spctx;
185 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
187 entry->type = SCHEMA_TYPE_XSD;
189 spctx = xmlSchemaNewDocParserCtxt(new_doc);
191 if ((entry->schema = xmlSchemaParse(spctx)))
193 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
194 entry->doc = entry->schema->doc;
195 xmldoc_add_ref(entry->doc);
199 FIXME("failed to parse doc\n");
204 xmlSchemaFreeParserCtxt(spctx);
208 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc)
210 cache_entry* entry = heap_alloc(sizeof(cache_entry));
211 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
213 FIXME("XDR schema support not implemented\n");
214 entry->type = SCHEMA_TYPE_XDR;
216 entry->schema = NULL;
217 entry->doc = new_doc;
218 xmldoc_init(entry->doc, &CLSID_DOMDocument30);
219 xmldoc_add_ref(entry->doc);
224 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
225 REFIID riid, void** ppvObject)
227 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
229 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
231 if ( IsEqualIID(riid, &IID_IUnknown) ||
232 IsEqualIID(riid, &IID_IDispatch) ||
233 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
234 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
240 FIXME("interface %s not implemented\n", debugstr_guid(riid));
241 return E_NOINTERFACE;
244 IXMLDOMSchemaCollection2_AddRef(iface);
249 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
251 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
252 LONG ref = InterlockedIncrement(&This->ref);
253 TRACE("%p new ref %d\n", This, ref);
257 static void cache_free(void* data, xmlChar* name /* ignored */)
259 cache_entry_release((cache_entry*)data);
262 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
264 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
265 LONG ref = InterlockedDecrement(&This->ref);
266 TRACE("%p new ref %d\n", This, ref);
270 xmlHashFree(This->cache, cache_free);
277 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
280 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
282 TRACE("(%p)->(%p)\n", This, pctinfo);
289 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
290 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
292 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
295 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
297 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
302 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
303 REFIID riid, LPOLESTR* rgszNames,
304 UINT cNames, LCID lcid, DISPID* rgDispId)
306 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
310 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
313 if(!rgszNames || cNames == 0 || !rgDispId)
316 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
319 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
320 ITypeInfo_Release(typeinfo);
326 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
327 DISPID dispIdMember, REFIID riid, LCID lcid,
328 WORD wFlags, DISPPARAMS* pDispParams,
329 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
332 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
336 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
337 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
339 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
342 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
343 pVarResult, pExcepInfo, puArgErr);
344 ITypeInfo_Release(typeinfo);
350 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
352 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
353 xmlChar* name = xmlChar_from_wchar(uri);
354 TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
360 xmlHashRemoveEntry(This->cache, name, cache_free);
366 xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
367 cache_entry* entry = cache_entry_from_url((char const*)url);
372 cache_entry_add_ref(entry);
380 xmlHashRemoveEntry(This->cache, name, cache_free);
381 xmlHashAddEntry(This->cache, name, entry);
387 xmlDocPtr doc = NULL;
390 IXMLDOMNode* domnode = NULL;
391 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
394 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
398 IXMLDOMNode_Release(domnode);
402 type = schema_type_from_xmlDocPtr(doc);
404 if (type == SCHEMA_TYPE_XSD)
406 entry = cache_entry_from_xsd_doc(doc);
408 else if (type == SCHEMA_TYPE_XDR)
410 entry = cache_entry_from_xdr_doc(doc);
414 WARN("invalid schema!\n");
418 IXMLDOMNode_Release(domnode);
422 cache_entry_add_ref(entry);
430 xmlHashRemoveEntry(This->cache, name, cache_free);
431 xmlHashAddEntry(This->cache, name, entry);
445 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
448 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
451 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
456 name = xmlChar_from_wchar(uri);
457 entry = (cache_entry*) xmlHashLookup(This->cache, name);
460 /* TODO: this should be read-only */
462 return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
468 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
470 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
471 xmlChar* name = xmlChar_from_wchar(uri);
472 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
474 xmlHashRemoveEntry(This->cache, name, cache_free);
479 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
481 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
482 TRACE("(%p)->(%p)\n", This, length);
486 *length = xmlHashSize(This->cache);
490 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
492 cache_index_data* index_data = (cache_index_data*)index;
494 if (index_data->index-- == 0)
495 *index_data->out = bstr_from_xmlChar(name);
498 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
499 LONG index, BSTR* len)
501 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
502 cache_index_data data = {index,len};
503 TRACE("(%p)->(%i, %p)\n", This, index, len);
509 if (index >= xmlHashSize(This->cache))
512 xmlHashScan(This->cache, cache_index, &data);
516 static void cache_copy(void* data, void* dest, xmlChar* name)
518 schema_cache* This = (schema_cache*) dest;
519 cache_entry* entry = (cache_entry*) data;
521 if (xmlHashLookup(This->cache, name) == NULL)
523 cache_entry_add_ref(entry);
524 xmlHashAddEntry(This->cache, name, entry);
528 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
529 IXMLDOMSchemaCollection* otherCollection)
531 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
532 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
533 TRACE("(%p)->(%p)\n", This, That);
535 if (!otherCollection)
538 /* TODO: detect errors while copying & return E_FAIL */
539 xmlHashScan(That->cache, cache_copy, This);
544 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
553 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
559 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
560 VARIANT_BOOL validateOnLoad)
566 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
567 VARIANT_BOOL* validateOnLoad)
573 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
574 BSTR namespaceURI, ISchema** schema)
582 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
583 IXMLDOMNode* node, ISchemaItem** item)
591 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
593 schema_cache_QueryInterface,
595 schema_cache_Release,
596 schema_cache_GetTypeInfoCount,
597 schema_cache_GetTypeInfo,
598 schema_cache_GetIDsOfNames,
603 schema_cache_get_length,
604 schema_cache_get_namespaceURI,
605 schema_cache_addCollection,
606 schema_cache_get__newEnum,
607 schema_cache_validate,
608 schema_cache_put_validateOnLoad,
609 schema_cache_get_validateOnLoad,
610 schema_cache_getSchema,
611 schema_cache_getDeclaration
614 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
616 schema_cache* This = heap_alloc(sizeof(schema_cache));
618 return E_OUTOFMEMORY;
620 This->lpVtbl = &schema_cache_vtbl;
621 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
624 *ppObj = &This->lpVtbl;
630 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
632 MESSAGE("This program tried to use a SchemaCache object, but\n"
633 "libxml2 support was not present at compile time.\n");