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
29 # include <libxml/parser.h>
30 # include <libxml/xmlerror.h>
31 # include <libxml/tree.h>
32 # include <libxml/xmlschemas.h>
33 # include <libxml/schemasInternals.h>
34 # include <libxml/hash.h>
35 # include <libxml/parser.h>
36 # include <libxml/parserInternals.h>
37 # include <libxml/xmlIO.h>
46 #include "wine/debug.h"
48 #include "msxml_private.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
52 /* We use a chained hashtable, which can hold any number of schemas
53 * TODO: grow/shrink hashtable depending on load factor
54 * TODO: implement read-only where appropriate
57 /* This is just the number of buckets, should be prime */
58 #define DEFAULT_HASHTABLE_SIZE 17
62 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
64 static const xmlChar XSD_schema[] = "schema";
65 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
66 static const xmlChar XDR_schema[] = "Schema";
67 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
68 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
70 static xmlChar const* datatypes_src;
71 static int datatypes_len;
72 static HGLOBAL datatypes_handle;
73 static HRSRC datatypes_rsrc;
74 static xmlSchemaPtr datatypes_schema;
76 static const WCHAR emptyW[] = {0};
84 typedef enum _SCHEMA_TYPE {
90 typedef struct _schema_cache
92 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
93 MSXML_VERSION version;
94 xmlHashTablePtr cache;
98 typedef struct _cache_entry
106 typedef struct _cache_index_data
112 /* datatypes lookup stuff
113 * generated with help from gperf */
114 #define DT_MIN_STR_LEN 2
115 #define DT_MAX_STR_LEN 11
116 #define DT_MIN_HASH_VALUE 2
117 #define DT_MAX_HASH_VALUE 115
119 static const xmlChar DT_bin_base64[] = "bin.base64";
120 static const xmlChar DT_bin_hex[] = "bin.hex";
121 static const xmlChar DT_boolean[] = "boolean";
122 static const xmlChar DT_char[] = "char";
123 static const xmlChar DT_date[] = "date";
124 static const xmlChar DT_date_tz[] = "date.tz";
125 static const xmlChar DT_dateTime[] = "dateTime";
126 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
127 static const xmlChar DT_entity[] = "entity";
128 static const xmlChar DT_entities[] = "entities";
129 static const xmlChar DT_enumeration[] = "enumeration";
130 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
131 static const xmlChar DT_float[] = "float";
132 static const xmlChar DT_i1[] = "i1";
133 static const xmlChar DT_i2[] = "i2";
134 static const xmlChar DT_i4[] = "i4";
135 static const xmlChar DT_i8[] = "i8";
136 static const xmlChar DT_id[] = "id";
137 static const xmlChar DT_idref[] = "idref";
138 static const xmlChar DT_idrefs[] = "idrefs";
139 static const xmlChar DT_int[] = "int";
140 static const xmlChar DT_nmtoken[] = "nmtoken";
141 static const xmlChar DT_nmtokens[] = "nmtokens";
142 static const xmlChar DT_notation[] = "notation";
143 static const xmlChar DT_number[] = "number";
144 static const xmlChar DT_r4[] = "r4";
145 static const xmlChar DT_r8[] = "r8";
146 static const xmlChar DT_string[] = "string";
147 static const xmlChar DT_time[] = "time";
148 static const xmlChar DT_time_tz[] = "time.tz";
149 static const xmlChar DT_ui1[] = "ui1";
150 static const xmlChar DT_ui2[] = "ui2";
151 static const xmlChar DT_ui4[] = "ui4";
152 static const xmlChar DT_ui8[] = "ui8";
153 static const xmlChar DT_uri[] = "uri";
154 static const xmlChar DT_uuid[] = "uuid";
156 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
157 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
158 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
159 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
160 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
161 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
162 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
163 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
164 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
165 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
166 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
167 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
168 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
169 static const OLECHAR wDT_i1[] = {'i','1',0};
170 static const OLECHAR wDT_i2[] = {'i','2',0};
171 static const OLECHAR wDT_i4[] = {'i','4',0};
172 static const OLECHAR wDT_i8[] = {'i','8',0};
173 static const OLECHAR wDT_id[] = {'i','d',0};
174 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
175 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
176 static const OLECHAR wDT_int[] = {'i','n','t',0};
177 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
178 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
179 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
180 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
181 static const OLECHAR wDT_r4[] = {'r','4',0};
182 static const OLECHAR wDT_r8[] = {'r','8',0};
183 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
184 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
185 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
186 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
187 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
188 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
189 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
190 static const OLECHAR wDT_uri[] = {'u','r','i',0};
191 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
193 static const BYTE hash_assoc_values[] =
195 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
196 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
197 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
198 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
199 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
200 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
201 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
202 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
203 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
204 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
205 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
206 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
207 0, 0, 10, 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, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
216 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
217 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
218 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
219 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
220 116, 116, 116, 116, 116, 116
223 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
227 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
231 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
235 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
239 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
240 static void parser_serror(void* ctx, xmlErrorPtr err)
242 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
246 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
248 TRACE("(%p)\n", spctx);
250 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
251 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
252 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
255 return xmlSchemaParse(spctx);
258 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
262 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
266 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
270 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
274 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
275 static void validate_serror(void* ctx, xmlErrorPtr err)
277 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
281 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
283 xmlSchemaValidCtxtPtr svctx;
286 TRACE("(%p, %p)\n", schema, tree);
287 /* TODO: if validateOnLoad property is false,
288 * we probably need to validate the schema here. */
289 svctx = xmlSchemaNewValidCtxt(schema);
290 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
291 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
292 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
295 if (tree->type == XML_DOCUMENT_NODE)
296 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
298 err = xmlSchemaValidateOneElement(svctx, tree);
300 xmlSchemaFreeValidCtxt(svctx);
301 return err? S_FALSE : S_OK;
304 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
306 DWORD hval = (len == -1)? xmlStrlen(str) : len;
311 hval += hash_assoc_values[str[10]];
314 hval += hash_assoc_values[str[9]];
317 hval += hash_assoc_values[str[8]];
320 hval += hash_assoc_values[str[7]];
323 hval += hash_assoc_values[str[6]];
326 hval += hash_assoc_values[str[5]];
329 hval += hash_assoc_values[str[4]];
332 hval += hash_assoc_values[str[3]];
335 hval += hash_assoc_values[str[2]];
338 hval += hash_assoc_values[str[1]];
341 hval += hash_assoc_values[str[0]];
347 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
349 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
354 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
357 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
360 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
363 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
366 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
369 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
372 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
375 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
378 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
381 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
384 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
390 static const xmlChar *const DT_string_table[DT__N_TYPES] =
430 static const WCHAR *const DT_wstring_table[DT__N_TYPES] =
470 static const XDR_DT DT_lookup_table[] =
523 -1, -1, -1, -1, -1, -1, -1, -1, -1,
524 -1, -1, -1, -1, -1, -1, -1, -1, -1,
525 -1, -1, -1, -1, -1, -1, -1, -1, -1,
526 -1, -1, -1, -1, -1, -1, -1, -1, -1,
527 -1, -1, -1, -1, -1, -1, -1, -1, -1,
528 -1, -1, -1, -1, -1, -1, -1, -1,
532 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
534 DWORD hash = dt_hash(str, len);
535 XDR_DT dt = DT_INVALID;
537 if (hash <= DT_MAX_HASH_VALUE)
538 dt = DT_lookup_table[hash];
540 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
546 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
548 DWORD hash = dt_hash_bstr(bstr, len);
549 XDR_DT dt = DT_INVALID;
551 if (hash <= DT_MAX_HASH_VALUE)
552 dt = DT_lookup_table[hash];
554 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
560 xmlChar const* dt_to_str(XDR_DT dt)
562 if (dt == DT_INVALID)
565 return DT_string_table[dt];
568 OLECHAR const* dt_to_bstr(XDR_DT dt)
570 if (dt == DT_INVALID)
573 return DT_wstring_table[dt];
576 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
583 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
585 if (!datatypes_schema)
587 xmlSchemaParserCtxtPtr spctx;
588 assert(datatypes_src != NULL);
589 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
590 datatypes_schema = Schema_parse(spctx);
591 xmlSchemaFreeParserCtxt(spctx);
627 assert(datatypes_schema != NULL);
628 if (content && xmlStrlen(content))
630 tmp_doc = xmlNewDoc(NULL);
631 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
632 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
634 xmlDocSetRootElement(tmp_doc, node);
636 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
640 { /* probably the node is being created manually and has no content yet */
645 FIXME("need to handle dt:%s\n", dt_to_str(dt));
650 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
652 return (node->ns != NULL)? node->ns->href : NULL;
655 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
657 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
658 xmlHashLookup(This->cache, nsURI);
661 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
663 cache_entry* entry = get_entry(This, get_node_nsURI(node));
664 return (!entry)? NULL : entry->schema;
667 static xmlExternalEntityLoader _external_entity_loader;
669 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
670 xmlParserCtxtPtr ctxt)
672 xmlParserInputPtr input;
674 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
676 assert(MSXML_hInstance != NULL);
677 assert(datatypes_rsrc != NULL);
678 assert(datatypes_handle != NULL);
679 assert(datatypes_src != NULL);
681 /* TODO: if the desired schema is in the cache, load it from there */
682 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
684 TRACE("loading built-in schema for %s\n", URL);
685 input = xmlNewStringInputStream(ctxt, datatypes_src);
689 input = _external_entity_loader(URL, ID, ctxt);
695 void schemasInit(void)
699 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
701 FIXME("failed to find resource for %s\n", DT_nsURI);
705 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
707 FIXME("failed to load resource for %s\n", DT_nsURI);
710 buf = LockResource(datatypes_handle);
711 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
713 /* Resource is loaded as raw data,
714 * need a null-terminated string */
715 while (buf[len] != '>')
717 datatypes_src = BAD_CAST buf;
718 datatypes_len = len + 1;
720 if (xmlGetExternalEntityLoader() != external_entity_loader)
722 _external_entity_loader = xmlGetExternalEntityLoader();
723 xmlSetExternalEntityLoader(external_entity_loader);
727 void schemasCleanup(void)
729 xmlSchemaFree(datatypes_schema);
730 xmlSetExternalEntityLoader(_external_entity_loader);
733 static LONG cache_entry_add_ref(cache_entry* entry)
735 LONG ref = InterlockedIncrement(&entry->ref);
736 TRACE("%p new ref %d\n", entry, ref);
740 static LONG cache_entry_release(cache_entry* entry)
742 LONG ref = InterlockedDecrement(&entry->ref);
743 TRACE("%p new ref %d\n", entry, ref);
747 if (entry->type == SCHEMA_TYPE_XSD)
749 xmldoc_release(entry->doc);
750 entry->schema->doc = NULL;
751 xmlSchemaFree(entry->schema);
754 else /* SCHEMA_TYPE_XDR */
756 xmldoc_release(entry->doc);
757 xmldoc_release(entry->schema->doc);
758 entry->schema->doc = NULL;
759 xmlSchemaFree(entry->schema);
766 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
768 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
771 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
773 xmlNodePtr root = NULL;
775 root = xmlDocGetRootElement(schema);
776 if (root && root->ns)
779 if (xmlStrEqual(root->name, XDR_schema) &&
780 xmlStrEqual(root->ns->href, XDR_nsURI))
782 return SCHEMA_TYPE_XDR;
784 else if (xmlStrEqual(root->name, XSD_schema) &&
785 xmlStrEqual(root->ns->href, XSD_nsURI))
787 return SCHEMA_TYPE_XSD;
790 return SCHEMA_TYPE_INVALID;
793 static BOOL link_datatypes(xmlDocPtr schema)
795 xmlNodePtr root, next, child;
798 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
799 root = xmlDocGetRootElement(schema);
803 for (ns = root->nsDef; ns != NULL; ns = ns->next)
805 if (xmlStrEqual(ns->href, DT_nsURI))
812 next = xmlFirstElementChild(root);
813 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
814 if (next) child = xmlAddPrevSibling(next, child);
815 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
816 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
821 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
823 cache_entry* entry = heap_alloc(sizeof(cache_entry));
824 xmlSchemaParserCtxtPtr spctx;
825 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
827 link_datatypes(new_doc);
829 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
830 * do we need to do something special here? */
831 entry->type = SCHEMA_TYPE_XSD;
833 spctx = xmlSchemaNewDocParserCtxt(new_doc);
835 if ((entry->schema = Schema_parse(spctx)))
837 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
838 entry->doc = entry->schema->doc;
839 xmldoc_add_ref(entry->doc);
843 FIXME("failed to parse doc\n");
848 xmlSchemaFreeParserCtxt(spctx);
852 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
854 cache_entry* entry = heap_alloc(sizeof(cache_entry));
855 xmlSchemaParserCtxtPtr spctx;
856 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
858 link_datatypes(xsd_doc);
860 entry->type = SCHEMA_TYPE_XDR;
862 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
864 if ((entry->schema = Schema_parse(spctx)))
866 entry->doc = new_doc;
867 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
868 xmldoc_init(entry->doc, DOMDocument_version(v));
869 xmldoc_add_ref(entry->doc);
870 xmldoc_add_ref(entry->schema->doc);
874 FIXME("failed to parse doc\n");
880 xmlSchemaFreeParserCtxt(spctx);
885 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
888 IXMLDOMDocument3* domdoc = NULL;
889 xmlDocPtr doc = NULL;
890 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
891 VARIANT_BOOL b = VARIANT_FALSE;
892 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
896 FIXME("failed to create domdoc\n");
899 assert(domdoc != NULL);
900 assert(V_VT(&url) == VT_BSTR);
902 hr = IXMLDOMDocument3_load(domdoc, url, &b);
905 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
906 if (b != VARIANT_TRUE)
908 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
909 IXMLDOMDocument3_Release(domdoc);
913 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
914 type = schema_type_from_xmlDocPtr(doc);
918 case SCHEMA_TYPE_XSD:
919 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
921 case SCHEMA_TYPE_XDR:
922 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
924 case SCHEMA_TYPE_INVALID:
926 FIXME("invalid schema\n");
929 IXMLDOMDocument3_Release(domdoc);
934 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
935 REFIID riid, void** ppvObject)
937 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
939 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
941 if ( IsEqualIID(riid, &IID_IUnknown) ||
942 IsEqualIID(riid, &IID_IDispatch) ||
943 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
944 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
950 FIXME("interface %s not implemented\n", debugstr_guid(riid));
952 return E_NOINTERFACE;
955 IXMLDOMSchemaCollection2_AddRef(iface);
960 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
962 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
963 LONG ref = InterlockedIncrement(&This->ref);
964 TRACE("%p new ref %d\n", This, ref);
968 static void cache_free(void* data, xmlChar* name /* ignored */)
970 cache_entry_release((cache_entry*)data);
973 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
975 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
976 LONG ref = InterlockedDecrement(&This->ref);
977 TRACE("%p new ref %d\n", This, ref);
981 xmlHashFree(This->cache, cache_free);
988 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
991 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
993 TRACE("(%p)->(%p)\n", This, pctinfo);
1000 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1001 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1003 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1006 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1008 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1013 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1014 REFIID riid, LPOLESTR* rgszNames,
1015 UINT cNames, LCID lcid, DISPID* rgDispId)
1017 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1018 ITypeInfo* typeinfo;
1021 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1024 if(!rgszNames || cNames == 0 || !rgDispId)
1025 return E_INVALIDARG;
1027 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1030 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1031 ITypeInfo_Release(typeinfo);
1037 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1038 DISPID dispIdMember, REFIID riid, LCID lcid,
1039 WORD wFlags, DISPPARAMS* pDispParams,
1040 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1043 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1044 ITypeInfo* typeinfo;
1047 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1048 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1050 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1053 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1054 pVarResult, pExcepInfo, puArgErr);
1055 ITypeInfo_Release(typeinfo);
1061 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1063 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1064 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1065 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1071 xmlHashRemoveEntry(This->cache, name, cache_free);
1077 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1081 cache_entry_add_ref(entry);
1089 xmlHashRemoveEntry(This->cache, name, cache_free);
1090 xmlHashAddEntry(This->cache, name, entry);
1096 xmlDocPtr doc = NULL;
1099 IXMLDOMNode* domnode = NULL;
1100 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1103 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1107 IXMLDOMNode_Release(domnode);
1109 return E_INVALIDARG;
1111 type = schema_type_from_xmlDocPtr(doc);
1113 if (type == SCHEMA_TYPE_XSD)
1115 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1117 else if (type == SCHEMA_TYPE_XDR)
1119 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1123 WARN("invalid schema!\n");
1127 IXMLDOMNode_Release(domnode);
1131 cache_entry_add_ref(entry);
1139 xmlHashRemoveEntry(This->cache, name, cache_free);
1140 xmlHashAddEntry(This->cache, name, entry);
1147 return E_INVALIDARG;
1154 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1157 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1160 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1165 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1166 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1169 /* TODO: this should be read-only */
1171 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1177 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1179 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1180 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1181 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1183 xmlHashRemoveEntry(This->cache, name, cache_free);
1188 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1190 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1191 TRACE("(%p)->(%p)\n", This, length);
1195 *length = xmlHashSize(This->cache);
1199 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1201 cache_index_data* index_data = (cache_index_data*)index;
1203 if (index_data->index-- == 0)
1204 *index_data->out = bstr_from_xmlChar(name);
1207 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1208 LONG index, BSTR* len)
1210 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1211 cache_index_data data = {index,len};
1212 TRACE("(%p)->(%i, %p)\n", This, index, len);
1218 if (index >= xmlHashSize(This->cache))
1221 xmlHashScan(This->cache, cache_index, &data);
1225 static void cache_copy(void* data, void* dest, xmlChar* name)
1227 schema_cache* This = (schema_cache*) dest;
1228 cache_entry* entry = (cache_entry*) data;
1230 if (xmlHashLookup(This->cache, name) == NULL)
1232 cache_entry_add_ref(entry);
1233 xmlHashAddEntry(This->cache, name, entry);
1237 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1238 IXMLDOMSchemaCollection* otherCollection)
1240 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1241 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1242 TRACE("(%p)->(%p)\n", This, That);
1244 if (!otherCollection)
1247 /* TODO: detect errors while copying & return E_FAIL */
1248 xmlHashScan(That->cache, cache_copy, This);
1253 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1262 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1268 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1269 VARIANT_BOOL validateOnLoad)
1275 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1276 VARIANT_BOOL* validateOnLoad)
1282 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1283 BSTR namespaceURI, ISchema** schema)
1291 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1292 IXMLDOMNode* node, ISchemaItem** item)
1300 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1302 schema_cache_QueryInterface,
1303 schema_cache_AddRef,
1304 schema_cache_Release,
1305 schema_cache_GetTypeInfoCount,
1306 schema_cache_GetTypeInfo,
1307 schema_cache_GetIDsOfNames,
1308 schema_cache_Invoke,
1311 schema_cache_remove,
1312 schema_cache_get_length,
1313 schema_cache_get_namespaceURI,
1314 schema_cache_addCollection,
1315 schema_cache_get__newEnum,
1316 schema_cache_validate,
1317 schema_cache_put_validateOnLoad,
1318 schema_cache_get_validateOnLoad,
1319 schema_cache_getSchema,
1320 schema_cache_getDeclaration
1323 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1325 xmlSchemaElementPtr decl = NULL;
1326 xmlChar const* nsURI = get_node_nsURI(node);
1328 TRACE("(%p, %p)\n", schema, node);
1330 if (xmlStrEqual(nsURI, schema->targetNamespace))
1331 decl = xmlHashLookup(schema->elemDecl, node->name);
1333 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1335 FIXME("declaration not found in main schema - need to check schema imports!\n");
1336 /*xmlSchemaImportPtr import;
1338 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1340 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1343 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1349 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1351 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1352 while (decl != NULL && decl->refDecl != NULL)
1353 decl = decl->refDecl;
1354 return (decl != NULL)? decl->node : NULL;
1357 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1359 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1360 xmlSchemaPtr schema;
1362 TRACE("(%p, %p)\n", This, tree);
1367 if (tree->type == XML_DOCUMENT_NODE)
1368 tree = xmlDocGetRootElement(tree->doc);
1370 schema = get_node_schema(This, tree);
1371 /* TODO: if the ns is not in the cache, and it's a URL,
1372 * do we try to load from that? */
1374 return Schema_validate_tree(schema, tree);
1376 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1381 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1383 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1384 xmlSchemaPtr schema = get_node_schema(This, node);
1385 XDR_DT dt = DT_INVALID;
1387 TRACE("(%p, %p)\n", This, node);
1389 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1391 dt = str_to_dt(node->name, -1);
1396 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1398 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1401 dt = str_to_dt(str, -1);
1409 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1411 schema_cache* This = heap_alloc(sizeof(schema_cache));
1413 return E_OUTOFMEMORY;
1415 This->lpVtbl = &schema_cache_vtbl;
1416 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1419 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1420 This->version = MSXML3;
1421 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1422 This->version = MSXML4;
1423 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1424 This->version = MSXML6;
1426 This->version = MSXML_DEFAULT;
1428 *ppObj = &This->lpVtbl;
1434 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1436 MESSAGE("This program tried to use a SchemaCache object, but\n"
1437 "libxml2 support was not present at compile time.\n");