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
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
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";
66 typedef enum _SCHEMA_TYPE
{
72 typedef struct _schema_cache
74 const struct IXMLDOMSchemaCollection2Vtbl
* lpVtbl
;
75 xmlHashTablePtr cache
;
79 typedef struct _cache_entry
87 typedef struct _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
);
100 static LONG
cache_entry_release(cache_entry
* entry
)
102 LONG ref
= InterlockedDecrement(&entry
->ref
);
103 TRACE("%p new ref %d\n", entry
, ref
);
107 if (entry
->type
== SCHEMA_TYPE_XSD
)
109 xmldoc_release(entry
->doc
);
110 entry
->schema
->doc
= NULL
;
111 xmlSchemaFree(entry
->schema
);
114 else /* SCHEMA_TYPE_XDR */
116 xmldoc_release(entry
->doc
);
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
)
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
;
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
);
169 xmlSchemaFreeParserCtxt(spctx
);
173 FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url
));
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
;
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
);
198 FIXME("failed to parse doc\n");
203 xmlSchemaFreeParserCtxt(spctx
);
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
;
215 entry
->schema
= NULL
;
216 entry
->doc
= new_doc
;
217 xmldoc_init(entry
->doc
, &CLSID_DOMDocument30
);
218 xmldoc_add_ref(entry
->doc
);
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
) )
239 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
240 return E_NOINTERFACE
;
243 IXMLDOMSchemaCollection2_AddRef(iface
);
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
);
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
);
269 xmlHashFree(This
->cache
, cache_free
);
276 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
279 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
281 TRACE("(%p)->(%p)\n", This
, pctinfo
);
288 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
289 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
291 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
294 TRACE("(%p)->(%u %u %p)\n", This
, iTInfo
, lcid
, ppTInfo
);
296 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, ppTInfo
);
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
);
309 TRACE("(%p)->(%s %p %u %u %p)\n", This
, debugstr_guid(riid
), rgszNames
, cNames
,
312 if(!rgszNames
|| cNames
== 0 || !rgDispId
)
315 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, &typeinfo
);
318 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, rgszNames
, cNames
, rgDispId
);
319 ITypeInfo_Release(typeinfo
);
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
,
331 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
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
);
341 hr
= ITypeInfo_Invoke(typeinfo
, &(This
->lpVtbl
), dispIdMember
, wFlags
, pDispParams
,
342 pVarResult
, pExcepInfo
, puArgErr
);
343 ITypeInfo_Release(typeinfo
);
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
));
359 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
365 xmlChar
* url
= xmlChar_from_wchar(V_BSTR(&var
));
366 cache_entry
* entry
= cache_entry_from_url((char const*)url
);
371 cache_entry_add_ref(entry
);
379 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
380 xmlHashAddEntry(This
->cache
, name
, entry
);
386 xmlDocPtr doc
= NULL
;
389 IXMLDOMNode
* domnode
= NULL
;
390 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
393 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
397 IXMLDOMNode_Release(domnode
);
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
);
413 WARN("invalid schema!\n");
417 IXMLDOMNode_Release(domnode
);
421 cache_entry_add_ref(entry
);
429 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
430 xmlHashAddEntry(This
->cache
, name
, entry
);
444 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
447 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
450 TRACE("(%p)->(%s, %p)\n", This
, wine_dbgstr_w(uri
), node
);
455 name
= xmlChar_from_wchar(uri
);
456 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
459 /* TODO: this should be read-only */
461 return DOMDocument_create_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
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
);
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
);
485 *length
= xmlHashSize(This
->cache
);
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
);
508 if (index
>= xmlHashSize(This
->cache
))
511 xmlHashScan(This
->cache
, cache_index
, &data
);
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
)
537 /* TODO: detect errors while copying & return E_FAIL */
538 xmlHashScan(That
->cache
, cache_copy
, This
);
543 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
552 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
558 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
559 VARIANT_BOOL validateOnLoad
)
565 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
566 VARIANT_BOOL
* validateOnLoad
)
572 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
573 BSTR namespaceURI
, ISchema
** schema
)
581 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
582 IXMLDOMNode
* node
, ISchemaItem
** item
)
590 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl
=
592 schema_cache_QueryInterface
,
594 schema_cache_Release
,
595 schema_cache_GetTypeInfoCount
,
596 schema_cache_GetTypeInfo
,
597 schema_cache_GetIDsOfNames
,
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
, ...)
617 LIBXML2_CALLBACK_ERR(SchemaCache_validate_tree
, msg
, ap
);
621 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
625 LIBXML2_CALLBACK_WARN(SchemaCache_validate_tree
, msg
, ap
);
629 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
630 static void validate_serror(void* ctx
, xmlErrorPtr err
)
632 LIBXML2_CALLBACK_SERROR(SchemaCache_validate_tree
, err
);
636 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
638 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
640 xmlChar
const* ns
= NULL
;
641 TRACE("(%p, %p)\n", This
, tree
);
646 if ((xmlNodePtr
)tree
->doc
== tree
)
648 xmlNodePtr root
= xmlDocGetRootElement(tree
->doc
);
649 if (root
&& root
->ns
)
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? */
662 if (entry
->type
== SCHEMA_TYPE_XDR
)
664 FIXME("partial stub: XDR schema support not implemented\n");
667 else if (entry
->type
== SCHEMA_TYPE_XSD
)
669 xmlSchemaValidCtxtPtr svctx
;
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
);
679 if ((xmlNodePtr
)tree
->doc
== tree
)
680 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
682 err
= xmlSchemaValidateOneElement(svctx
, tree
);
684 xmlSchemaFreeValidCtxt(svctx
);
685 return err
? S_FALSE
: S_OK
;
692 HRESULT
SchemaCache_create(IUnknown
* pUnkOuter
, void** ppObj
)
694 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
696 return E_OUTOFMEMORY
;
698 This
->lpVtbl
= &schema_cache_vtbl
;
699 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
702 *ppObj
= &This
->lpVtbl
;
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");