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: 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 17
50 #include <libxml/tree.h>
51 #include <libxml/xmlschemas.h>
52 #include <libxml/schemasInternals.h>
53 #include <libxml/hash.h>
54 #include <libxml/parser.h>
55 #include <libxml/parserInternals.h>
56 #include <libxml/xmlIO.h>
58 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
60 static const xmlChar XSD_schema[] = "schema";
61 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
62 static const xmlChar XDR_schema[] = "Schema";
63 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
64 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
66 static xmlChar const* datatypes_src = NULL;
67 static int datatypes_len = 0;
68 static HGLOBAL datatypes_handle = NULL;
69 static HRSRC datatypes_rsrc = NULL;
70 static xmlSchemaPtr datatypes_schema = NULL;
78 typedef enum _SCHEMA_TYPE {
84 typedef struct _schema_cache
86 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
87 MSXML_VERSION version;
88 xmlHashTablePtr cache;
92 typedef struct _cache_entry
100 typedef struct _cache_index_data
106 /* datatypes lookup stuff
107 * generated with help from gperf */
108 #define DT_MIN_STR_LEN 2
109 #define DT_MAX_STR_LEN 11
110 #define DT_MIN_HASH_VALUE 2
111 #define DT_MAX_HASH_VALUE 115
113 static const xmlChar DT_bin_base64[] = "bin.base64";
114 static const xmlChar DT_bin_hex[] = "bin.hex";
115 static const xmlChar DT_boolean[] = "boolean";
116 static const xmlChar DT_char[] = "char";
117 static const xmlChar DT_date[] = "date";
118 static const xmlChar DT_date_tz[] = "date.tz";
119 static const xmlChar DT_dateTime[] = "dateTime";
120 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
121 static const xmlChar DT_entity[] = "entity";
122 static const xmlChar DT_entities[] = "entities";
123 static const xmlChar DT_enumeration[] = "enumeration";
124 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
125 static const xmlChar DT_float[] = "float";
126 static const xmlChar DT_i1[] = "i1";
127 static const xmlChar DT_i2[] = "i2";
128 static const xmlChar DT_i4[] = "i4";
129 static const xmlChar DT_i8[] = "i8";
130 static const xmlChar DT_id[] = "id";
131 static const xmlChar DT_idref[] = "idref";
132 static const xmlChar DT_idrefs[] = "idrefs";
133 static const xmlChar DT_int[] = "int";
134 static const xmlChar DT_nmtoken[] = "nmtoken";
135 static const xmlChar DT_nmtokens[] = "nmtokens";
136 static const xmlChar DT_notation[] = "notation";
137 static const xmlChar DT_number[] = "number";
138 static const xmlChar DT_r4[] = "r4";
139 static const xmlChar DT_r8[] = "r8";
140 static const xmlChar DT_string[] = "string";
141 static const xmlChar DT_time[] = "time";
142 static const xmlChar DT_time_tz[] = "time.tz";
143 static const xmlChar DT_ui1[] = "ui1";
144 static const xmlChar DT_ui2[] = "ui2";
145 static const xmlChar DT_ui4[] = "ui4";
146 static const xmlChar DT_ui8[] = "ui8";
147 static const xmlChar DT_uri[] = "uri";
148 static const xmlChar DT_uuid[] = "uuid";
150 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
151 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
152 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
153 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
154 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
155 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
156 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
157 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
158 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
159 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
160 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
161 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
162 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
163 static const OLECHAR wDT_i1[] = {'i','1',0};
164 static const OLECHAR wDT_i2[] = {'i','2',0};
165 static const OLECHAR wDT_i4[] = {'i','4',0};
166 static const OLECHAR wDT_i8[] = {'i','8',0};
167 static const OLECHAR wDT_id[] = {'i','d',0};
168 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
169 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
170 static const OLECHAR wDT_int[] = {'i','n','t',0};
171 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
172 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
173 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
174 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
175 static const OLECHAR wDT_r4[] = {'r','4',0};
176 static const OLECHAR wDT_r8[] = {'r','8',0};
177 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
178 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
179 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
180 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
181 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
182 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
183 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
184 static const OLECHAR wDT_uri[] = {'u','r','i',0};
185 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
187 static const BYTE hash_assoc_values[] =
189 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
190 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
191 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
192 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
193 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
194 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
195 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
196 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
197 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
198 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
199 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
200 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
201 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
202 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
203 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
206 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
207 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
208 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
209 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
213 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
214 116, 116, 116, 116, 116, 116
217 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
221 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
225 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
229 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
233 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
234 static void parser_serror(void* ctx, xmlErrorPtr err)
236 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
240 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
242 TRACE("(%p)\n", spctx);
244 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
245 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
246 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
249 return xmlSchemaParse(spctx);
252 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
256 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
260 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
264 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
268 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
269 static void validate_serror(void* ctx, xmlErrorPtr err)
271 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
275 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
277 xmlSchemaValidCtxtPtr svctx;
280 TRACE("(%p, %p)\n", schema, tree);
281 /* TODO: if validateOnLoad property is false,
282 * we probably need to validate the schema here. */
283 svctx = xmlSchemaNewValidCtxt(schema);
284 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
285 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
286 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
289 if (tree->type == XML_DOCUMENT_NODE)
290 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
292 err = xmlSchemaValidateOneElement(svctx, tree);
294 xmlSchemaFreeValidCtxt(svctx);
295 return err? S_FALSE : S_OK;
298 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
300 DWORD hval = (len == -1)? xmlStrlen(str) : len;
305 hval += hash_assoc_values[str[10]];
308 hval += hash_assoc_values[str[9]];
311 hval += hash_assoc_values[str[8]];
314 hval += hash_assoc_values[str[7]];
317 hval += hash_assoc_values[str[6]];
320 hval += hash_assoc_values[str[5]];
323 hval += hash_assoc_values[str[4]];
326 hval += hash_assoc_values[str[3]];
329 hval += hash_assoc_values[str[2]];
332 hval += hash_assoc_values[str[1]];
335 hval += hash_assoc_values[str[0]];
341 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
343 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
348 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
351 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
354 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
357 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
360 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
363 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
366 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
369 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
372 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
375 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
378 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
384 static const xmlChar const* DT_string_table[DT__N_TYPES] =
424 static const WCHAR const* DT_wstring_table[DT__N_TYPES] =
464 static const XDR_DT DT_lookup_table[] =
517 -1, -1, -1, -1, -1, -1, -1, -1, -1,
518 -1, -1, -1, -1, -1, -1, -1, -1, -1,
519 -1, -1, -1, -1, -1, -1, -1, -1, -1,
520 -1, -1, -1, -1, -1, -1, -1, -1, -1,
521 -1, -1, -1, -1, -1, -1, -1, -1, -1,
522 -1, -1, -1, -1, -1, -1, -1, -1,
526 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
528 DWORD hash = dt_hash(str, len);
529 XDR_DT dt = DT_INVALID;
531 if (hash <= DT_MAX_HASH_VALUE)
532 dt = DT_lookup_table[hash];
534 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
540 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
542 DWORD hash = dt_hash_bstr(bstr, len);
543 XDR_DT dt = DT_INVALID;
545 if (hash <= DT_MAX_HASH_VALUE)
546 dt = DT_lookup_table[hash];
548 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
554 xmlChar const* dt_to_str(XDR_DT dt)
556 if (dt == DT_INVALID)
559 return DT_string_table[dt];
562 OLECHAR const* dt_to_bstr(XDR_DT dt)
564 if (dt == DT_INVALID)
567 return DT_wstring_table[dt];
570 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
577 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
579 if (!datatypes_schema)
581 xmlSchemaParserCtxtPtr spctx;
582 assert(datatypes_src != NULL);
583 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
584 datatypes_schema = Schema_parse(spctx);
585 xmlSchemaFreeParserCtxt(spctx);
621 assert(datatypes_schema != NULL);
622 if (content && xmlStrlen(content))
624 tmp_doc = xmlNewDoc(NULL);
625 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
626 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
628 xmlDocSetRootElement(tmp_doc, node);
630 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
634 { /* probably the node is being created manually and has no content yet */
639 FIXME("need to handle dt:%s\n", dt_to_str(dt));
644 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
646 return (node->ns != NULL)? node->ns->href : NULL;
649 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
651 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
652 xmlHashLookup(This->cache, nsURI);
655 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
657 cache_entry* entry = get_entry(This, get_node_nsURI(node));
658 return (!entry)? NULL : entry->schema;
661 xmlExternalEntityLoader _external_entity_loader = NULL;
663 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
664 xmlParserCtxtPtr ctxt)
666 xmlParserInputPtr input;
668 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
670 assert(MSXML_hInstance != NULL);
671 assert(datatypes_rsrc != NULL);
672 assert(datatypes_handle != NULL);
673 assert(datatypes_src != NULL);
675 /* TODO: if the desired schema is in the cache, load it from there */
676 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
678 TRACE("loading built-in schema for %s\n", URL);
679 input = xmlNewStringInputStream(ctxt, datatypes_src);
683 input = _external_entity_loader(URL, ID, ctxt);
689 void schemasInit(void)
693 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
695 FIXME("failed to find resource for %s\n", DT_nsURI);
699 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
701 FIXME("failed to load resource for %s\n", DT_nsURI);
704 buf = LockResource(datatypes_handle);
705 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
707 /* Resource is loaded as raw data,
708 * need a null-terminated string */
709 while (buf[len] != '>')
711 datatypes_src = BAD_CAST buf;
712 datatypes_len = len + 1;
714 if ((void*)xmlGetExternalEntityLoader() != (void*)external_entity_loader)
716 _external_entity_loader = xmlGetExternalEntityLoader();
717 xmlSetExternalEntityLoader(external_entity_loader);
721 void schemasCleanup(void)
723 if (datatypes_handle)
724 FreeResource(datatypes_handle);
725 if (datatypes_schema)
726 xmlSchemaFree(datatypes_schema);
727 xmlSetExternalEntityLoader(_external_entity_loader);
730 static LONG cache_entry_add_ref(cache_entry* entry)
732 LONG ref = InterlockedIncrement(&entry->ref);
733 TRACE("%p new ref %d\n", entry, ref);
737 static LONG cache_entry_release(cache_entry* entry)
739 LONG ref = InterlockedDecrement(&entry->ref);
740 TRACE("%p new ref %d\n", entry, ref);
744 if (entry->type == SCHEMA_TYPE_XSD)
746 xmldoc_release(entry->doc);
747 entry->schema->doc = NULL;
748 xmlSchemaFree(entry->schema);
751 else /* SCHEMA_TYPE_XDR */
753 xmldoc_release(entry->doc);
754 xmldoc_release(entry->schema->doc);
755 entry->schema->doc = NULL;
756 xmlSchemaFree(entry->schema);
763 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
765 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
768 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
770 xmlNodePtr root = NULL;
772 root = xmlDocGetRootElement(schema);
773 if (root && root->ns)
776 if (xmlStrEqual(root->name, XDR_schema) &&
777 xmlStrEqual(root->ns->href, XDR_nsURI))
779 return SCHEMA_TYPE_XDR;
781 else if (xmlStrEqual(root->name, XSD_schema) &&
782 xmlStrEqual(root->ns->href, XSD_nsURI))
784 return SCHEMA_TYPE_XSD;
787 return SCHEMA_TYPE_INVALID;
790 static BOOL link_datatypes(xmlDocPtr schema)
792 xmlNodePtr root, next, child;
795 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
796 root = xmlDocGetRootElement(schema);
800 for (ns = root->nsDef; ns != NULL; ns = ns->next)
802 if (xmlStrEqual(ns->href, DT_nsURI))
809 next = xmlFirstElementChild(root);
810 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
811 if (next) child = xmlAddPrevSibling(next, child);
812 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
813 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
818 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
820 cache_entry* entry = heap_alloc(sizeof(cache_entry));
821 xmlSchemaParserCtxtPtr spctx;
822 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
824 link_datatypes(new_doc);
826 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
827 * do we need to do something special here? */
828 entry->type = SCHEMA_TYPE_XSD;
830 spctx = xmlSchemaNewDocParserCtxt(new_doc);
832 if ((entry->schema = Schema_parse(spctx)))
834 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
835 entry->doc = entry->schema->doc;
836 xmldoc_add_ref(entry->doc);
840 FIXME("failed to parse doc\n");
845 xmlSchemaFreeParserCtxt(spctx);
849 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
851 cache_entry* entry = heap_alloc(sizeof(cache_entry));
852 xmlSchemaParserCtxtPtr spctx;
853 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
855 link_datatypes(xsd_doc);
857 entry->type = SCHEMA_TYPE_XDR;
859 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
861 if ((entry->schema = Schema_parse(spctx)))
863 entry->doc = new_doc;
864 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
865 xmldoc_init(entry->doc, DOMDocument_version(v));
866 xmldoc_add_ref(entry->doc);
867 xmldoc_add_ref(entry->schema->doc);
871 FIXME("failed to parse doc\n");
877 xmlSchemaFreeParserCtxt(spctx);
882 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
885 IXMLDOMDocument3* domdoc = NULL;
886 xmlDocPtr doc = NULL;
887 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
888 VARIANT_BOOL b = VARIANT_FALSE;
889 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
893 FIXME("failed to create domdoc\n");
896 assert(domdoc != NULL);
897 assert(V_VT(&url) == VT_BSTR);
899 hr = IXMLDOMDocument3_load(domdoc, url, &b);
902 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
903 if (b != VARIANT_TRUE)
905 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
906 IXMLDOMDocument3_Release(domdoc);
910 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
911 type = schema_type_from_xmlDocPtr(doc);
915 case SCHEMA_TYPE_XSD:
916 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
918 case SCHEMA_TYPE_XDR:
919 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
921 case SCHEMA_TYPE_INVALID:
923 FIXME("invalid schema\n");
926 IXMLDOMDocument3_Release(domdoc);
931 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
932 REFIID riid, void** ppvObject)
934 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
936 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
938 if ( IsEqualIID(riid, &IID_IUnknown) ||
939 IsEqualIID(riid, &IID_IDispatch) ||
940 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
941 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
947 FIXME("interface %s not implemented\n", debugstr_guid(riid));
948 return E_NOINTERFACE;
951 IXMLDOMSchemaCollection2_AddRef(iface);
956 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
958 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
959 LONG ref = InterlockedIncrement(&This->ref);
960 TRACE("%p new ref %d\n", This, ref);
964 static void cache_free(void* data, xmlChar* name /* ignored */)
966 cache_entry_release((cache_entry*)data);
969 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
971 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
972 LONG ref = InterlockedDecrement(&This->ref);
973 TRACE("%p new ref %d\n", This, ref);
977 xmlHashFree(This->cache, cache_free);
984 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
987 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
989 TRACE("(%p)->(%p)\n", This, pctinfo);
996 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
997 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
999 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1002 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1004 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1009 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1010 REFIID riid, LPOLESTR* rgszNames,
1011 UINT cNames, LCID lcid, DISPID* rgDispId)
1013 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1014 ITypeInfo* typeinfo;
1017 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1020 if(!rgszNames || cNames == 0 || !rgDispId)
1021 return E_INVALIDARG;
1023 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1026 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1027 ITypeInfo_Release(typeinfo);
1033 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1034 DISPID dispIdMember, REFIID riid, LCID lcid,
1035 WORD wFlags, DISPPARAMS* pDispParams,
1036 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1039 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1040 ITypeInfo* typeinfo;
1043 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1044 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1046 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1049 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1050 pVarResult, pExcepInfo, puArgErr);
1051 ITypeInfo_Release(typeinfo);
1057 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1059 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1060 xmlChar* name = xmlChar_from_wchar(uri);
1061 TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
1067 xmlHashRemoveEntry(This->cache, name, cache_free);
1073 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1077 cache_entry_add_ref(entry);
1085 xmlHashRemoveEntry(This->cache, name, cache_free);
1086 xmlHashAddEntry(This->cache, name, entry);
1092 xmlDocPtr doc = NULL;
1095 IXMLDOMNode* domnode = NULL;
1096 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1099 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1103 IXMLDOMNode_Release(domnode);
1105 return E_INVALIDARG;
1107 type = schema_type_from_xmlDocPtr(doc);
1109 if (type == SCHEMA_TYPE_XSD)
1111 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1113 else if (type == SCHEMA_TYPE_XDR)
1115 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1119 WARN("invalid schema!\n");
1123 IXMLDOMNode_Release(domnode);
1127 cache_entry_add_ref(entry);
1135 xmlHashRemoveEntry(This->cache, name, cache_free);
1136 xmlHashAddEntry(This->cache, name, entry);
1143 return E_INVALIDARG;
1150 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1153 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1156 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1161 name = xmlChar_from_wchar(uri);
1162 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1165 /* TODO: this should be read-only */
1167 return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1173 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1175 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1176 xmlChar* name = xmlChar_from_wchar(uri);
1177 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1179 xmlHashRemoveEntry(This->cache, name, cache_free);
1184 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1186 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1187 TRACE("(%p)->(%p)\n", This, length);
1191 *length = xmlHashSize(This->cache);
1195 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1197 cache_index_data* index_data = (cache_index_data*)index;
1199 if (index_data->index-- == 0)
1200 *index_data->out = bstr_from_xmlChar(name);
1203 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1204 LONG index, BSTR* len)
1206 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1207 cache_index_data data = {index,len};
1208 TRACE("(%p)->(%i, %p)\n", This, index, len);
1214 if (index >= xmlHashSize(This->cache))
1217 xmlHashScan(This->cache, cache_index, &data);
1221 static void cache_copy(void* data, void* dest, xmlChar* name)
1223 schema_cache* This = (schema_cache*) dest;
1224 cache_entry* entry = (cache_entry*) data;
1226 if (xmlHashLookup(This->cache, name) == NULL)
1228 cache_entry_add_ref(entry);
1229 xmlHashAddEntry(This->cache, name, entry);
1233 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1234 IXMLDOMSchemaCollection* otherCollection)
1236 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1237 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1238 TRACE("(%p)->(%p)\n", This, That);
1240 if (!otherCollection)
1243 /* TODO: detect errors while copying & return E_FAIL */
1244 xmlHashScan(That->cache, cache_copy, This);
1249 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1258 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1264 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1265 VARIANT_BOOL validateOnLoad)
1271 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1272 VARIANT_BOOL* validateOnLoad)
1278 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1279 BSTR namespaceURI, ISchema** schema)
1287 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1288 IXMLDOMNode* node, ISchemaItem** item)
1296 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1298 schema_cache_QueryInterface,
1299 schema_cache_AddRef,
1300 schema_cache_Release,
1301 schema_cache_GetTypeInfoCount,
1302 schema_cache_GetTypeInfo,
1303 schema_cache_GetIDsOfNames,
1304 schema_cache_Invoke,
1307 schema_cache_remove,
1308 schema_cache_get_length,
1309 schema_cache_get_namespaceURI,
1310 schema_cache_addCollection,
1311 schema_cache_get__newEnum,
1312 schema_cache_validate,
1313 schema_cache_put_validateOnLoad,
1314 schema_cache_get_validateOnLoad,
1315 schema_cache_getSchema,
1316 schema_cache_getDeclaration
1319 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1321 xmlSchemaElementPtr decl = NULL;
1322 xmlChar const* nsURI = get_node_nsURI(node);
1324 TRACE("(%p, %p)\n", schema, node);
1326 if (xmlStrEqual(nsURI, schema->targetNamespace))
1327 decl = xmlHashLookup(schema->elemDecl, node->name);
1329 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1331 FIXME("declaration not found in main schema - need to check schema imports!\n");
1332 /*xmlSchemaImportPtr import;
1334 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1336 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1339 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1345 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1347 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1348 while (decl != NULL && decl->refDecl != NULL)
1349 decl = decl->refDecl;
1350 return (decl != NULL)? decl->node : NULL;
1353 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1355 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1356 xmlSchemaPtr schema;
1358 TRACE("(%p, %p)\n", This, tree);
1363 if (tree->type == XML_DOCUMENT_NODE)
1364 tree = xmlDocGetRootElement(tree->doc);
1366 schema = get_node_schema(This, tree);
1367 /* TODO: if the ns is not in the cache, and it's a URL,
1368 * do we try to load from that? */
1370 return Schema_validate_tree(schema, tree);
1372 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1377 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1379 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1380 xmlSchemaPtr schema = get_node_schema(This, node);
1381 XDR_DT dt = DT_INVALID;
1383 TRACE("(%p, %p)\n", This, node);
1385 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1387 dt = str_to_dt(node->name, -1);
1392 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1394 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1397 dt = str_to_dt(str, -1);
1405 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1407 schema_cache* This = heap_alloc(sizeof(schema_cache));
1409 return E_OUTOFMEMORY;
1411 This->lpVtbl = &schema_cache_vtbl;
1412 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1415 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1416 This->version = MSXML3;
1417 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1418 This->version = MSXML4;
1419 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1420 This->version = MSXML6;
1422 This->version = MSXML_DEFAULT;
1424 *ppObj = &This->lpVtbl;
1430 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1432 MESSAGE("This program tried to use a SchemaCache object, but\n"
1433 "libxml2 support was not present at compile time.\n");