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: versioned constructor
42 * TODO: grow/shrink hashtable depending on load factor
43 * TODO: implement read-only where appropriate
46 /* This is just the number of buckets, should be prime */
47 #define DEFAULT_HASHTABLE_SIZE 17
51 #include <libxml/tree.h>
52 #include <libxml/xmlschemas.h>
53 #include <libxml/schemasInternals.h>
54 #include <libxml/hash.h>
55 #include <libxml/parser.h>
56 #include <libxml/parserInternals.h>
57 #include <libxml/xmlIO.h>
59 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
61 static const xmlChar XSD_schema[] = "schema";
62 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
63 static const xmlChar XDR_schema[] = "Schema";
64 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
65 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
67 static xmlChar const* datatypes_src = NULL;
68 static int datatypes_len = 0;
69 static HGLOBAL datatypes_handle = NULL;
70 static HRSRC datatypes_rsrc = NULL;
71 static xmlSchemaPtr datatypes_schema = NULL;
79 typedef enum _SCHEMA_TYPE {
85 typedef struct _schema_cache
87 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
88 MSXML_VERSION version;
89 xmlHashTablePtr cache;
93 typedef struct _cache_entry
101 typedef struct _cache_index_data
107 /* datatypes lookup stuff
108 * generated with help from gperf */
109 #define DT_MIN_STR_LEN 2
110 #define DT_MAX_STR_LEN 11
111 #define DT_MIN_HASH_VALUE 2
112 #define DT_MAX_HASH_VALUE 115
114 static const xmlChar DT_bin_base64[] = "bin.base64";
115 static const xmlChar DT_bin_hex[] = "bin.hex";
116 static const xmlChar DT_boolean[] = "boolean";
117 static const xmlChar DT_char[] = "char";
118 static const xmlChar DT_date[] = "date";
119 static const xmlChar DT_date_tz[] = "date.tz";
120 static const xmlChar DT_dateTime[] = "dateTime";
121 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
122 static const xmlChar DT_entity[] = "entity";
123 static const xmlChar DT_entities[] = "entities";
124 static const xmlChar DT_enumeration[] = "enumeration";
125 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
126 static const xmlChar DT_float[] = "float";
127 static const xmlChar DT_i1[] = "i1";
128 static const xmlChar DT_i2[] = "i2";
129 static const xmlChar DT_i4[] = "i4";
130 static const xmlChar DT_i8[] = "i8";
131 static const xmlChar DT_id[] = "id";
132 static const xmlChar DT_idref[] = "idref";
133 static const xmlChar DT_idrefs[] = "idrefs";
134 static const xmlChar DT_int[] = "int";
135 static const xmlChar DT_nmtoken[] = "nmtoken";
136 static const xmlChar DT_nmtokens[] = "nmtokens";
137 static const xmlChar DT_notation[] = "notation";
138 static const xmlChar DT_number[] = "number";
139 static const xmlChar DT_r4[] = "r4";
140 static const xmlChar DT_r8[] = "r8";
141 static const xmlChar DT_string[] = "string";
142 static const xmlChar DT_time[] = "time";
143 static const xmlChar DT_time_tz[] = "time.tz";
144 static const xmlChar DT_ui1[] = "ui1";
145 static const xmlChar DT_ui2[] = "ui2";
146 static const xmlChar DT_ui4[] = "ui4";
147 static const xmlChar DT_ui8[] = "ui8";
148 static const xmlChar DT_uri[] = "uri";
149 static const xmlChar DT_uuid[] = "uuid";
151 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
152 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
153 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
154 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
155 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
156 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
157 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
158 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
159 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
160 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
161 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
162 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
163 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
164 static const OLECHAR wDT_i1[] = {'i','1',0};
165 static const OLECHAR wDT_i2[] = {'i','2',0};
166 static const OLECHAR wDT_i4[] = {'i','4',0};
167 static const OLECHAR wDT_i8[] = {'i','8',0};
168 static const OLECHAR wDT_id[] = {'i','d',0};
169 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
170 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
171 static const OLECHAR wDT_int[] = {'i','n','t',0};
172 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
173 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
174 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
175 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
176 static const OLECHAR wDT_r4[] = {'r','4',0};
177 static const OLECHAR wDT_r8[] = {'r','8',0};
178 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
179 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
180 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
181 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
182 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
183 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
184 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
185 static const OLECHAR wDT_uri[] = {'u','r','i',0};
186 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
188 static const BYTE hash_assoc_values[] =
190 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
191 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
192 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
193 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
194 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
195 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
196 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
197 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
198 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
199 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
200 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
201 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
202 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
203 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
206 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
207 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
208 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
209 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
213 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
214 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116
218 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
222 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
226 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
230 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
234 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
235 static void parser_serror(void* ctx, xmlErrorPtr err)
237 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
241 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
243 TRACE("(%p)\n", spctx);
245 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
246 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
247 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
250 return xmlSchemaParse(spctx);
253 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
257 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
261 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
265 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
269 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
270 static void validate_serror(void* ctx, xmlErrorPtr err)
272 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
276 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
278 xmlSchemaValidCtxtPtr svctx;
281 TRACE("(%p, %p)\n", schema, tree);
282 /* TODO: if validateOnLoad property is false,
283 * we probably need to validate the schema here. */
284 svctx = xmlSchemaNewValidCtxt(schema);
285 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
286 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
287 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
290 if (tree->type == XML_DOCUMENT_NODE)
291 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
293 err = xmlSchemaValidateOneElement(svctx, tree);
295 xmlSchemaFreeValidCtxt(svctx);
296 return err? S_FALSE : S_OK;
299 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
301 DWORD hval = (len == -1)? xmlStrlen(str) : len;
306 hval += hash_assoc_values[str[10]];
309 hval += hash_assoc_values[str[9]];
312 hval += hash_assoc_values[str[8]];
315 hval += hash_assoc_values[str[7]];
318 hval += hash_assoc_values[str[6]];
321 hval += hash_assoc_values[str[5]];
324 hval += hash_assoc_values[str[4]];
327 hval += hash_assoc_values[str[3]];
330 hval += hash_assoc_values[str[2]];
333 hval += hash_assoc_values[str[1]];
336 hval += hash_assoc_values[str[0]];
342 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
344 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
349 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
352 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
355 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
358 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
361 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
364 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
367 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
370 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
373 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
376 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
379 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
385 static const xmlChar const* DT_string_table[DT__N_TYPES] =
425 static const WCHAR const* DT_wstring_table[DT__N_TYPES] =
465 static const XDR_DT DT_lookup_table[] =
518 -1, -1, -1, -1, -1, -1, -1, -1, -1,
519 -1, -1, -1, -1, -1, -1, -1, -1, -1,
520 -1, -1, -1, -1, -1, -1, -1, -1, -1,
521 -1, -1, -1, -1, -1, -1, -1, -1, -1,
522 -1, -1, -1, -1, -1, -1, -1, -1, -1,
523 -1, -1, -1, -1, -1, -1, -1, -1,
527 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
529 DWORD hash = dt_hash(str, len);
530 XDR_DT dt = DT_INVALID;
532 if (hash <= DT_MAX_HASH_VALUE)
533 dt = DT_lookup_table[hash];
535 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
541 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
543 DWORD hash = dt_hash_bstr(bstr, len);
544 XDR_DT dt = DT_INVALID;
546 if (hash <= DT_MAX_HASH_VALUE)
547 dt = DT_lookup_table[hash];
549 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
555 xmlChar const* dt_to_str(XDR_DT dt)
557 if (dt == DT_INVALID)
560 return DT_string_table[dt];
563 OLECHAR const* dt_to_bstr(XDR_DT dt)
565 if (dt == DT_INVALID)
568 return DT_wstring_table[dt];
571 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
578 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
580 if (!datatypes_schema)
582 xmlSchemaParserCtxtPtr spctx;
583 assert(datatypes_src != NULL);
584 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
585 datatypes_schema = Schema_parse(spctx);
586 xmlSchemaFreeParserCtxt(spctx);
622 assert(datatypes_schema != NULL);
623 if (content && xmlStrlen(content))
625 tmp_doc = xmlNewDoc(NULL);
626 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
627 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
629 xmlDocSetRootElement(tmp_doc, node);
631 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
635 { /* probably the node is being created manually and has no content yet */
640 FIXME("need to handle dt:%s\n", dt_to_str(dt));
645 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
647 return (node->ns != NULL)? node->ns->href : NULL;
650 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
652 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
653 xmlHashLookup(This->cache, nsURI);
656 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
658 cache_entry* entry = get_entry(This, get_node_nsURI(node));
659 return (!entry)? NULL : entry->schema;
662 xmlExternalEntityLoader _external_entity_loader = NULL;
664 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
665 xmlParserCtxtPtr ctxt)
667 xmlParserInputPtr input;
669 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
671 assert(MSXML_hInstance != NULL);
672 assert(datatypes_rsrc != NULL);
673 assert(datatypes_handle != NULL);
674 assert(datatypes_src != NULL);
676 /* TODO: if the desired schema is in the cache, load it from there */
677 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
679 TRACE("loading built-in schema for %s\n", URL);
680 input = xmlNewStringInputStream(ctxt, datatypes_src);
684 input = _external_entity_loader(URL, ID, ctxt);
690 void schemasInit(void)
694 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
696 FIXME("failed to find resource for %s\n", DT_nsURI);
700 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
702 FIXME("failed to load resource for %s\n", DT_nsURI);
705 buf = LockResource(datatypes_handle);
706 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
708 /* Resource is loaded as raw data,
709 * need a null-terminated string */
710 while (buf[len] != '>')
712 datatypes_src = BAD_CAST buf;
713 datatypes_len = len + 1;
715 if ((void*)xmlGetExternalEntityLoader() != (void*)external_entity_loader)
717 _external_entity_loader = xmlGetExternalEntityLoader();
718 xmlSetExternalEntityLoader(external_entity_loader);
722 void schemasCleanup(void)
724 if (datatypes_handle)
725 FreeResource(datatypes_handle);
726 if (datatypes_schema)
727 xmlSchemaFree(datatypes_schema);
728 xmlSetExternalEntityLoader(_external_entity_loader);
731 static LONG cache_entry_add_ref(cache_entry* entry)
733 LONG ref = InterlockedIncrement(&entry->ref);
734 TRACE("%p new ref %d\n", entry, ref);
738 static LONG cache_entry_release(cache_entry* entry)
740 LONG ref = InterlockedDecrement(&entry->ref);
741 TRACE("%p new ref %d\n", entry, ref);
745 if (entry->type == SCHEMA_TYPE_XSD)
747 xmldoc_release(entry->doc);
748 entry->schema->doc = NULL;
749 xmlSchemaFree(entry->schema);
752 else /* SCHEMA_TYPE_XDR */
754 xmldoc_release(entry->doc);
755 xmldoc_release(entry->schema->doc);
756 entry->schema->doc = NULL;
757 xmlSchemaFree(entry->schema);
764 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
766 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
769 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
773 root = xmlDocGetRootElement(schema);
774 if (root && root->ns)
777 if (xmlStrEqual(root->name, XDR_schema) &&
778 xmlStrEqual(root->ns->href, XDR_nsURI))
780 return SCHEMA_TYPE_XDR;
782 else if (xmlStrEqual(root->name, XSD_schema) &&
783 xmlStrEqual(root->ns->href, XSD_nsURI))
785 return SCHEMA_TYPE_XSD;
788 return SCHEMA_TYPE_INVALID;
791 static BOOL link_datatypes(xmlDocPtr schema)
793 xmlNodePtr root, next, child;
796 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
797 root = xmlDocGetRootElement(schema);
801 for (ns = root->nsDef; ns != NULL; ns = ns->next)
803 if (xmlStrEqual(ns->href, DT_nsURI))
810 next = xmlFirstElementChild(root);
811 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
812 if (next) child = xmlAddPrevSibling(next, child);
813 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
814 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
819 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
821 cache_entry* entry = heap_alloc(sizeof(cache_entry));
822 xmlSchemaParserCtxtPtr spctx;
823 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
825 link_datatypes(new_doc);
827 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
828 * do we need to do something special here? */
829 entry->type = SCHEMA_TYPE_XSD;
831 spctx = xmlSchemaNewDocParserCtxt(new_doc);
833 if ((entry->schema = Schema_parse(spctx)))
835 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
836 entry->doc = entry->schema->doc;
837 xmldoc_add_ref(entry->doc);
841 FIXME("failed to parse doc\n");
846 xmlSchemaFreeParserCtxt(spctx);
850 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
852 cache_entry* entry = heap_alloc(sizeof(cache_entry));
853 xmlSchemaParserCtxtPtr spctx;
854 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
856 link_datatypes(xsd_doc);
858 entry->type = SCHEMA_TYPE_XDR;
860 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
862 if ((entry->schema = Schema_parse(spctx)))
864 entry->doc = new_doc;
865 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
866 xmldoc_init(entry->doc, DOMDocument_version(v));
867 xmldoc_add_ref(entry->doc);
868 xmldoc_add_ref(entry->schema->doc);
872 FIXME("failed to parse doc\n");
878 xmlSchemaFreeParserCtxt(spctx);
883 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
886 IXMLDOMDocument3* domdoc = NULL;
887 xmlDocPtr doc = NULL;
888 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
889 VARIANT_BOOL b = VARIANT_FALSE;
890 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
894 FIXME("failed to create domdoc\n");
897 assert(domdoc != NULL);
898 assert(V_VT(&url) == VT_BSTR);
900 hr = IXMLDOMDocument3_load(domdoc, url, &b);
903 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
904 if (b != VARIANT_TRUE)
906 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
907 IXMLDOMDocument3_Release(domdoc);
911 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
912 type = schema_type_from_xmlDocPtr(doc);
916 case SCHEMA_TYPE_XSD:
917 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
919 case SCHEMA_TYPE_XDR:
920 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
922 case SCHEMA_TYPE_INVALID:
924 FIXME("invalid schema\n");
927 IXMLDOMDocument3_Release(domdoc);
932 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
933 REFIID riid, void** ppvObject)
935 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
937 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
939 if ( IsEqualIID(riid, &IID_IUnknown) ||
940 IsEqualIID(riid, &IID_IDispatch) ||
941 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
942 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
948 FIXME("interface %s not implemented\n", debugstr_guid(riid));
949 return E_NOINTERFACE;
952 IXMLDOMSchemaCollection2_AddRef(iface);
957 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
959 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
960 LONG ref = InterlockedIncrement(&This->ref);
961 TRACE("%p new ref %d\n", This, ref);
965 static void cache_free(void* data, xmlChar* name /* ignored */)
967 cache_entry_release((cache_entry*)data);
970 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
972 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
973 LONG ref = InterlockedDecrement(&This->ref);
974 TRACE("%p new ref %d\n", This, ref);
978 xmlHashFree(This->cache, cache_free);
985 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
988 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
990 TRACE("(%p)->(%p)\n", This, pctinfo);
997 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
998 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1000 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1003 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1005 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1010 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1011 REFIID riid, LPOLESTR* rgszNames,
1012 UINT cNames, LCID lcid, DISPID* rgDispId)
1014 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1015 ITypeInfo* typeinfo;
1018 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1021 if(!rgszNames || cNames == 0 || !rgDispId)
1022 return E_INVALIDARG;
1024 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1027 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1028 ITypeInfo_Release(typeinfo);
1034 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1035 DISPID dispIdMember, REFIID riid, LCID lcid,
1036 WORD wFlags, DISPPARAMS* pDispParams,
1037 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1040 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1041 ITypeInfo* typeinfo;
1044 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1045 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1047 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1050 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1051 pVarResult, pExcepInfo, puArgErr);
1052 ITypeInfo_Release(typeinfo);
1058 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1060 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1061 xmlChar* name = xmlChar_from_wchar(uri);
1062 TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
1068 xmlHashRemoveEntry(This->cache, name, cache_free);
1074 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1078 cache_entry_add_ref(entry);
1086 xmlHashRemoveEntry(This->cache, name, cache_free);
1087 xmlHashAddEntry(This->cache, name, entry);
1093 xmlDocPtr doc = NULL;
1096 IXMLDOMNode* domnode = NULL;
1097 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1100 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1104 IXMLDOMNode_Release(domnode);
1106 return E_INVALIDARG;
1108 type = schema_type_from_xmlDocPtr(doc);
1110 if (type == SCHEMA_TYPE_XSD)
1112 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1114 else if (type == SCHEMA_TYPE_XDR)
1116 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1120 WARN("invalid schema!\n");
1124 IXMLDOMNode_Release(domnode);
1128 cache_entry_add_ref(entry);
1136 xmlHashRemoveEntry(This->cache, name, cache_free);
1137 xmlHashAddEntry(This->cache, name, entry);
1144 return E_INVALIDARG;
1151 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1154 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1157 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1162 name = xmlChar_from_wchar(uri);
1163 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1166 /* TODO: this should be read-only */
1168 return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1174 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1176 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1177 xmlChar* name = xmlChar_from_wchar(uri);
1178 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1180 xmlHashRemoveEntry(This->cache, name, cache_free);
1185 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1187 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1188 TRACE("(%p)->(%p)\n", This, length);
1192 *length = xmlHashSize(This->cache);
1196 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1198 cache_index_data* index_data = (cache_index_data*)index;
1200 if (index_data->index-- == 0)
1201 *index_data->out = bstr_from_xmlChar(name);
1204 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1205 LONG index, BSTR* len)
1207 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1208 cache_index_data data = {index,len};
1209 TRACE("(%p)->(%i, %p)\n", This, index, len);
1215 if (index >= xmlHashSize(This->cache))
1218 xmlHashScan(This->cache, cache_index, &data);
1222 static void cache_copy(void* data, void* dest, xmlChar* name)
1224 schema_cache* This = (schema_cache*) dest;
1225 cache_entry* entry = (cache_entry*) data;
1227 if (xmlHashLookup(This->cache, name) == NULL)
1229 cache_entry_add_ref(entry);
1230 xmlHashAddEntry(This->cache, name, entry);
1234 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1235 IXMLDOMSchemaCollection* otherCollection)
1237 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1238 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1239 TRACE("(%p)->(%p)\n", This, That);
1241 if (!otherCollection)
1244 /* TODO: detect errors while copying & return E_FAIL */
1245 xmlHashScan(That->cache, cache_copy, This);
1250 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1259 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1265 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1266 VARIANT_BOOL validateOnLoad)
1272 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1273 VARIANT_BOOL* validateOnLoad)
1279 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1280 BSTR namespaceURI, ISchema** schema)
1288 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1289 IXMLDOMNode* node, ISchemaItem** item)
1297 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1299 schema_cache_QueryInterface,
1300 schema_cache_AddRef,
1301 schema_cache_Release,
1302 schema_cache_GetTypeInfoCount,
1303 schema_cache_GetTypeInfo,
1304 schema_cache_GetIDsOfNames,
1305 schema_cache_Invoke,
1308 schema_cache_remove,
1309 schema_cache_get_length,
1310 schema_cache_get_namespaceURI,
1311 schema_cache_addCollection,
1312 schema_cache_get__newEnum,
1313 schema_cache_validate,
1314 schema_cache_put_validateOnLoad,
1315 schema_cache_get_validateOnLoad,
1316 schema_cache_getSchema,
1317 schema_cache_getDeclaration
1320 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1322 xmlSchemaElementPtr decl = NULL;
1323 xmlChar const* nsURI = get_node_nsURI(node);
1325 TRACE("(%p, %p)\n", schema, node);
1327 if (xmlStrEqual(nsURI, schema->targetNamespace))
1328 decl = xmlHashLookup(schema->elemDecl, node->name);
1330 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1332 FIXME("declaration not found in main schema - need to check schema imports!\n");
1333 /*xmlSchemaImportPtr import;
1335 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1337 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1340 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1346 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1348 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1349 while (decl != NULL && decl->refDecl != NULL)
1350 decl = decl->refDecl;
1351 return (decl != NULL)? decl->node : NULL;
1354 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1356 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1357 xmlSchemaPtr schema;
1359 TRACE("(%p, %p)\n", This, tree);
1364 if (tree->type == XML_DOCUMENT_NODE)
1365 tree = xmlDocGetRootElement(tree->doc);
1367 schema = get_node_schema(This, tree);
1368 /* TODO: if the ns is not in the cache, and it's a URL,
1369 * do we try to load from that? */
1371 return Schema_validate_tree(schema, tree);
1373 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1378 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1380 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1381 xmlSchemaPtr schema = get_node_schema(This, node);
1382 XDR_DT dt = DT_INVALID;
1384 TRACE("(%p, %p)\n", This, node);
1386 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1388 dt = str_to_dt(node->name, -1);
1393 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1395 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1398 dt = str_to_dt(str, -1);
1406 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1408 schema_cache* This = heap_alloc(sizeof(schema_cache));
1410 return E_OUTOFMEMORY;
1412 This->lpVtbl = &schema_cache_vtbl;
1413 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1416 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1417 This->version = MSXML3;
1418 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1419 This->version = MSXML4;
1420 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1421 This->version = MSXML6;
1423 This->version = MSXML_DEFAULT;
1425 *ppObj = &This->lpVtbl;
1431 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1433 MESSAGE("This program tried to use a SchemaCache object, but\n"
1434 "libxml2 support was not present at compile time.\n");