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