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/xmlerror.h>
30 # include <libxml/tree.h>
31 # include <libxml/xmlschemas.h>
32 # include <libxml/schemasInternals.h>
33 # include <libxml/hash.h>
34 # include <libxml/parser.h>
35 # include <libxml/parserInternals.h>
36 # include <libxml/xmlIO.h>
37 # include <libxml/xmlversion.h>
38 # include <libxml/xpath.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 * CacheType_NS is a special type used for read-only collection build with
86 * IXMLDOMDocument2::namespaces()
89 CacheEntryType_Invalid,
98 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface;
101 MSXML_VERSION version;
102 xmlHashTablePtr cache;
104 VARIANT_BOOL validateOnLoad;
122 /* datatypes lookup stuff
123 * generated with help from gperf */
124 #define DT_MIN_STR_LEN 2
125 #define DT_MAX_STR_LEN 11
126 #define DT_MIN_HASH_VALUE 2
127 #define DT_MAX_HASH_VALUE 115
129 static const xmlChar DT_bin_base64[] = "bin.base64";
130 static const xmlChar DT_bin_hex[] = "bin.hex";
131 static const xmlChar DT_boolean[] = "boolean";
132 static const xmlChar DT_char[] = "char";
133 static const xmlChar DT_date[] = "date";
134 static const xmlChar DT_date_tz[] = "date.tz";
135 static const xmlChar DT_dateTime[] = "dateTime";
136 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
137 static const xmlChar DT_entity[] = "entity";
138 static const xmlChar DT_entities[] = "entities";
139 static const xmlChar DT_enumeration[] = "enumeration";
140 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
141 static const xmlChar DT_float[] = "float";
142 static const xmlChar DT_i1[] = "i1";
143 static const xmlChar DT_i2[] = "i2";
144 static const xmlChar DT_i4[] = "i4";
145 static const xmlChar DT_i8[] = "i8";
146 static const xmlChar DT_id[] = "id";
147 static const xmlChar DT_idref[] = "idref";
148 static const xmlChar DT_idrefs[] = "idrefs";
149 static const xmlChar DT_int[] = "int";
150 static const xmlChar DT_nmtoken[] = "nmtoken";
151 static const xmlChar DT_nmtokens[] = "nmtokens";
152 static const xmlChar DT_notation[] = "notation";
153 static const xmlChar DT_number[] = "number";
154 static const xmlChar DT_r4[] = "r4";
155 static const xmlChar DT_r8[] = "r8";
156 static const xmlChar DT_string[] = "string";
157 static const xmlChar DT_time[] = "time";
158 static const xmlChar DT_time_tz[] = "time.tz";
159 static const xmlChar DT_ui1[] = "ui1";
160 static const xmlChar DT_ui2[] = "ui2";
161 static const xmlChar DT_ui4[] = "ui4";
162 static const xmlChar DT_ui8[] = "ui8";
163 static const xmlChar DT_uri[] = "uri";
164 static const xmlChar DT_uuid[] = "uuid";
166 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
167 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
168 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
169 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
170 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
171 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
172 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
173 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
174 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
175 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
176 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
177 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
178 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
179 static const OLECHAR wDT_i1[] = {'i','1',0};
180 static const OLECHAR wDT_i2[] = {'i','2',0};
181 static const OLECHAR wDT_i4[] = {'i','4',0};
182 static const OLECHAR wDT_i8[] = {'i','8',0};
183 static const OLECHAR wDT_id[] = {'i','d',0};
184 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
185 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
186 static const OLECHAR wDT_int[] = {'i','n','t',0};
187 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
188 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
189 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
190 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
191 static const OLECHAR wDT_r4[] = {'r','4',0};
192 static const OLECHAR wDT_r8[] = {'r','8',0};
193 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
194 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
195 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
196 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
197 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
198 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
199 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
200 static const OLECHAR wDT_uri[] = {'u','r','i',0};
201 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
203 static const BYTE hash_assoc_values[] =
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, 10, 116, 116, 55,
210 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
211 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
212 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
213 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
214 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
215 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
216 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
217 0, 0, 10, 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, 116, 116, 116, 116,
222 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
223 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
224 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
225 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
226 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
227 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
228 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
229 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
230 116, 116, 116, 116, 116, 116
233 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
237 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
241 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
245 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
249 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
250 static void parser_serror(void* ctx, xmlErrorPtr err)
252 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
256 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
258 TRACE("(%p)\n", spctx);
260 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
261 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
262 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
265 return xmlSchemaParse(spctx);
268 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
272 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
276 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
280 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
284 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
285 static void validate_serror(void* ctx, xmlErrorPtr err)
287 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
291 static HRESULT schema_cache_get_item(IUnknown *iface, LONG index, VARIANT *item)
293 V_VT(item) = VT_BSTR;
294 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2*)iface, index, &V_BSTR(item));
297 static const struct enumvariant_funcs schemacache_enumvariant = {
298 schema_cache_get_item,
302 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
304 xmlSchemaValidCtxtPtr svctx;
307 TRACE("(%p, %p)\n", schema, tree);
308 /* TODO: if validateOnLoad property is false,
309 * we probably need to validate the schema here. */
310 svctx = xmlSchemaNewValidCtxt(schema);
311 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
312 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
313 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
316 if (tree->type == XML_DOCUMENT_NODE)
317 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
319 err = xmlSchemaValidateOneElement(svctx, tree);
321 xmlSchemaFreeValidCtxt(svctx);
322 return err? S_FALSE : S_OK;
325 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
327 DWORD hval = (len == -1)? xmlStrlen(str) : len;
332 hval += hash_assoc_values[str[10]];
335 hval += hash_assoc_values[str[9]];
338 hval += hash_assoc_values[str[8]];
341 hval += hash_assoc_values[str[7]];
344 hval += hash_assoc_values[str[6]];
347 hval += hash_assoc_values[str[5]];
350 hval += hash_assoc_values[str[4]];
353 hval += hash_assoc_values[str[3]];
356 hval += hash_assoc_values[str[2]];
359 hval += hash_assoc_values[str[1]];
362 hval += hash_assoc_values[str[0]];
368 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
370 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
375 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
378 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
381 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
384 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
387 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
390 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
393 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
396 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
399 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
402 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
405 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
411 static const xmlChar *const DT_string_table[LAST_DT] =
451 static const WCHAR *const DT_wstring_table[LAST_DT] =
491 static const XDR_DT DT_lookup_table[] =
544 -1, -1, -1, -1, -1, -1, -1, -1, -1,
545 -1, -1, -1, -1, -1, -1, -1, -1, -1,
546 -1, -1, -1, -1, -1, -1, -1, -1, -1,
547 -1, -1, -1, -1, -1, -1, -1, -1, -1,
548 -1, -1, -1, -1, -1, -1, -1, -1, -1,
549 -1, -1, -1, -1, -1, -1, -1, -1,
553 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
555 DWORD hash = dt_hash(str, len);
556 XDR_DT dt = DT_INVALID;
558 if (hash <= DT_MAX_HASH_VALUE)
559 dt = DT_lookup_table[hash];
561 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
567 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
569 DWORD hash = dt_hash_bstr(bstr, len);
570 XDR_DT dt = DT_INVALID;
572 if (hash <= DT_MAX_HASH_VALUE)
573 dt = DT_lookup_table[hash];
575 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
581 xmlChar const* dt_to_str(XDR_DT dt)
583 if (dt == DT_INVALID)
586 return DT_string_table[dt];
589 OLECHAR const* dt_to_bstr(XDR_DT dt)
591 if (dt == DT_INVALID)
594 return DT_wstring_table[dt];
597 const char* debugstr_dt(XDR_DT dt)
599 return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL);
602 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
609 TRACE("(dt:%s, %s)\n", debugstr_dt(dt), debugstr_a((char const*)content));
611 if (!datatypes_schema)
613 xmlSchemaParserCtxtPtr spctx;
614 assert(datatypes_src != NULL);
615 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
616 datatypes_schema = Schema_parse(spctx);
617 xmlSchemaFreeParserCtxt(spctx);
653 if (!datatypes_schema)
655 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
656 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n");
658 /* Hopefully they don't need much in the way of XDR datatypes support... */
662 if (content && xmlStrlen(content))
664 tmp_doc = xmlNewDoc(NULL);
665 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
666 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
668 xmlDocSetRootElement(tmp_doc, node);
670 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
674 { /* probably the node is being created manually and has no content yet */
679 FIXME("need to handle dt:%s\n", debugstr_dt(dt));
684 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
686 return (node->ns != NULL)? node->ns->href : NULL;
689 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
691 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
692 xmlHashLookup(This->cache, nsURI);
695 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
697 cache_entry* entry = get_entry(This, get_node_nsURI(node));
698 return (!entry)? NULL : entry->schema;
701 static xmlExternalEntityLoader _external_entity_loader;
703 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
704 xmlParserCtxtPtr ctxt)
706 xmlParserInputPtr input;
708 TRACE("(%s %s %p)\n", debugstr_a(URL), debugstr_a(ID), ctxt);
710 assert(MSXML_hInstance != NULL);
711 assert(datatypes_rsrc != NULL);
712 assert(datatypes_handle != NULL);
713 assert(datatypes_src != NULL);
715 /* TODO: if the desired schema is in the cache, load it from there */
716 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
718 TRACE("loading built-in schema for %s\n", URL);
719 input = xmlNewStringInputStream(ctxt, datatypes_src);
723 input = _external_entity_loader(URL, ID, ctxt);
729 void schemasInit(void)
733 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
735 FIXME("failed to find resource for %s\n", DT_nsURI);
739 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
741 FIXME("failed to load resource for %s\n", DT_nsURI);
744 buf = LockResource(datatypes_handle);
745 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
747 /* Resource is loaded as raw data,
748 * need a null-terminated string */
749 while (buf[len] != '>')
752 datatypes_len = len + 1;
754 if (xmlGetExternalEntityLoader() != external_entity_loader)
756 _external_entity_loader = xmlGetExternalEntityLoader();
757 xmlSetExternalEntityLoader(external_entity_loader);
761 void schemasCleanup(void)
763 xmlSchemaFree(datatypes_schema);
764 xmlSetExternalEntityLoader(_external_entity_loader);
767 static LONG cache_entry_add_ref(cache_entry* entry)
769 LONG ref = InterlockedIncrement(&entry->ref);
770 TRACE("(%p)->(%d)\n", entry, ref);
774 static LONG cache_entry_release(cache_entry* entry)
776 LONG ref = InterlockedDecrement(&entry->ref);
777 TRACE("(%p)->(%d)\n", entry, ref);
781 if (entry->type == CacheEntryType_XSD)
783 xmldoc_release(entry->doc);
784 entry->schema->doc = NULL;
785 xmlSchemaFree(entry->schema);
787 else if (entry->type == CacheEntryType_XDR)
789 xmldoc_release(entry->doc);
790 xmldoc_release(entry->schema->doc);
791 entry->schema->doc = NULL;
792 xmlSchemaFree(entry->schema);
800 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl;
802 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
804 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
807 static inline schema_cache* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection* iface)
809 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
812 static inline schema_cache* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
814 return iface->lpVtbl == (void*)&XMLDOMSchemaCollection2Vtbl ? impl_from_IXMLDOMSchemaCollection(iface) : NULL;
817 static inline CacheEntryType cache_type_from_xmlDocPtr(xmlDocPtr schema)
819 xmlNodePtr root = NULL;
821 root = xmlDocGetRootElement(schema);
822 if (root && root->ns)
825 if (xmlStrEqual(root->name, XDR_schema) &&
826 xmlStrEqual(root->ns->href, XDR_nsURI))
828 return CacheEntryType_XDR;
830 else if (xmlStrEqual(root->name, XSD_schema) &&
831 xmlStrEqual(root->ns->href, XSD_nsURI))
833 return CacheEntryType_XSD;
836 return CacheEntryType_Invalid;
839 static BOOL link_datatypes(xmlDocPtr schema)
841 xmlNodePtr root, next, child;
844 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
845 root = xmlDocGetRootElement(schema);
849 for (ns = root->nsDef; ns != NULL; ns = ns->next)
851 if (xmlStrEqual(ns->href, DT_nsURI))
858 next = xmlFirstElementChild(root);
859 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
860 if (next) child = xmlAddPrevSibling(next, child);
861 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
862 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
867 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
869 cache_entry* entry = heap_alloc(sizeof(cache_entry));
870 xmlSchemaParserCtxtPtr spctx;
871 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
873 link_datatypes(new_doc);
875 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
876 * do we need to do something special here? */
877 entry->type = CacheEntryType_XSD;
879 spctx = xmlSchemaNewDocParserCtxt(new_doc);
881 if ((entry->schema = Schema_parse(spctx)))
883 xmldoc_init(entry->schema->doc, v);
884 entry->doc = entry->schema->doc;
885 xmldoc_add_ref(entry->doc);
889 FIXME("failed to parse doc\n");
894 xmlSchemaFreeParserCtxt(spctx);
898 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version)
900 cache_entry* entry = heap_alloc(sizeof(cache_entry));
901 xmlSchemaParserCtxtPtr spctx;
902 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
904 link_datatypes(xsd_doc);
906 entry->type = CacheEntryType_XDR;
908 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
910 if ((entry->schema = Schema_parse(spctx)))
912 entry->doc = new_doc;
913 xmldoc_init(entry->schema->doc, version);
914 xmldoc_init(entry->doc, version);
915 xmldoc_add_ref(entry->doc);
916 xmldoc_add_ref(entry->schema->doc);
920 FIXME("failed to parse doc\n");
926 xmlSchemaFreeParserCtxt(spctx);
931 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version)
934 IXMLDOMDocument3* domdoc = NULL;
935 xmlDocPtr doc = NULL;
936 HRESULT hr = DOMDocument_create(version, NULL, (void**)&domdoc);
937 VARIANT_BOOL b = VARIANT_FALSE;
938 CacheEntryType type = CacheEntryType_Invalid;
942 FIXME("failed to create domdoc\n");
945 assert(domdoc != NULL);
946 assert(V_VT(&url) == VT_BSTR);
948 hr = IXMLDOMDocument3_load(domdoc, url, &b);
951 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
952 if (b != VARIANT_TRUE)
954 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url)));
955 IXMLDOMDocument3_Release(domdoc);
959 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
960 type = cache_type_from_xmlDocPtr(doc);
964 case CacheEntryType_XSD:
965 entry = cache_entry_from_xsd_doc(doc, nsURI, version);
967 case CacheEntryType_XDR:
968 entry = cache_entry_from_xdr_doc(doc, nsURI, version);
972 FIXME("invalid schema\n");
975 IXMLDOMDocument3_Release(domdoc);
980 static void cache_free(void* data, xmlChar* name /* ignored */)
982 cache_entry_release((cache_entry*)data);
985 /* This one adds all namespaces defined in document to a cache, without anything
986 associated with uri obviously.
987 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
988 it uses additional node type to describe namespace definition attribute while
989 in msxml it's expected to be a normal attribute - as a workaround document is
990 queried at libxml2 level here. */
991 HRESULT cache_from_doc_ns(IXMLDOMSchemaCollection2 *iface, xmlnode *node)
993 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
994 static const xmlChar query[] = "//*/namespace::*";
995 xmlXPathObjectPtr nodeset;
996 xmlXPathContextPtr ctxt;
1000 ctxt = xmlXPathNewContext(node->node->doc);
1002 nodeset = xmlXPathEvalExpression(query, ctxt);
1003 xmlXPathFreeContext(ctxt);
1007 int pos = 0, len = xmlXPathNodeSetGetLength(nodeset->nodesetval);
1009 if (len == 0) return S_OK;
1013 xmlNodePtr node = xmlXPathNodeSetItem(nodeset->nodesetval, pos);
1014 if (node->type == XML_NAMESPACE_DECL)
1016 static const xmlChar defns[] = "http://www.w3.org/XML/1998/namespace";
1017 xmlNsPtr ns = (xmlNsPtr)node;
1020 /* filter out default uri */
1021 if (xmlStrEqual(ns->href, defns))
1027 entry = heap_alloc(sizeof(cache_entry));
1028 entry->type = CacheEntryType_NS;
1030 entry->schema = NULL;
1033 xmlHashRemoveEntry(This->cache, ns->href, cache_free);
1034 xmlHashAddEntry(This->cache, ns->href, entry);
1039 xmlXPathFreeObject(nodeset);
1045 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
1046 REFIID riid, void** ppvObject)
1048 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1050 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1052 if ( IsEqualIID(riid, &IID_IUnknown) ||
1053 IsEqualIID(riid, &IID_IDispatch) ||
1054 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
1055 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
1059 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
1061 return *ppvObject ? S_OK : E_NOINTERFACE;
1065 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1067 return E_NOINTERFACE;
1070 IXMLDOMSchemaCollection2_AddRef(iface);
1075 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
1077 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1078 LONG ref = InterlockedIncrement(&This->ref);
1079 TRACE("(%p)->(%d)\n", This, ref);
1083 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
1085 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1086 LONG ref = InterlockedDecrement(&This->ref);
1087 TRACE("(%p)->(%d)\n", This, ref);
1091 xmlHashFree(This->cache, cache_free);
1092 release_dispex(&This->dispex);
1099 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1102 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1103 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1106 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1107 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1109 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1110 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
1111 iTInfo, lcid, ppTInfo);
1114 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1115 REFIID riid, LPOLESTR* rgszNames,
1116 UINT cNames, LCID lcid, DISPID* rgDispId)
1118 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1119 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1120 riid, rgszNames, cNames, lcid, rgDispId);
1123 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1124 DISPID dispIdMember, REFIID riid, LCID lcid,
1125 WORD wFlags, DISPPARAMS* pDispParams,
1126 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1129 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1130 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1131 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1134 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1136 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1137 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1138 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1140 if (This->read_only) return E_FAIL;
1146 xmlHashRemoveEntry(This->cache, name, cache_free);
1152 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1156 cache_entry_add_ref(entry);
1164 xmlHashRemoveEntry(This->cache, name, cache_free);
1165 xmlHashAddEntry(This->cache, name, entry);
1171 xmlDocPtr doc = NULL;
1173 CacheEntryType type;
1174 IXMLDOMNode* domnode = NULL;
1175 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1178 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1182 IXMLDOMNode_Release(domnode);
1184 return E_INVALIDARG;
1186 type = cache_type_from_xmlDocPtr(doc);
1188 if (type == CacheEntryType_XSD)
1190 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1192 else if (type == CacheEntryType_XDR)
1194 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1198 WARN("invalid schema!\n");
1202 IXMLDOMNode_Release(domnode);
1206 cache_entry_add_ref(entry);
1214 xmlHashRemoveEntry(This->cache, name, cache_free);
1215 xmlHashAddEntry(This->cache, name, entry);
1222 return E_INVALIDARG;
1229 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1232 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1236 TRACE("(%p)->(%s %p)\n", This, debugstr_w(uri), node);
1238 if (This->version == MSXML6) return E_NOTIMPL;
1243 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1244 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1247 /* TODO: this should be read-only */
1248 if (entry && entry->doc)
1249 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1255 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1257 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1258 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1259 TRACE("(%p)->(%s)\n", This, debugstr_w(uri));
1261 if (This->version == MSXML6) return E_NOTIMPL;
1263 xmlHashRemoveEntry(This->cache, name, cache_free);
1268 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1270 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1271 TRACE("(%p)->(%p)\n", This, length);
1275 *length = xmlHashSize(This->cache);
1279 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1281 cache_index_data* index_data = (cache_index_data*)index;
1283 if (index_data->index-- == 0)
1284 *index_data->out = bstr_from_xmlChar(name);
1287 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1288 LONG index, BSTR* len)
1290 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1291 cache_index_data data = {index, len};
1292 TRACE("(%p)->(%i %p)\n", This, index, len);
1297 if (index >= xmlHashSize(This->cache))
1301 xmlHashScan(This->cache, cache_index, &data);
1305 static void cache_copy(void* data, void* dest, xmlChar* name)
1307 schema_cache* This = (schema_cache*) dest;
1308 cache_entry* entry = (cache_entry*) data;
1310 if (xmlHashLookup(This->cache, name) == NULL)
1312 cache_entry_add_ref(entry);
1313 xmlHashAddEntry(This->cache, name, entry);
1317 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1318 IXMLDOMSchemaCollection* collection)
1320 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1323 TRACE("(%p)->(%p)\n", This, collection);
1328 That = unsafe_impl_from_IXMLDOMSchemaCollection(collection);
1331 ERR("external collection implementation\n");
1335 /* TODO: detect errors while copying & return E_FAIL */
1336 xmlHashScan(That->cache, cache_copy, This);
1341 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface, IUnknown** enumv)
1343 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1344 TRACE("(%p)->(%p)\n", This, enumv);
1345 return create_enumvariant((IUnknown*)iface, TRUE, &schemacache_enumvariant, (IEnumVARIANT**)enumv);
1348 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1350 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1351 FIXME("(%p): stub\n", This);
1355 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1358 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1359 FIXME("(%p)->(%d): stub\n", This, value);
1361 This->validateOnLoad = value;
1362 /* it's ok to disable it, cause we don't validate on load anyway */
1363 if (value == VARIANT_FALSE) return S_OK;
1368 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1369 VARIANT_BOOL* value)
1371 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1372 TRACE("(%p)->(%p)\n", This, value);
1374 if (!value) return E_POINTER;
1375 *value = This->validateOnLoad;
1380 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1381 BSTR namespaceURI, ISchema** schema)
1383 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1384 FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(namespaceURI), schema);
1390 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1391 IXMLDOMNode* node, ISchemaItem** item)
1393 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1394 FIXME("(%p)->(%p %p): stub\n", This, node, item);
1400 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl =
1402 schema_cache_QueryInterface,
1403 schema_cache_AddRef,
1404 schema_cache_Release,
1405 schema_cache_GetTypeInfoCount,
1406 schema_cache_GetTypeInfo,
1407 schema_cache_GetIDsOfNames,
1408 schema_cache_Invoke,
1411 schema_cache_remove,
1412 schema_cache_get_length,
1413 schema_cache_get_namespaceURI,
1414 schema_cache_addCollection,
1415 schema_cache_get__newEnum,
1416 schema_cache_validate,
1417 schema_cache_put_validateOnLoad,
1418 schema_cache_get_validateOnLoad,
1419 schema_cache_getSchema,
1420 schema_cache_getDeclaration
1423 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1425 xmlSchemaElementPtr decl = NULL;
1426 xmlChar const* nsURI = get_node_nsURI(node);
1428 TRACE("(%p, %p)\n", schema, node);
1430 if (xmlStrEqual(nsURI, schema->targetNamespace))
1431 decl = xmlHashLookup(schema->elemDecl, node->name);
1433 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1435 FIXME("declaration not found in main schema - need to check schema imports!\n");
1436 /*xmlSchemaImportPtr import;
1438 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1440 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1443 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1449 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1451 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1452 while (decl != NULL && decl->refDecl != NULL)
1453 decl = decl->refDecl;
1454 return (decl != NULL)? decl->node : NULL;
1457 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1459 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1460 xmlSchemaPtr schema;
1462 TRACE("(%p, %p)\n", This, tree);
1467 if (tree->type == XML_DOCUMENT_NODE)
1468 tree = xmlDocGetRootElement(tree->doc);
1470 schema = get_node_schema(This, tree);
1471 /* TODO: if the ns is not in the cache, and it's a URL,
1472 * do we try to load from that? */
1474 return Schema_validate_tree(schema, tree);
1476 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1481 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1483 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1484 xmlSchemaPtr schema = get_node_schema(This, node);
1485 XDR_DT dt = DT_INVALID;
1487 TRACE("(%p, %p)\n", This, node);
1489 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1491 dt = str_to_dt(node->name, -1);
1496 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1498 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1501 dt = str_to_dt(str, -1);
1509 static const tid_t schemacache_iface_tids[] = {
1510 IXMLDOMSchemaCollection2_tid,
1514 static dispex_static_data_t schemacache_dispex = {
1516 IXMLDOMSchemaCollection2_tid,
1518 schemacache_iface_tids
1521 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1523 schema_cache* This = heap_alloc(sizeof(schema_cache));
1525 return E_OUTOFMEMORY;
1527 TRACE("(%d %p %p)\n", version, outer, obj);
1529 This->IXMLDOMSchemaCollection2_iface.lpVtbl = &XMLDOMSchemaCollection2Vtbl;
1530 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1532 This->version = version;
1533 This->validateOnLoad = VARIANT_TRUE;
1534 This->read_only = 0;
1535 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSchemaCollection2_iface, &schemacache_dispex);
1537 *obj = &This->IXMLDOMSchemaCollection2_iface;
1543 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1545 MESSAGE("This program tried to use a SchemaCache object, but\n"
1546 "libxml2 support was not present at compile time.\n");