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>
38 # include <libxml/xmlversion.h>
47 #include "wine/debug.h"
49 #include "msxml_private.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
53 /* We use a chained hashtable, which can hold any number of schemas
54 * TODO: grow/shrink hashtable depending on load factor
55 * TODO: implement read-only where appropriate
58 /* This is just the number of buckets, should be prime */
59 #define DEFAULT_HASHTABLE_SIZE 17
63 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
65 static const xmlChar XSD_schema[] = "schema";
66 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
67 static const xmlChar XDR_schema[] = "Schema";
68 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
69 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
71 static xmlChar const* datatypes_src;
72 static int datatypes_len;
73 static HGLOBAL datatypes_handle;
74 static HRSRC datatypes_rsrc;
75 static xmlSchemaPtr datatypes_schema;
77 static const WCHAR emptyW[] = {0};
85 typedef enum _SCHEMA_TYPE {
91 typedef struct _schema_cache
93 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
94 MSXML_VERSION version;
95 xmlHashTablePtr cache;
99 typedef struct _cache_entry
107 typedef struct _cache_index_data
113 /* datatypes lookup stuff
114 * generated with help from gperf */
115 #define DT_MIN_STR_LEN 2
116 #define DT_MAX_STR_LEN 11
117 #define DT_MIN_HASH_VALUE 2
118 #define DT_MAX_HASH_VALUE 115
120 static const xmlChar DT_bin_base64[] = "bin.base64";
121 static const xmlChar DT_bin_hex[] = "bin.hex";
122 static const xmlChar DT_boolean[] = "boolean";
123 static const xmlChar DT_char[] = "char";
124 static const xmlChar DT_date[] = "date";
125 static const xmlChar DT_date_tz[] = "date.tz";
126 static const xmlChar DT_dateTime[] = "dateTime";
127 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
128 static const xmlChar DT_entity[] = "entity";
129 static const xmlChar DT_entities[] = "entities";
130 static const xmlChar DT_enumeration[] = "enumeration";
131 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
132 static const xmlChar DT_float[] = "float";
133 static const xmlChar DT_i1[] = "i1";
134 static const xmlChar DT_i2[] = "i2";
135 static const xmlChar DT_i4[] = "i4";
136 static const xmlChar DT_i8[] = "i8";
137 static const xmlChar DT_id[] = "id";
138 static const xmlChar DT_idref[] = "idref";
139 static const xmlChar DT_idrefs[] = "idrefs";
140 static const xmlChar DT_int[] = "int";
141 static const xmlChar DT_nmtoken[] = "nmtoken";
142 static const xmlChar DT_nmtokens[] = "nmtokens";
143 static const xmlChar DT_notation[] = "notation";
144 static const xmlChar DT_number[] = "number";
145 static const xmlChar DT_r4[] = "r4";
146 static const xmlChar DT_r8[] = "r8";
147 static const xmlChar DT_string[] = "string";
148 static const xmlChar DT_time[] = "time";
149 static const xmlChar DT_time_tz[] = "time.tz";
150 static const xmlChar DT_ui1[] = "ui1";
151 static const xmlChar DT_ui2[] = "ui2";
152 static const xmlChar DT_ui4[] = "ui4";
153 static const xmlChar DT_ui8[] = "ui8";
154 static const xmlChar DT_uri[] = "uri";
155 static const xmlChar DT_uuid[] = "uuid";
157 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
158 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
159 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
160 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
161 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
162 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
163 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
164 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
165 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
166 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
167 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
168 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
169 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
170 static const OLECHAR wDT_i1[] = {'i','1',0};
171 static const OLECHAR wDT_i2[] = {'i','2',0};
172 static const OLECHAR wDT_i4[] = {'i','4',0};
173 static const OLECHAR wDT_i8[] = {'i','8',0};
174 static const OLECHAR wDT_id[] = {'i','d',0};
175 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
176 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
177 static const OLECHAR wDT_int[] = {'i','n','t',0};
178 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
179 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
180 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
181 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
182 static const OLECHAR wDT_r4[] = {'r','4',0};
183 static const OLECHAR wDT_r8[] = {'r','8',0};
184 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
185 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
186 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
187 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
188 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
189 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
190 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
191 static const OLECHAR wDT_uri[] = {'u','r','i',0};
192 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
194 static const BYTE hash_assoc_values[] =
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, 116, 116, 116, 116,
200 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
201 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
202 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
203 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
204 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
205 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
206 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
207 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
208 0, 0, 10, 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, 116, 116, 116, 116,
221 116, 116, 116, 116, 116, 116
224 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
228 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
232 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
236 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
240 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
241 static void parser_serror(void* ctx, xmlErrorPtr err)
243 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
247 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
249 TRACE("(%p)\n", spctx);
251 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
252 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
253 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
256 return xmlSchemaParse(spctx);
259 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
263 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
267 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
271 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
275 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
276 static void validate_serror(void* ctx, xmlErrorPtr err)
278 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
282 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
284 xmlSchemaValidCtxtPtr svctx;
287 TRACE("(%p, %p)\n", schema, tree);
288 /* TODO: if validateOnLoad property is false,
289 * we probably need to validate the schema here. */
290 svctx = xmlSchemaNewValidCtxt(schema);
291 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
292 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
293 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
296 if (tree->type == XML_DOCUMENT_NODE)
297 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
299 err = xmlSchemaValidateOneElement(svctx, tree);
301 xmlSchemaFreeValidCtxt(svctx);
302 return err? S_FALSE : S_OK;
305 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
307 DWORD hval = (len == -1)? xmlStrlen(str) : len;
312 hval += hash_assoc_values[str[10]];
315 hval += hash_assoc_values[str[9]];
318 hval += hash_assoc_values[str[8]];
321 hval += hash_assoc_values[str[7]];
324 hval += hash_assoc_values[str[6]];
327 hval += hash_assoc_values[str[5]];
330 hval += hash_assoc_values[str[4]];
333 hval += hash_assoc_values[str[3]];
336 hval += hash_assoc_values[str[2]];
339 hval += hash_assoc_values[str[1]];
342 hval += hash_assoc_values[str[0]];
348 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
350 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
355 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
358 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
361 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
364 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
367 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
370 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
373 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
376 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
379 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
382 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
385 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
391 static const xmlChar *const DT_string_table[DT__N_TYPES] =
431 static const WCHAR *const DT_wstring_table[DT__N_TYPES] =
471 static const XDR_DT DT_lookup_table[] =
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, -1,
529 -1, -1, -1, -1, -1, -1, -1, -1,
533 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
535 DWORD hash = dt_hash(str, len);
536 XDR_DT dt = DT_INVALID;
538 if (hash <= DT_MAX_HASH_VALUE)
539 dt = DT_lookup_table[hash];
541 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
547 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
549 DWORD hash = dt_hash_bstr(bstr, len);
550 XDR_DT dt = DT_INVALID;
552 if (hash <= DT_MAX_HASH_VALUE)
553 dt = DT_lookup_table[hash];
555 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
561 xmlChar const* dt_to_str(XDR_DT dt)
563 if (dt == DT_INVALID)
566 return DT_string_table[dt];
569 OLECHAR const* dt_to_bstr(XDR_DT dt)
571 if (dt == DT_INVALID)
574 return DT_wstring_table[dt];
577 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
584 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
586 if (!datatypes_schema)
588 xmlSchemaParserCtxtPtr spctx;
589 assert(datatypes_src != NULL);
590 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
591 datatypes_schema = Schema_parse(spctx);
592 xmlSchemaFreeParserCtxt(spctx);
628 if (!datatypes_schema)
630 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
631 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n");
633 /* Hopefully they don't need much in the way of XDR datatypes support... */
637 if (content && xmlStrlen(content))
639 tmp_doc = xmlNewDoc(NULL);
640 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
641 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
643 xmlDocSetRootElement(tmp_doc, node);
645 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
649 { /* probably the node is being created manually and has no content yet */
654 FIXME("need to handle dt:%s\n", dt_to_str(dt));
659 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
661 return (node->ns != NULL)? node->ns->href : NULL;
664 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
666 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
667 xmlHashLookup(This->cache, nsURI);
670 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
672 cache_entry* entry = get_entry(This, get_node_nsURI(node));
673 return (!entry)? NULL : entry->schema;
676 static xmlExternalEntityLoader _external_entity_loader;
678 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
679 xmlParserCtxtPtr ctxt)
681 xmlParserInputPtr input;
683 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
685 assert(MSXML_hInstance != NULL);
686 assert(datatypes_rsrc != NULL);
687 assert(datatypes_handle != NULL);
688 assert(datatypes_src != NULL);
690 /* TODO: if the desired schema is in the cache, load it from there */
691 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
693 TRACE("loading built-in schema for %s\n", URL);
694 input = xmlNewStringInputStream(ctxt, datatypes_src);
698 input = _external_entity_loader(URL, ID, ctxt);
704 void schemasInit(void)
708 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
710 FIXME("failed to find resource for %s\n", DT_nsURI);
714 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
716 FIXME("failed to load resource for %s\n", DT_nsURI);
719 buf = LockResource(datatypes_handle);
720 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
722 /* Resource is loaded as raw data,
723 * need a null-terminated string */
724 while (buf[len] != '>')
726 datatypes_src = BAD_CAST buf;
727 datatypes_len = len + 1;
729 if (xmlGetExternalEntityLoader() != external_entity_loader)
731 _external_entity_loader = xmlGetExternalEntityLoader();
732 xmlSetExternalEntityLoader(external_entity_loader);
736 void schemasCleanup(void)
738 xmlSchemaFree(datatypes_schema);
739 xmlSetExternalEntityLoader(_external_entity_loader);
742 static LONG cache_entry_add_ref(cache_entry* entry)
744 LONG ref = InterlockedIncrement(&entry->ref);
745 TRACE("%p new ref %d\n", entry, ref);
749 static LONG cache_entry_release(cache_entry* entry)
751 LONG ref = InterlockedDecrement(&entry->ref);
752 TRACE("%p new ref %d\n", entry, ref);
756 if (entry->type == SCHEMA_TYPE_XSD)
758 xmldoc_release(entry->doc);
759 entry->schema->doc = NULL;
760 xmlSchemaFree(entry->schema);
763 else /* SCHEMA_TYPE_XDR */
765 xmldoc_release(entry->doc);
766 xmldoc_release(entry->schema->doc);
767 entry->schema->doc = NULL;
768 xmlSchemaFree(entry->schema);
775 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
777 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
780 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
782 xmlNodePtr root = NULL;
784 root = xmlDocGetRootElement(schema);
785 if (root && root->ns)
788 if (xmlStrEqual(root->name, XDR_schema) &&
789 xmlStrEqual(root->ns->href, XDR_nsURI))
791 return SCHEMA_TYPE_XDR;
793 else if (xmlStrEqual(root->name, XSD_schema) &&
794 xmlStrEqual(root->ns->href, XSD_nsURI))
796 return SCHEMA_TYPE_XSD;
799 return SCHEMA_TYPE_INVALID;
802 static BOOL link_datatypes(xmlDocPtr schema)
804 xmlNodePtr root, next, child;
807 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
808 root = xmlDocGetRootElement(schema);
812 for (ns = root->nsDef; ns != NULL; ns = ns->next)
814 if (xmlStrEqual(ns->href, DT_nsURI))
821 next = xmlFirstElementChild(root);
822 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
823 if (next) child = xmlAddPrevSibling(next, child);
824 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
825 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
830 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
832 cache_entry* entry = heap_alloc(sizeof(cache_entry));
833 xmlSchemaParserCtxtPtr spctx;
834 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
836 link_datatypes(new_doc);
838 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
839 * do we need to do something special here? */
840 entry->type = SCHEMA_TYPE_XSD;
842 spctx = xmlSchemaNewDocParserCtxt(new_doc);
844 if ((entry->schema = Schema_parse(spctx)))
846 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
847 entry->doc = entry->schema->doc;
848 xmldoc_add_ref(entry->doc);
852 FIXME("failed to parse doc\n");
857 xmlSchemaFreeParserCtxt(spctx);
861 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
863 cache_entry* entry = heap_alloc(sizeof(cache_entry));
864 xmlSchemaParserCtxtPtr spctx;
865 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
867 link_datatypes(xsd_doc);
869 entry->type = SCHEMA_TYPE_XDR;
871 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
873 if ((entry->schema = Schema_parse(spctx)))
875 entry->doc = new_doc;
876 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
877 xmldoc_init(entry->doc, DOMDocument_version(v));
878 xmldoc_add_ref(entry->doc);
879 xmldoc_add_ref(entry->schema->doc);
883 FIXME("failed to parse doc\n");
889 xmlSchemaFreeParserCtxt(spctx);
894 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
897 IXMLDOMDocument3* domdoc = NULL;
898 xmlDocPtr doc = NULL;
899 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
900 VARIANT_BOOL b = VARIANT_FALSE;
901 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
905 FIXME("failed to create domdoc\n");
908 assert(domdoc != NULL);
909 assert(V_VT(&url) == VT_BSTR);
911 hr = IXMLDOMDocument3_load(domdoc, url, &b);
914 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
915 if (b != VARIANT_TRUE)
917 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
918 IXMLDOMDocument3_Release(domdoc);
922 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
923 type = schema_type_from_xmlDocPtr(doc);
927 case SCHEMA_TYPE_XSD:
928 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
930 case SCHEMA_TYPE_XDR:
931 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
933 case SCHEMA_TYPE_INVALID:
935 FIXME("invalid schema\n");
938 IXMLDOMDocument3_Release(domdoc);
943 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
944 REFIID riid, void** ppvObject)
946 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
948 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
950 if ( IsEqualIID(riid, &IID_IUnknown) ||
951 IsEqualIID(riid, &IID_IDispatch) ||
952 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
953 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
959 FIXME("interface %s not implemented\n", debugstr_guid(riid));
961 return E_NOINTERFACE;
964 IXMLDOMSchemaCollection2_AddRef(iface);
969 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
971 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
972 LONG ref = InterlockedIncrement(&This->ref);
973 TRACE("%p new ref %d\n", This, ref);
977 static void cache_free(void* data, xmlChar* name /* ignored */)
979 cache_entry_release((cache_entry*)data);
982 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
984 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
985 LONG ref = InterlockedDecrement(&This->ref);
986 TRACE("%p new ref %d\n", This, ref);
990 xmlHashFree(This->cache, cache_free);
997 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1000 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1002 TRACE("(%p)->(%p)\n", This, pctinfo);
1009 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1010 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1012 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1015 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1017 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1022 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1023 REFIID riid, LPOLESTR* rgszNames,
1024 UINT cNames, LCID lcid, DISPID* rgDispId)
1026 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1027 ITypeInfo* typeinfo;
1030 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1033 if(!rgszNames || cNames == 0 || !rgDispId)
1034 return E_INVALIDARG;
1036 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1039 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1040 ITypeInfo_Release(typeinfo);
1046 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1047 DISPID dispIdMember, REFIID riid, LCID lcid,
1048 WORD wFlags, DISPPARAMS* pDispParams,
1049 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1052 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1053 ITypeInfo* typeinfo;
1056 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1057 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1059 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1062 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1063 pVarResult, pExcepInfo, puArgErr);
1064 ITypeInfo_Release(typeinfo);
1070 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1072 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1073 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1074 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1080 xmlHashRemoveEntry(This->cache, name, cache_free);
1086 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1090 cache_entry_add_ref(entry);
1098 xmlHashRemoveEntry(This->cache, name, cache_free);
1099 xmlHashAddEntry(This->cache, name, entry);
1105 xmlDocPtr doc = NULL;
1108 IXMLDOMNode* domnode = NULL;
1109 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1112 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1116 IXMLDOMNode_Release(domnode);
1118 return E_INVALIDARG;
1120 type = schema_type_from_xmlDocPtr(doc);
1122 if (type == SCHEMA_TYPE_XSD)
1124 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1126 else if (type == SCHEMA_TYPE_XDR)
1128 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1132 WARN("invalid schema!\n");
1136 IXMLDOMNode_Release(domnode);
1140 cache_entry_add_ref(entry);
1148 xmlHashRemoveEntry(This->cache, name, cache_free);
1149 xmlHashAddEntry(This->cache, name, entry);
1156 return E_INVALIDARG;
1163 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1166 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1169 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1174 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1175 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1178 /* TODO: this should be read-only */
1180 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1186 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1188 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1189 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1190 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1192 xmlHashRemoveEntry(This->cache, name, cache_free);
1197 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1199 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1200 TRACE("(%p)->(%p)\n", This, length);
1204 *length = xmlHashSize(This->cache);
1208 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1210 cache_index_data* index_data = (cache_index_data*)index;
1212 if (index_data->index-- == 0)
1213 *index_data->out = bstr_from_xmlChar(name);
1216 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1217 LONG index, BSTR* len)
1219 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1220 cache_index_data data = {index,len};
1221 TRACE("(%p)->(%i, %p)\n", This, index, len);
1227 if (index >= xmlHashSize(This->cache))
1230 xmlHashScan(This->cache, cache_index, &data);
1234 static void cache_copy(void* data, void* dest, xmlChar* name)
1236 schema_cache* This = (schema_cache*) dest;
1237 cache_entry* entry = (cache_entry*) data;
1239 if (xmlHashLookup(This->cache, name) == NULL)
1241 cache_entry_add_ref(entry);
1242 xmlHashAddEntry(This->cache, name, entry);
1246 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1247 IXMLDOMSchemaCollection* otherCollection)
1249 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1250 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1251 TRACE("(%p)->(%p)\n", This, That);
1253 if (!otherCollection)
1256 /* TODO: detect errors while copying & return E_FAIL */
1257 xmlHashScan(That->cache, cache_copy, This);
1262 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1271 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1277 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1278 VARIANT_BOOL validateOnLoad)
1284 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1285 VARIANT_BOOL* validateOnLoad)
1291 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1292 BSTR namespaceURI, ISchema** schema)
1300 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1301 IXMLDOMNode* node, ISchemaItem** item)
1309 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1311 schema_cache_QueryInterface,
1312 schema_cache_AddRef,
1313 schema_cache_Release,
1314 schema_cache_GetTypeInfoCount,
1315 schema_cache_GetTypeInfo,
1316 schema_cache_GetIDsOfNames,
1317 schema_cache_Invoke,
1320 schema_cache_remove,
1321 schema_cache_get_length,
1322 schema_cache_get_namespaceURI,
1323 schema_cache_addCollection,
1324 schema_cache_get__newEnum,
1325 schema_cache_validate,
1326 schema_cache_put_validateOnLoad,
1327 schema_cache_get_validateOnLoad,
1328 schema_cache_getSchema,
1329 schema_cache_getDeclaration
1332 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1334 xmlSchemaElementPtr decl = NULL;
1335 xmlChar const* nsURI = get_node_nsURI(node);
1337 TRACE("(%p, %p)\n", schema, node);
1339 if (xmlStrEqual(nsURI, schema->targetNamespace))
1340 decl = xmlHashLookup(schema->elemDecl, node->name);
1342 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1344 FIXME("declaration not found in main schema - need to check schema imports!\n");
1345 /*xmlSchemaImportPtr import;
1347 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1349 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1352 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1358 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1360 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1361 while (decl != NULL && decl->refDecl != NULL)
1362 decl = decl->refDecl;
1363 return (decl != NULL)? decl->node : NULL;
1366 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1368 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1369 xmlSchemaPtr schema;
1371 TRACE("(%p, %p)\n", This, tree);
1376 if (tree->type == XML_DOCUMENT_NODE)
1377 tree = xmlDocGetRootElement(tree->doc);
1379 schema = get_node_schema(This, tree);
1380 /* TODO: if the ns is not in the cache, and it's a URL,
1381 * do we try to load from that? */
1383 return Schema_validate_tree(schema, tree);
1385 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1390 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1392 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1393 xmlSchemaPtr schema = get_node_schema(This, node);
1394 XDR_DT dt = DT_INVALID;
1396 TRACE("(%p, %p)\n", This, node);
1398 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1400 dt = str_to_dt(node->name, -1);
1405 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1407 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1410 dt = str_to_dt(str, -1);
1418 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1420 schema_cache* This = heap_alloc(sizeof(schema_cache));
1422 return E_OUTOFMEMORY;
1424 This->lpVtbl = &schema_cache_vtbl;
1425 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1428 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1429 This->version = MSXML3;
1430 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1431 This->version = MSXML4;
1432 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1433 This->version = MSXML6;
1435 This->version = MSXML_DEFAULT;
1437 *ppObj = &This->lpVtbl;
1443 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1445 MESSAGE("This program tried to use a SchemaCache object, but\n"
1446 "libxml2 support was not present at compile time.\n");