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 IXMLDOMSchemaCollectionVtbl *lpVtbl;
76 xmlHashTablePtr cache;
80 typedef struct _cache_entry
88 static LONG cache_entry_add_ref(cache_entry* entry)
90 LONG ref = InterlockedIncrement(&entry->ref);
91 TRACE("%p new ref %d\n", entry, ref);
95 static LONG cache_entry_release(cache_entry* entry)
97 LONG ref = InterlockedDecrement(&entry->ref);
98 TRACE("%p new ref %d\n", entry, ref);
102 if (entry->type == SCHEMA_TYPE_XSD)
104 xmldoc_release(entry->doc);
105 entry->schema->doc = NULL;
106 xmlSchemaFree(entry->schema);
109 else /* SCHEMA_TYPE_XDR */
111 xmldoc_release(entry->doc);
118 static inline schema_cache *impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
120 return (schema_cache *)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
123 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
127 root = xmlDocGetRootElement(schema);
128 if (root && root->ns)
131 if (xmlStrEqual(root->name, XDR_schema) &&
132 xmlStrEqual(root->ns->href, XDR_nsURI))
134 return SCHEMA_TYPE_XDR;
136 else if (xmlStrEqual(root->name, XSD_schema) &&
137 xmlStrEqual(root->ns->href, XSD_nsURI))
139 return SCHEMA_TYPE_XSD;
142 return SCHEMA_TYPE_INVALID;
145 static cache_entry* cache_entry_from_url(char const* url)
147 cache_entry* entry = heap_alloc(sizeof(cache_entry));
148 xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
149 entry->type = SCHEMA_TYPE_XSD;
153 if((entry->schema = xmlSchemaParse(spctx)))
155 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
156 entry->doc = entry->schema->doc;
157 xmldoc_add_ref(entry->doc);
164 xmlSchemaFreeParserCtxt(spctx);
168 FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
175 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc)
177 cache_entry* entry = heap_alloc(sizeof(cache_entry));
178 xmlSchemaParserCtxtPtr spctx;
179 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
181 entry->type = SCHEMA_TYPE_XSD;
183 spctx = xmlSchemaNewDocParserCtxt(new_doc);
185 if ((entry->schema = xmlSchemaParse(spctx)))
187 xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
188 entry->doc = entry->schema->doc;
189 xmldoc_add_ref(entry->doc);
193 FIXME("failed to parse doc\n");
198 xmlSchemaFreeParserCtxt(spctx);
202 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc)
204 cache_entry* entry = heap_alloc(sizeof(cache_entry));
205 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
207 FIXME("XDR schema support not implemented\n");
208 entry->type = SCHEMA_TYPE_XDR;
210 entry->schema = NULL;
211 entry->doc = new_doc;
212 xmldoc_init(entry->doc, &CLSID_DOMDocument30);
213 xmldoc_add_ref(entry->doc);
218 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection *iface, REFIID riid, void** ppvObject)
220 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
222 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
224 if ( IsEqualIID(riid, &IID_IUnknown) ||
225 IsEqualIID(riid, &IID_IDispatch) ||
226 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) )
232 FIXME("interface %s not implemented\n", debugstr_guid(riid));
233 return E_NOINTERFACE;
236 IXMLDOMSchemaCollection_AddRef(iface);
241 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection *iface)
243 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
244 LONG ref = InterlockedIncrement(&This->ref);
245 TRACE("%p new ref %d\n", This, ref);
249 static void cache_free(void* data, xmlChar* name /* ignored */)
251 cache_entry_release((cache_entry*)data);
254 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection *iface)
256 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
257 LONG ref = InterlockedDecrement(&This->ref);
258 TRACE("%p new ref %d\n", This, ref);
262 xmlHashFree(This->cache, cache_free);
269 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection *iface, UINT* pctinfo)
271 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
273 TRACE("(%p)->(%p)\n", This, pctinfo);
280 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection *iface,
281 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
283 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
286 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
288 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
293 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection *iface,
300 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
304 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
307 if(!rgszNames || cNames == 0 || !rgDispId)
310 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
313 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
314 ITypeInfo_Release(typeinfo);
320 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection *iface,
325 DISPPARAMS* pDispParams,
327 EXCEPINFO* pExcepInfo,
330 schema_cache *This = impl_from_IXMLDOMSchemaCollection(iface);
334 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
335 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
337 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
340 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
341 pVarResult, pExcepInfo, puArgErr);
342 ITypeInfo_Release(typeinfo);
348 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection *iface, BSTR uri, VARIANT var)
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));
358 xmlHashRemoveEntry(This->cache, name, cache_free);
364 xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
365 cache_entry* entry = cache_entry_from_url((char const*)url);
370 cache_entry_add_ref(entry);
378 xmlHashRemoveEntry(This->cache, name, cache_free);
379 xmlHashAddEntry(This->cache, name, entry);
385 xmlDocPtr doc = NULL;
388 IXMLDOMNode* domnode = NULL;
389 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
392 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
396 IXMLDOMNode_Release(domnode);
400 type = schema_type_from_xmlDocPtr(doc);
402 if (type == SCHEMA_TYPE_XSD)
404 entry = cache_entry_from_xsd_doc(doc);
406 else if (type == SCHEMA_TYPE_XDR)
408 entry = cache_entry_from_xdr_doc(doc);
412 WARN("invalid schema!\n");
416 IXMLDOMNode_Release(domnode);
420 cache_entry_add_ref(entry);
428 xmlHashRemoveEntry(This->cache, name, cache_free);
429 xmlHashAddEntry(This->cache, name, entry);
443 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection *iface, BSTR uri, IXMLDOMNode **node)
449 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection *iface, BSTR uri)
455 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection *iface, LONG *length)
461 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection *iface, LONG index, BSTR *len)
467 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection *iface, IXMLDOMSchemaCollection *otherCollection)
473 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection *iface, IUnknown **ppUnk)
479 /* TODO: validate? validateOnLoad property? */
480 static const struct IXMLDOMSchemaCollectionVtbl schema_vtbl =
482 schema_cache_QueryInterface,
484 schema_cache_Release,
485 schema_cache_GetTypeInfoCount,
486 schema_cache_GetTypeInfo,
487 schema_cache_GetIDsOfNames,
492 schema_cache_get_length,
493 schema_cache_get_namespaceURI,
494 schema_cache_addCollection,
495 schema_cache_get__newEnum
498 HRESULT SchemaCache_create(IUnknown *pUnkOuter, LPVOID *ppObj)
500 schema_cache *schema = heap_alloc( sizeof (*schema) );
502 return E_OUTOFMEMORY;
504 schema->lpVtbl = &schema_vtbl;
505 schema->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
508 *ppObj = &schema->lpVtbl;
514 HRESULT SchemaCache_create(IUnknown *pUnkOuter, LPVOID *ppObj)
516 MESSAGE("This program tried to use a SchemaCache object, but\n"
517 "libxml2 support was not present at compile time.\n");