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;
67 static int datatypes_len;
68 static HGLOBAL datatypes_handle;
69 static HRSRC datatypes_rsrc;
70 static xmlSchemaPtr datatypes_schema;
72 static const WCHAR emptyW[] = {0};
80 typedef enum _SCHEMA_TYPE {
86 typedef struct _schema_cache
88 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
89 MSXML_VERSION version;
90 xmlHashTablePtr cache;
94 typedef struct _cache_entry
102 typedef struct _cache_index_data
108 /* datatypes lookup stuff
109 * generated with help from gperf */
110 #define DT_MIN_STR_LEN 2
111 #define DT_MAX_STR_LEN 11
112 #define DT_MIN_HASH_VALUE 2
113 #define DT_MAX_HASH_VALUE 115
115 static const xmlChar DT_bin_base64[] = "bin.base64";
116 static const xmlChar DT_bin_hex[] = "bin.hex";
117 static const xmlChar DT_boolean[] = "boolean";
118 static const xmlChar DT_char[] = "char";
119 static const xmlChar DT_date[] = "date";
120 static const xmlChar DT_date_tz[] = "date.tz";
121 static const xmlChar DT_dateTime[] = "dateTime";
122 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
123 static const xmlChar DT_entity[] = "entity";
124 static const xmlChar DT_entities[] = "entities";
125 static const xmlChar DT_enumeration[] = "enumeration";
126 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
127 static const xmlChar DT_float[] = "float";
128 static const xmlChar DT_i1[] = "i1";
129 static const xmlChar DT_i2[] = "i2";
130 static const xmlChar DT_i4[] = "i4";
131 static const xmlChar DT_i8[] = "i8";
132 static const xmlChar DT_id[] = "id";
133 static const xmlChar DT_idref[] = "idref";
134 static const xmlChar DT_idrefs[] = "idrefs";
135 static const xmlChar DT_int[] = "int";
136 static const xmlChar DT_nmtoken[] = "nmtoken";
137 static const xmlChar DT_nmtokens[] = "nmtokens";
138 static const xmlChar DT_notation[] = "notation";
139 static const xmlChar DT_number[] = "number";
140 static const xmlChar DT_r4[] = "r4";
141 static const xmlChar DT_r8[] = "r8";
142 static const xmlChar DT_string[] = "string";
143 static const xmlChar DT_time[] = "time";
144 static const xmlChar DT_time_tz[] = "time.tz";
145 static const xmlChar DT_ui1[] = "ui1";
146 static const xmlChar DT_ui2[] = "ui2";
147 static const xmlChar DT_ui4[] = "ui4";
148 static const xmlChar DT_ui8[] = "ui8";
149 static const xmlChar DT_uri[] = "uri";
150 static const xmlChar DT_uuid[] = "uuid";
152 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
153 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
154 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
155 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
156 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
157 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
158 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
159 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
160 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
161 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
162 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
163 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
164 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
165 static const OLECHAR wDT_i1[] = {'i','1',0};
166 static const OLECHAR wDT_i2[] = {'i','2',0};
167 static const OLECHAR wDT_i4[] = {'i','4',0};
168 static const OLECHAR wDT_i8[] = {'i','8',0};
169 static const OLECHAR wDT_id[] = {'i','d',0};
170 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
171 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
172 static const OLECHAR wDT_int[] = {'i','n','t',0};
173 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
174 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
175 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
176 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
177 static const OLECHAR wDT_r4[] = {'r','4',0};
178 static const OLECHAR wDT_r8[] = {'r','8',0};
179 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
180 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
181 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
182 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
183 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
184 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
185 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
186 static const OLECHAR wDT_uri[] = {'u','r','i',0};
187 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
189 static const BYTE hash_assoc_values[] =
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, 116, 116, 116, 116,
194 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
195 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
196 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
197 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
198 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
199 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
200 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
201 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
202 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
203 0, 0, 10, 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, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
216 116, 116, 116, 116, 116, 116
219 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
223 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
227 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
231 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
235 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
236 static void parser_serror(void* ctx, xmlErrorPtr err)
238 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
242 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
244 TRACE("(%p)\n", spctx);
246 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
247 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
248 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
251 return xmlSchemaParse(spctx);
254 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
258 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
262 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
266 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
270 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
271 static void validate_serror(void* ctx, xmlErrorPtr err)
273 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
277 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
279 xmlSchemaValidCtxtPtr svctx;
282 TRACE("(%p, %p)\n", schema, tree);
283 /* TODO: if validateOnLoad property is false,
284 * we probably need to validate the schema here. */
285 svctx = xmlSchemaNewValidCtxt(schema);
286 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
287 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
288 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
291 if (tree->type == XML_DOCUMENT_NODE)
292 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
294 err = xmlSchemaValidateOneElement(svctx, tree);
296 xmlSchemaFreeValidCtxt(svctx);
297 return err? S_FALSE : S_OK;
300 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
302 DWORD hval = (len == -1)? xmlStrlen(str) : len;
307 hval += hash_assoc_values[str[10]];
310 hval += hash_assoc_values[str[9]];
313 hval += hash_assoc_values[str[8]];
316 hval += hash_assoc_values[str[7]];
319 hval += hash_assoc_values[str[6]];
322 hval += hash_assoc_values[str[5]];
325 hval += hash_assoc_values[str[4]];
328 hval += hash_assoc_values[str[3]];
331 hval += hash_assoc_values[str[2]];
334 hval += hash_assoc_values[str[1]];
337 hval += hash_assoc_values[str[0]];
343 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
345 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
350 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
353 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
356 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
359 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
362 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
365 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
368 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
371 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
374 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
377 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
380 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
386 static const xmlChar const* DT_string_table[DT__N_TYPES] =
426 static const WCHAR const* DT_wstring_table[DT__N_TYPES] =
466 static const XDR_DT DT_lookup_table[] =
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, -1,
523 -1, -1, -1, -1, -1, -1, -1, -1, -1,
524 -1, -1, -1, -1, -1, -1, -1, -1,
528 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
530 DWORD hash = dt_hash(str, len);
531 XDR_DT dt = DT_INVALID;
533 if (hash <= DT_MAX_HASH_VALUE)
534 dt = DT_lookup_table[hash];
536 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
542 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
544 DWORD hash = dt_hash_bstr(bstr, len);
545 XDR_DT dt = DT_INVALID;
547 if (hash <= DT_MAX_HASH_VALUE)
548 dt = DT_lookup_table[hash];
550 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
556 xmlChar const* dt_to_str(XDR_DT dt)
558 if (dt == DT_INVALID)
561 return DT_string_table[dt];
564 OLECHAR const* dt_to_bstr(XDR_DT dt)
566 if (dt == DT_INVALID)
569 return DT_wstring_table[dt];
572 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
579 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
581 if (!datatypes_schema)
583 xmlSchemaParserCtxtPtr spctx;
584 assert(datatypes_src != NULL);
585 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
586 datatypes_schema = Schema_parse(spctx);
587 xmlSchemaFreeParserCtxt(spctx);
623 assert(datatypes_schema != NULL);
624 if (content && xmlStrlen(content))
626 tmp_doc = xmlNewDoc(NULL);
627 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
628 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
630 xmlDocSetRootElement(tmp_doc, node);
632 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
636 { /* probably the node is being created manually and has no content yet */
641 FIXME("need to handle dt:%s\n", dt_to_str(dt));
646 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
648 return (node->ns != NULL)? node->ns->href : NULL;
651 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
653 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
654 xmlHashLookup(This->cache, nsURI);
657 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
659 cache_entry* entry = get_entry(This, get_node_nsURI(node));
660 return (!entry)? NULL : entry->schema;
663 xmlExternalEntityLoader _external_entity_loader = NULL;
665 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
666 xmlParserCtxtPtr ctxt)
668 xmlParserInputPtr input;
670 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
672 assert(MSXML_hInstance != NULL);
673 assert(datatypes_rsrc != NULL);
674 assert(datatypes_handle != NULL);
675 assert(datatypes_src != NULL);
677 /* TODO: if the desired schema is in the cache, load it from there */
678 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
680 TRACE("loading built-in schema for %s\n", URL);
681 input = xmlNewStringInputStream(ctxt, datatypes_src);
685 input = _external_entity_loader(URL, ID, ctxt);
691 void schemasInit(void)
695 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
697 FIXME("failed to find resource for %s\n", DT_nsURI);
701 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
703 FIXME("failed to load resource for %s\n", DT_nsURI);
706 buf = LockResource(datatypes_handle);
707 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
709 /* Resource is loaded as raw data,
710 * need a null-terminated string */
711 while (buf[len] != '>')
713 datatypes_src = BAD_CAST buf;
714 datatypes_len = len + 1;
716 if ((void*)xmlGetExternalEntityLoader() != (void*)external_entity_loader)
718 _external_entity_loader = xmlGetExternalEntityLoader();
719 xmlSetExternalEntityLoader(external_entity_loader);
723 void schemasCleanup(void)
725 if (datatypes_handle)
726 FreeResource(datatypes_handle);
727 if (datatypes_schema)
728 xmlSchemaFree(datatypes_schema);
729 xmlSetExternalEntityLoader(_external_entity_loader);
732 static LONG cache_entry_add_ref(cache_entry* entry)
734 LONG ref = InterlockedIncrement(&entry->ref);
735 TRACE("%p new ref %d\n", entry, ref);
739 static LONG cache_entry_release(cache_entry* entry)
741 LONG ref = InterlockedDecrement(&entry->ref);
742 TRACE("%p new ref %d\n", entry, ref);
746 if (entry->type == SCHEMA_TYPE_XSD)
748 xmldoc_release(entry->doc);
749 entry->schema->doc = NULL;
750 xmlSchemaFree(entry->schema);
753 else /* SCHEMA_TYPE_XDR */
755 xmldoc_release(entry->doc);
756 xmldoc_release(entry->schema->doc);
757 entry->schema->doc = NULL;
758 xmlSchemaFree(entry->schema);
765 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
767 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
770 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
772 xmlNodePtr root = NULL;
774 root = xmlDocGetRootElement(schema);
775 if (root && root->ns)
778 if (xmlStrEqual(root->name, XDR_schema) &&
779 xmlStrEqual(root->ns->href, XDR_nsURI))
781 return SCHEMA_TYPE_XDR;
783 else if (xmlStrEqual(root->name, XSD_schema) &&
784 xmlStrEqual(root->ns->href, XSD_nsURI))
786 return SCHEMA_TYPE_XSD;
789 return SCHEMA_TYPE_INVALID;
792 static BOOL link_datatypes(xmlDocPtr schema)
794 xmlNodePtr root, next, child;
797 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
798 root = xmlDocGetRootElement(schema);
802 for (ns = root->nsDef; ns != NULL; ns = ns->next)
804 if (xmlStrEqual(ns->href, DT_nsURI))
811 next = xmlFirstElementChild(root);
812 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
813 if (next) child = xmlAddPrevSibling(next, child);
814 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
815 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
820 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
822 cache_entry* entry = heap_alloc(sizeof(cache_entry));
823 xmlSchemaParserCtxtPtr spctx;
824 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
826 link_datatypes(new_doc);
828 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
829 * do we need to do something special here? */
830 entry->type = SCHEMA_TYPE_XSD;
832 spctx = xmlSchemaNewDocParserCtxt(new_doc);
834 if ((entry->schema = Schema_parse(spctx)))
836 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
837 entry->doc = entry->schema->doc;
838 xmldoc_add_ref(entry->doc);
842 FIXME("failed to parse doc\n");
847 xmlSchemaFreeParserCtxt(spctx);
851 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
853 cache_entry* entry = heap_alloc(sizeof(cache_entry));
854 xmlSchemaParserCtxtPtr spctx;
855 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
857 link_datatypes(xsd_doc);
859 entry->type = SCHEMA_TYPE_XDR;
861 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
863 if ((entry->schema = Schema_parse(spctx)))
865 entry->doc = new_doc;
866 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
867 xmldoc_init(entry->doc, DOMDocument_version(v));
868 xmldoc_add_ref(entry->doc);
869 xmldoc_add_ref(entry->schema->doc);
873 FIXME("failed to parse doc\n");
879 xmlSchemaFreeParserCtxt(spctx);
884 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
887 IXMLDOMDocument3* domdoc = NULL;
888 xmlDocPtr doc = NULL;
889 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
890 VARIANT_BOOL b = VARIANT_FALSE;
891 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
895 FIXME("failed to create domdoc\n");
898 assert(domdoc != NULL);
899 assert(V_VT(&url) == VT_BSTR);
901 hr = IXMLDOMDocument3_load(domdoc, url, &b);
904 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
905 if (b != VARIANT_TRUE)
907 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
908 IXMLDOMDocument3_Release(domdoc);
912 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
913 type = schema_type_from_xmlDocPtr(doc);
917 case SCHEMA_TYPE_XSD:
918 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
920 case SCHEMA_TYPE_XDR:
921 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
923 case SCHEMA_TYPE_INVALID:
925 FIXME("invalid schema\n");
928 IXMLDOMDocument3_Release(domdoc);
933 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
934 REFIID riid, void** ppvObject)
936 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
938 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
940 if ( IsEqualIID(riid, &IID_IUnknown) ||
941 IsEqualIID(riid, &IID_IDispatch) ||
942 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
943 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
949 FIXME("interface %s not implemented\n", debugstr_guid(riid));
950 return E_NOINTERFACE;
953 IXMLDOMSchemaCollection2_AddRef(iface);
958 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
960 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
961 LONG ref = InterlockedIncrement(&This->ref);
962 TRACE("%p new ref %d\n", This, ref);
966 static void cache_free(void* data, xmlChar* name /* ignored */)
968 cache_entry_release((cache_entry*)data);
971 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
973 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
974 LONG ref = InterlockedDecrement(&This->ref);
975 TRACE("%p new ref %d\n", This, ref);
979 xmlHashFree(This->cache, cache_free);
986 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
989 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
991 TRACE("(%p)->(%p)\n", This, pctinfo);
998 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
999 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1001 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1004 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1006 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1011 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1012 REFIID riid, LPOLESTR* rgszNames,
1013 UINT cNames, LCID lcid, DISPID* rgDispId)
1015 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1016 ITypeInfo* typeinfo;
1019 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1022 if(!rgszNames || cNames == 0 || !rgDispId)
1023 return E_INVALIDARG;
1025 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1028 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1029 ITypeInfo_Release(typeinfo);
1035 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1036 DISPID dispIdMember, REFIID riid, LCID lcid,
1037 WORD wFlags, DISPPARAMS* pDispParams,
1038 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1041 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1042 ITypeInfo* typeinfo;
1045 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1046 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1048 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1051 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1052 pVarResult, pExcepInfo, puArgErr);
1053 ITypeInfo_Release(typeinfo);
1059 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1061 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1062 xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1063 TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
1069 xmlHashRemoveEntry(This->cache, name, cache_free);
1075 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1079 cache_entry_add_ref(entry);
1087 xmlHashRemoveEntry(This->cache, name, cache_free);
1088 xmlHashAddEntry(This->cache, name, entry);
1094 xmlDocPtr doc = NULL;
1097 IXMLDOMNode* domnode = NULL;
1098 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1101 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1105 IXMLDOMNode_Release(domnode);
1107 return E_INVALIDARG;
1109 type = schema_type_from_xmlDocPtr(doc);
1111 if (type == SCHEMA_TYPE_XSD)
1113 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1115 else if (type == SCHEMA_TYPE_XDR)
1117 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1121 WARN("invalid schema!\n");
1125 IXMLDOMNode_Release(domnode);
1129 cache_entry_add_ref(entry);
1137 xmlHashRemoveEntry(This->cache, name, cache_free);
1138 xmlHashAddEntry(This->cache, name, entry);
1145 return E_INVALIDARG;
1152 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1155 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1158 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1163 name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1164 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1167 /* TODO: this should be read-only */
1169 return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1175 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1177 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1178 xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1179 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1181 xmlHashRemoveEntry(This->cache, name, cache_free);
1186 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1188 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1189 TRACE("(%p)->(%p)\n", This, length);
1193 *length = xmlHashSize(This->cache);
1197 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1199 cache_index_data* index_data = (cache_index_data*)index;
1201 if (index_data->index-- == 0)
1202 *index_data->out = bstr_from_xmlChar(name);
1205 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1206 LONG index, BSTR* len)
1208 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1209 cache_index_data data = {index,len};
1210 TRACE("(%p)->(%i, %p)\n", This, index, len);
1216 if (index >= xmlHashSize(This->cache))
1219 xmlHashScan(This->cache, cache_index, &data);
1223 static void cache_copy(void* data, void* dest, xmlChar* name)
1225 schema_cache* This = (schema_cache*) dest;
1226 cache_entry* entry = (cache_entry*) data;
1228 if (xmlHashLookup(This->cache, name) == NULL)
1230 cache_entry_add_ref(entry);
1231 xmlHashAddEntry(This->cache, name, entry);
1235 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1236 IXMLDOMSchemaCollection* otherCollection)
1238 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1239 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1240 TRACE("(%p)->(%p)\n", This, That);
1242 if (!otherCollection)
1245 /* TODO: detect errors while copying & return E_FAIL */
1246 xmlHashScan(That->cache, cache_copy, This);
1251 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1260 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1266 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1267 VARIANT_BOOL validateOnLoad)
1273 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1274 VARIANT_BOOL* validateOnLoad)
1280 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1281 BSTR namespaceURI, ISchema** schema)
1289 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1290 IXMLDOMNode* node, ISchemaItem** item)
1298 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1300 schema_cache_QueryInterface,
1301 schema_cache_AddRef,
1302 schema_cache_Release,
1303 schema_cache_GetTypeInfoCount,
1304 schema_cache_GetTypeInfo,
1305 schema_cache_GetIDsOfNames,
1306 schema_cache_Invoke,
1309 schema_cache_remove,
1310 schema_cache_get_length,
1311 schema_cache_get_namespaceURI,
1312 schema_cache_addCollection,
1313 schema_cache_get__newEnum,
1314 schema_cache_validate,
1315 schema_cache_put_validateOnLoad,
1316 schema_cache_get_validateOnLoad,
1317 schema_cache_getSchema,
1318 schema_cache_getDeclaration
1321 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1323 xmlSchemaElementPtr decl = NULL;
1324 xmlChar const* nsURI = get_node_nsURI(node);
1326 TRACE("(%p, %p)\n", schema, node);
1328 if (xmlStrEqual(nsURI, schema->targetNamespace))
1329 decl = xmlHashLookup(schema->elemDecl, node->name);
1331 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1333 FIXME("declaration not found in main schema - need to check schema imports!\n");
1334 /*xmlSchemaImportPtr import;
1336 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1338 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1341 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1347 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1349 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1350 while (decl != NULL && decl->refDecl != NULL)
1351 decl = decl->refDecl;
1352 return (decl != NULL)? decl->node : NULL;
1355 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1357 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1358 xmlSchemaPtr schema;
1360 TRACE("(%p, %p)\n", This, tree);
1365 if (tree->type == XML_DOCUMENT_NODE)
1366 tree = xmlDocGetRootElement(tree->doc);
1368 schema = get_node_schema(This, tree);
1369 /* TODO: if the ns is not in the cache, and it's a URL,
1370 * do we try to load from that? */
1372 return Schema_validate_tree(schema, tree);
1374 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1379 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1381 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1382 xmlSchemaPtr schema = get_node_schema(This, node);
1383 XDR_DT dt = DT_INVALID;
1385 TRACE("(%p, %p)\n", This, node);
1387 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1389 dt = str_to_dt(node->name, -1);
1394 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1396 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1399 dt = str_to_dt(str, -1);
1407 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1409 schema_cache* This = heap_alloc(sizeof(schema_cache));
1411 return E_OUTOFMEMORY;
1413 This->lpVtbl = &schema_cache_vtbl;
1414 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1417 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1418 This->version = MSXML3;
1419 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1420 This->version = MSXML4;
1421 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1422 This->version = MSXML6;
1424 This->version = MSXML_DEFAULT;
1426 *ppObj = &This->lpVtbl;
1432 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1434 MESSAGE("This program tried to use a SchemaCache object, but\n"
1435 "libxml2 support was not present at compile time.\n");