msxml3: implement IXMLDOMSchemaCollection/XMLSchemaCache
Adam Martinson
amartinson at codeweavers.com
Fri Oct 8 11:30:41 CDT 2010
Implemented over libxml's chained hash table.
Only XSD schemas will work at the moment;
libxml does not support XDR schemas.
---
dlls/msxml3/schema.c | 273
++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 262 insertions(+), 11 deletions(-)
diff --git a/dlls/msxml3/schema.c b/dlls/msxml3/schema.c
index 38d9376..dad4754 100644
--- a/dlls/msxml3/schema.c
+++ b/dlls/msxml3/schema.c
@@ -1,7 +1,10 @@
/*
* Schema cache implementation
+ * TODO: implement read-only
+ * TODO: XDR schema support
*
* Copyright 2007 Huw Davies
+ * Copyright 2010 Adam Martinson for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,23 +33,77 @@
#include "msxml6.h"
#include "wine/debug.h"
+#include "wine/list.h"
#include "msxml_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(msxml);
+/* We use a chained hashtable, which can hold any number of schemas
+ * This is just the number of buckets, should be prime */
+#define DEFAULT_HASHTABLE_SIZE 31
+
+#ifdef HAVE_LIBXML2
+
+#include <libxml/tree.h>
+#include <libxml/xmlschemas.h>
+#include <libxml/schemasInternals.h>
+#include <libxml/hash.h>
+
+
+typedef enum _SCHEMA_TYPE {
+ SCHEMA_TYPE_INVALID,
+ SCHEMA_TYPE_XDR,
+ SCHEMA_TYPE_XSD
+} SCHEMA_TYPE;
+
+static const xmlChar XSD_schema[] = "schema";
+static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
+static const xmlChar XDR_schema[] = "Schema";
+static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
typedef struct
{
const struct IXMLDOMSchemaCollectionVtbl *lpVtbl;
+ xmlHashTablePtr cache;
LONG ref;
} schema_t;
+typedef struct _cache_index_data
+{
+ LONG index;
+ BSTR* name;
+} cache_index_data;
+
+static inline IXMLDOMDocument3* get_doc_iface(xmlSchemaPtr schema)
+{
+ return schema->_private;
+}
+
static inline schema_t *impl_from_IXMLDOMSchemaCollection(
IXMLDOMSchemaCollection *iface )
{
return (schema_t *)((char*)iface - FIELD_OFFSET(schema_t, lpVtbl));
}
+static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(const xmlDocPtr
schema)
+{
+ if (schema && schema->children && schema->children->ns)
+ {
+
+ if (xmlStrEqual(schema->children->name, XDR_schema) &&
+ xmlStrEqual(schema->children->ns->href, XDR_nsURI))
+ {
+ return SCHEMA_TYPE_XDR;
+ }
+ else if (xmlStrEqual(schema->children->name, XSD_schema) &&
+ xmlStrEqual(schema->children->ns->href, XSD_nsURI))
+ {
+ return SCHEMA_TYPE_XSD;
+ }
+ }
+ return SCHEMA_TYPE_INVALID;
+}
+
static HRESULT WINAPI schema_cache_QueryInterface(
IXMLDOMSchemaCollection *iface, REFIID riid, void** ppvObject )
{
schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
@@ -78,6 +135,17 @@ static ULONG WINAPI schema_cache_AddRef(
IXMLDOMSchemaCollection *iface )
return ref;
}
+static void cache_free(void* data, xmlChar* name)
+{
+ xmlSchemaPtr schema = (xmlSchemaPtr) data;
+ if (IXMLDOMDocument3_Release(get_doc_iface(schema)) == 0)
+ {
+ schema->doc = NULL;
+ xmlSchemaFree((xmlSchemaPtr)data);
+ }
+ xmlFree(name);
+}
+
static ULONG WINAPI schema_cache_Release( IXMLDOMSchemaCollection *iface )
{
schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
@@ -86,6 +154,7 @@ static ULONG WINAPI schema_cache_Release(
IXMLDOMSchemaCollection *iface )
if ( ref == 0 )
{
+ xmlHashFree(This->cache,cache_free);
heap_free( This );
}
@@ -171,40 +240,209 @@ static HRESULT WINAPI schema_cache_Invoke(
IXMLDOMSchemaCollection *iface,
return hr;
}
+static inline xmlSchemaPtr schema_from_url(schema_t* This, char
const* url)
+{
+ xmlSchemaPtr schema = NULL;
+ xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
+ if (spctx)
+ {
+ schema = xmlSchemaParse(spctx);
+ xmlSchemaFreeParserCtxt(spctx);
+ }
+ else
+ {
+ FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
+ }
+ return schema;
+}
+
+static inline xmlSchemaPtr schema_from_doc(schema_t* This, xmlDocPtr doc)
+{
+ xmlSchemaPtr schema;
+ xmlSchemaParserCtxtPtr spctx;
+ xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
+ spctx = xmlSchemaNewDocParserCtxt(new_doc);
+ schema = xmlSchemaParse(spctx);
+ xmlSchemaFreeParserCtxt(spctx);
+ return schema;
+}
+
static HRESULT WINAPI schema_cache_add( IXMLDOMSchemaCollection
*iface, BSTR uri, VARIANT var )
{
- FIXME("(%p)->(%s, var(vt %x)): stub\n", iface, debugstr_w(uri),
V_VT(&var));
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ xmlChar* name = xmlChar_from_wchar(uri);
+ TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
+
+ switch (V_VT(&var))
+ {
+ case VT_NULL:
+ {
+ xmlHashRemoveEntry(This->cache, name, cache_free);
+ }
+ break;
+
+ case VT_BSTR:
+ {
+ xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
+ xmlSchemaPtr schema = schema_from_url(This, (char
const*)url);
+ heap_free(url);
+
+ if (!schema)
+ {
+ heap_free(name);
+ return E_FAIL;
+ }
+
+ schema->_private = create_domdoc((xmlNodePtr)schema->doc);
+ xmlHashRemoveEntry(This->cache, name, cache_free);
+ xmlHashAddEntry(This->cache, name, schema);
+ }
+ break;
+
+ case VT_DISPATCH:
+ {
+ xmlDocPtr doc;
+ xmlSchemaPtr schema;
+ SCHEMA_TYPE type;
+ doc =
xmlNodePtr_from_domnode((IXMLDOMNode*)V_DISPATCH(&var),
+ XML_DOCUMENT_NODE)->doc;
+ if (!doc)
+ {
+ heap_free(name);
+ return E_INVALIDARG;
+ }
+ type = schema_type_from_xmlDocPtr(doc);
+
+ if (type == SCHEMA_TYPE_INVALID)
+ {
+ WARN("invalid schema\n");
+ return E_FAIL;
+ }
+ else if (type == SCHEMA_TYPE_XDR)
+ {
+ FIXME("XDR schema support not implemented\n");
+ return S_OK;
+ }
+ schema = schema_from_doc(This, doc);
+ if (!schema)
+ {
+ heap_free(name);
+ return E_FAIL;
+ }
+ schema->_private = V_DISPATCH(&var);
+ xmlHashRemoveEntry(This->cache, name, cache_free);
+ xmlHashAddEntry(This->cache, name, schema);
+ }
+ break;
+
+ default:
+ {
+ heap_free(name);
+ return E_INVALIDARG;
+ }
+ }
+ heap_free(name);
return S_OK;
}
static HRESULT WINAPI schema_cache_get( IXMLDOMSchemaCollection
*iface, BSTR uri, IXMLDOMNode **node )
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ xmlChar* name;
+ xmlSchemaPtr schema;
+ TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
+
+ if (!node)
+ return E_INVALIDARG;
+
+ name = xmlChar_from_wchar(uri);
+ schema = (xmlSchemaPtr) xmlHashLookup(This->cache, name);
+ heap_free(name);
+
+ if (schema)
+ {
+ IXMLDOMDocument3_QueryInterface(get_doc_iface(schema),
&IID_IXMLDOMNode,
+ (void**)node);
+ return S_OK;
+ }
+
+ *node = NULL;
+ return S_FALSE;
}
static HRESULT WINAPI schema_cache_remove( IXMLDOMSchemaCollection
*iface, BSTR uri )
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ xmlChar* name = xmlChar_from_wchar(uri);
+ TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
+
+ if (xmlHashRemoveEntry(This->cache, name, cache_free) == 0)
+ {
+ heap_free(name);
+ return S_OK;
+ }
+
+ heap_free(name);
+ return E_FAIL;
}
static HRESULT WINAPI schema_cache_get_length(
IXMLDOMSchemaCollection *iface, LONG *length )
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ TRACE("(%p)->(%p)\n", This, length);
+
+ if (!length)
+ return E_INVALIDARG;
+ *length = xmlHashSize(This->cache);
+ return S_OK;
+}
+
+static void cache_index(void* data, void* index_data, xmlChar* name)
+{
+ cache_index_data* pindex_data = (cache_index_data*) index_data;
+
+ if (pindex_data->index-- == 0)
+ *pindex_data->name = bstr_from_xmlChar(name);
}
static HRESULT WINAPI schema_cache_get_namespaceURI(
IXMLDOMSchemaCollection *iface, LONG index, BSTR *len )
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ cache_index_data data = {index,len};
+ TRACE("(%p)->(%i, %p)\n", This, index, len);
+
+ if (index >= xmlHashSize(This->cache) || !len)
+ return E_INVALIDARG;
+
+ xmlHashScan(This->cache, cache_index, &data);
+ return S_OK;
+}
+
+static void cache_copy(void* data, void* dest, xmlChar* name)
+{
+ schema_t* This = (schema_t*) dest;
+ xmlSchemaPtr schema = (xmlSchemaPtr) data;
+
+ if (xmlHashLookup(This->cache, name) == NULL)
+ {
+ xmldoc_add_ref(schema->doc);
+ xmlHashAddEntry(This->cache, name, schema);
+ }
}
static HRESULT WINAPI schema_cache_addCollection(
IXMLDOMSchemaCollection *iface, IXMLDOMSchemaCollection *otherCollection )
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ schema_t *This = impl_from_IXMLDOMSchemaCollection( iface );
+ schema_t *That = impl_from_IXMLDOMSchemaCollection( otherCollection );
+ TRACE("(%p)->(%p)\n", This, That);
+
+ if (!That)
+ return E_POINTER;
+
+ /* TODO: detect errors while copying & return E_FAIL */
+ xmlHashScan(That->cache, cache_copy, This);
+
+ return S_OK;
}
static HRESULT WINAPI schema_cache_get__newEnum(
IXMLDOMSchemaCollection *iface, IUnknown **ppUnk )
@@ -213,6 +451,7 @@ static HRESULT WINAPI schema_cache_get__newEnum(
IXMLDOMSchemaCollection *iface,
return E_NOTIMPL;
}
+/* TODO: validate? validateOnLoad property? */
static const struct IXMLDOMSchemaCollectionVtbl schema_vtbl =
{
schema_cache_QueryInterface,
@@ -239,7 +478,19 @@ HRESULT SchemaCache_create(IUnknown *pUnkOuter,
LPVOID *ppObj)
schema->lpVtbl = &schema_vtbl;
schema->ref = 1;
+ schema->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
*ppObj = &schema->lpVtbl;
return S_OK;
}
+
+#else
+
+HRESULT SchemaCache_create(IUnknown *pUnkOuter, LPVOID *ppObj)
+{
+ MESSAGE("This program tried to use a SchemaCache object, but\n"
+ "libxml2 support was not present at compile time.\n");
+ return E_NOTIMPL;
+}
+
+#endif
--
1.7.2.3
More information about the wine-patches
mailing list