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>
46 #include "wine/debug.h"
48 #include "msxml_private.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
52 /* We use a chained hashtable, which can hold any number of schemas
53 * TODO: grow/shrink hashtable depending on load factor
54 * TODO: implement read-only where appropriate
57 /* This is just the number of buckets, should be prime */
58 #define DEFAULT_HASHTABLE_SIZE 17
62 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
64 static const xmlChar XSD_schema[] = "schema";
65 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
66 static const xmlChar XDR_schema[] = "Schema";
67 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
68 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
70 static xmlChar const* datatypes_src;
71 static int datatypes_len;
72 static HGLOBAL datatypes_handle;
73 static HRSRC datatypes_rsrc;
74 static xmlSchemaPtr datatypes_schema;
76 static const WCHAR emptyW[] = {0};
84 typedef enum _SCHEMA_TYPE {
90 typedef struct _schema_cache
92 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
93 MSXML_VERSION version;
94 xmlHashTablePtr cache;
98 typedef struct _cache_entry
106 typedef struct _cache_index_data
112 /* datatypes lookup stuff
113 * generated with help from gperf */
114 #define DT_MIN_STR_LEN 2
115 #define DT_MAX_STR_LEN 11
116 #define DT_MIN_HASH_VALUE 2
117 #define DT_MAX_HASH_VALUE 115
119 static const xmlChar DT_bin_base64[] = "bin.base64";
120 static const xmlChar DT_bin_hex[] = "bin.hex";
121 static const xmlChar DT_boolean[] = "boolean";
122 static const xmlChar DT_char[] = "char";
123 static const xmlChar DT_date[] = "date";
124 static const xmlChar DT_date_tz[] = "date.tz";
125 static const xmlChar DT_dateTime[] = "dateTime";
126 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
127 static const xmlChar DT_entity[] = "entity";
128 static const xmlChar DT_entities[] = "entities";
129 static const xmlChar DT_enumeration[] = "enumeration";
130 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
131 static const xmlChar DT_float[] = "float";
132 static const xmlChar DT_i1[] = "i1";
133 static const xmlChar DT_i2[] = "i2";
134 static const xmlChar DT_i4[] = "i4";
135 static const xmlChar DT_i8[] = "i8";
136 static const xmlChar DT_id[] = "id";
137 static const xmlChar DT_idref[] = "idref";
138 static const xmlChar DT_idrefs[] = "idrefs";
139 static const xmlChar DT_int[] = "int";
140 static const xmlChar DT_nmtoken[] = "nmtoken";
141 static const xmlChar DT_nmtokens[] = "nmtokens";
142 static const xmlChar DT_notation[] = "notation";
143 static const xmlChar DT_number[] = "number";
144 static const xmlChar DT_r4[] = "r4";
145 static const xmlChar DT_r8[] = "r8";
146 static const xmlChar DT_string[] = "string";
147 static const xmlChar DT_time[] = "time";
148 static const xmlChar DT_time_tz[] = "time.tz";
149 static const xmlChar DT_ui1[] = "ui1";
150 static const xmlChar DT_ui2[] = "ui2";
151 static const xmlChar DT_ui4[] = "ui4";
152 static const xmlChar DT_ui8[] = "ui8";
153 static const xmlChar DT_uri[] = "uri";
154 static const xmlChar DT_uuid[] = "uuid";
156 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
157 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
158 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
159 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
160 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
161 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
162 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
163 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
164 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
165 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
166 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
167 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
168 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
169 static const OLECHAR wDT_i1[] = {'i','1',0};
170 static const OLECHAR wDT_i2[] = {'i','2',0};
171 static const OLECHAR wDT_i4[] = {'i','4',0};
172 static const OLECHAR wDT_i8[] = {'i','8',0};
173 static const OLECHAR wDT_id[] = {'i','d',0};
174 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
175 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
176 static const OLECHAR wDT_int[] = {'i','n','t',0};
177 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
178 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
179 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
180 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
181 static const OLECHAR wDT_r4[] = {'r','4',0};
182 static const OLECHAR wDT_r8[] = {'r','8',0};
183 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
184 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
185 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
186 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
187 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
188 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
189 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
190 static const OLECHAR wDT_uri[] = {'u','r','i',0};
191 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
193 static const BYTE hash_assoc_values[] =
195 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
196 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
197 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
198 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
199 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
200 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
201 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
202 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
203 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
204 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
205 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
206 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
207 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
208 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
209 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
213 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
214 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
216 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
217 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
218 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
219 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
220 116, 116, 116, 116, 116, 116
223 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
227 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
231 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
235 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
239 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
240 static void parser_serror(void* ctx, xmlErrorPtr err)
242 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
246 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
248 TRACE("(%p)\n", spctx);
250 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
251 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
252 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
255 return xmlSchemaParse(spctx);
258 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
262 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
266 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
270 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
274 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
275 static void validate_serror(void* ctx, xmlErrorPtr err)
277 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
281 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
283 xmlSchemaValidCtxtPtr svctx;
286 TRACE("(%p, %p)\n", schema, tree);
287 /* TODO: if validateOnLoad property is false,
288 * we probably need to validate the schema here. */
289 svctx = xmlSchemaNewValidCtxt(schema);
290 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
291 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
292 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
295 if (tree->type == XML_DOCUMENT_NODE)
296 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
298 err = xmlSchemaValidateOneElement(svctx, tree);
300 xmlSchemaFreeValidCtxt(svctx);
301 return err? S_FALSE : S_OK;
304 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
306 DWORD hval = (len == -1)? xmlStrlen(str) : len;
311 hval += hash_assoc_values[str[10]];
314 hval += hash_assoc_values[str[9]];
317 hval += hash_assoc_values[str[8]];
320 hval += hash_assoc_values[str[7]];
323 hval += hash_assoc_values[str[6]];
326 hval += hash_assoc_values[str[5]];
329 hval += hash_assoc_values[str[4]];
332 hval += hash_assoc_values[str[3]];
335 hval += hash_assoc_values[str[2]];
338 hval += hash_assoc_values[str[1]];
341 hval += hash_assoc_values[str[0]];
347 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
349 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
354 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
357 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
360 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
363 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
366 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
369 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
372 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
375 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
378 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
381 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
384 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
390 static const xmlChar *const DT_string_table[DT__N_TYPES] =
430 static const WCHAR *const DT_wstring_table[DT__N_TYPES] =
470 static const XDR_DT DT_lookup_table[] =
523 -1, -1, -1, -1, -1, -1, -1, -1, -1,
524 -1, -1, -1, -1, -1, -1, -1, -1, -1,
525 -1, -1, -1, -1, -1, -1, -1, -1, -1,
526 -1, -1, -1, -1, -1, -1, -1, -1, -1,
527 -1, -1, -1, -1, -1, -1, -1, -1, -1,
528 -1, -1, -1, -1, -1, -1, -1, -1,
532 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
534 DWORD hash = dt_hash(str, len);
535 XDR_DT dt = DT_INVALID;
537 if (hash <= DT_MAX_HASH_VALUE)
538 dt = DT_lookup_table[hash];
540 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
546 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
548 DWORD hash = dt_hash_bstr(bstr, len);
549 XDR_DT dt = DT_INVALID;
551 if (hash <= DT_MAX_HASH_VALUE)
552 dt = DT_lookup_table[hash];
554 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
560 xmlChar const* dt_to_str(XDR_DT dt)
562 if (dt == DT_INVALID)
565 return DT_string_table[dt];
568 OLECHAR const* dt_to_bstr(XDR_DT dt)
570 if (dt == DT_INVALID)
573 return DT_wstring_table[dt];
576 const char* debugstr_dt(XDR_DT dt)
578 return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL);
581 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
588 TRACE("(dt:%s, %s)\n", debugstr_dt(dt), wine_dbgstr_a((char const*)content));
590 if (!datatypes_schema)
592 xmlSchemaParserCtxtPtr spctx;
593 assert(datatypes_src != NULL);
594 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
595 datatypes_schema = Schema_parse(spctx);
596 xmlSchemaFreeParserCtxt(spctx);
632 if (!datatypes_schema)
634 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
635 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n");
637 /* Hopefully they don't need much in the way of XDR datatypes support... */
641 if (content && xmlStrlen(content))
643 tmp_doc = xmlNewDoc(NULL);
644 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
645 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
647 xmlDocSetRootElement(tmp_doc, node);
649 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
653 { /* probably the node is being created manually and has no content yet */
658 FIXME("need to handle dt:%s\n", debugstr_dt(dt));
663 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
665 return (node->ns != NULL)? node->ns->href : NULL;
668 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
670 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
671 xmlHashLookup(This->cache, nsURI);
674 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
676 cache_entry* entry = get_entry(This, get_node_nsURI(node));
677 return (!entry)? NULL : entry->schema;
680 static xmlExternalEntityLoader _external_entity_loader;
682 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
683 xmlParserCtxtPtr ctxt)
685 xmlParserInputPtr input;
687 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
689 assert(MSXML_hInstance != NULL);
690 assert(datatypes_rsrc != NULL);
691 assert(datatypes_handle != NULL);
692 assert(datatypes_src != NULL);
694 /* TODO: if the desired schema is in the cache, load it from there */
695 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
697 TRACE("loading built-in schema for %s\n", URL);
698 input = xmlNewStringInputStream(ctxt, datatypes_src);
702 input = _external_entity_loader(URL, ID, ctxt);
708 void schemasInit(void)
712 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
714 FIXME("failed to find resource for %s\n", DT_nsURI);
718 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
720 FIXME("failed to load resource for %s\n", DT_nsURI);
723 buf = LockResource(datatypes_handle);
724 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
726 /* Resource is loaded as raw data,
727 * need a null-terminated string */
728 while (buf[len] != '>')
730 datatypes_src = BAD_CAST buf;
731 datatypes_len = len + 1;
733 if (xmlGetExternalEntityLoader() != external_entity_loader)
735 _external_entity_loader = xmlGetExternalEntityLoader();
736 xmlSetExternalEntityLoader(external_entity_loader);
740 void schemasCleanup(void)
742 xmlSchemaFree(datatypes_schema);
743 xmlSetExternalEntityLoader(_external_entity_loader);
746 static LONG cache_entry_add_ref(cache_entry* entry)
748 LONG ref = InterlockedIncrement(&entry->ref);
749 TRACE("%p new ref %d\n", entry, ref);
753 static LONG cache_entry_release(cache_entry* entry)
755 LONG ref = InterlockedDecrement(&entry->ref);
756 TRACE("%p new ref %d\n", entry, ref);
760 if (entry->type == SCHEMA_TYPE_XSD)
762 xmldoc_release(entry->doc);
763 entry->schema->doc = NULL;
764 xmlSchemaFree(entry->schema);
767 else /* SCHEMA_TYPE_XDR */
769 xmldoc_release(entry->doc);
770 xmldoc_release(entry->schema->doc);
771 entry->schema->doc = NULL;
772 xmlSchemaFree(entry->schema);
779 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
781 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
784 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
786 xmlNodePtr root = NULL;
788 root = xmlDocGetRootElement(schema);
789 if (root && root->ns)
792 if (xmlStrEqual(root->name, XDR_schema) &&
793 xmlStrEqual(root->ns->href, XDR_nsURI))
795 return SCHEMA_TYPE_XDR;
797 else if (xmlStrEqual(root->name, XSD_schema) &&
798 xmlStrEqual(root->ns->href, XSD_nsURI))
800 return SCHEMA_TYPE_XSD;
803 return SCHEMA_TYPE_INVALID;
806 static BOOL link_datatypes(xmlDocPtr schema)
808 xmlNodePtr root, next, child;
811 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
812 root = xmlDocGetRootElement(schema);
816 for (ns = root->nsDef; ns != NULL; ns = ns->next)
818 if (xmlStrEqual(ns->href, DT_nsURI))
825 next = xmlFirstElementChild(root);
826 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
827 if (next) child = xmlAddPrevSibling(next, child);
828 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
829 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
834 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
836 cache_entry* entry = heap_alloc(sizeof(cache_entry));
837 xmlSchemaParserCtxtPtr spctx;
838 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
840 link_datatypes(new_doc);
842 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
843 * do we need to do something special here? */
844 entry->type = SCHEMA_TYPE_XSD;
846 spctx = xmlSchemaNewDocParserCtxt(new_doc);
848 if ((entry->schema = Schema_parse(spctx)))
850 xmldoc_init(entry->schema->doc, v);
851 entry->doc = entry->schema->doc;
852 xmldoc_add_ref(entry->doc);
856 FIXME("failed to parse doc\n");
861 xmlSchemaFreeParserCtxt(spctx);
865 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version)
867 cache_entry* entry = heap_alloc(sizeof(cache_entry));
868 xmlSchemaParserCtxtPtr spctx;
869 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
871 link_datatypes(xsd_doc);
873 entry->type = SCHEMA_TYPE_XDR;
875 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
877 if ((entry->schema = Schema_parse(spctx)))
879 entry->doc = new_doc;
880 xmldoc_init(entry->schema->doc, version);
881 xmldoc_init(entry->doc, version);
882 xmldoc_add_ref(entry->doc);
883 xmldoc_add_ref(entry->schema->doc);
887 FIXME("failed to parse doc\n");
893 xmlSchemaFreeParserCtxt(spctx);
898 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version)
901 IXMLDOMDocument3* domdoc = NULL;
902 xmlDocPtr doc = NULL;
903 HRESULT hr = DOMDocument_create(version, NULL, (void**)&domdoc);
904 VARIANT_BOOL b = VARIANT_FALSE;
905 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
909 FIXME("failed to create domdoc\n");
912 assert(domdoc != NULL);
913 assert(V_VT(&url) == VT_BSTR);
915 hr = IXMLDOMDocument3_load(domdoc, url, &b);
918 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
919 if (b != VARIANT_TRUE)
921 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
922 IXMLDOMDocument3_Release(domdoc);
926 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
927 type = schema_type_from_xmlDocPtr(doc);
931 case SCHEMA_TYPE_XSD:
932 entry = cache_entry_from_xsd_doc(doc, nsURI, version);
934 case SCHEMA_TYPE_XDR:
935 entry = cache_entry_from_xdr_doc(doc, nsURI, version);
937 case SCHEMA_TYPE_INVALID:
939 FIXME("invalid schema\n");
942 IXMLDOMDocument3_Release(domdoc);
947 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
948 REFIID riid, void** ppvObject)
950 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
952 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
954 if ( IsEqualIID(riid, &IID_IUnknown) ||
955 IsEqualIID(riid, &IID_IDispatch) ||
956 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
957 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
963 FIXME("interface %s not implemented\n", debugstr_guid(riid));
965 return E_NOINTERFACE;
968 IXMLDOMSchemaCollection2_AddRef(iface);
973 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
975 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
976 LONG ref = InterlockedIncrement(&This->ref);
977 TRACE("%p new ref %d\n", This, ref);
981 static void cache_free(void* data, xmlChar* name /* ignored */)
983 cache_entry_release((cache_entry*)data);
986 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
988 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
989 LONG ref = InterlockedDecrement(&This->ref);
990 TRACE("%p new ref %d\n", This, ref);
994 xmlHashFree(This->cache, cache_free);
1001 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1004 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1006 TRACE("(%p)->(%p)\n", This, pctinfo);
1013 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1014 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1016 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1019 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1021 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1026 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1027 REFIID riid, LPOLESTR* rgszNames,
1028 UINT cNames, LCID lcid, DISPID* rgDispId)
1030 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1031 ITypeInfo* typeinfo;
1034 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1037 if(!rgszNames || cNames == 0 || !rgDispId)
1038 return E_INVALIDARG;
1040 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1043 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1044 ITypeInfo_Release(typeinfo);
1050 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1051 DISPID dispIdMember, REFIID riid, LCID lcid,
1052 WORD wFlags, DISPPARAMS* pDispParams,
1053 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1056 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1057 ITypeInfo* typeinfo;
1060 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1061 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1063 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1066 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1067 pVarResult, pExcepInfo, puArgErr);
1068 ITypeInfo_Release(typeinfo);
1074 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1076 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1077 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1078 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1084 xmlHashRemoveEntry(This->cache, name, cache_free);
1090 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1094 cache_entry_add_ref(entry);
1102 xmlHashRemoveEntry(This->cache, name, cache_free);
1103 xmlHashAddEntry(This->cache, name, entry);
1109 xmlDocPtr doc = NULL;
1112 IXMLDOMNode* domnode = NULL;
1113 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1116 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1120 IXMLDOMNode_Release(domnode);
1122 return E_INVALIDARG;
1124 type = schema_type_from_xmlDocPtr(doc);
1126 if (type == SCHEMA_TYPE_XSD)
1128 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1130 else if (type == SCHEMA_TYPE_XDR)
1132 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1136 WARN("invalid schema!\n");
1140 IXMLDOMNode_Release(domnode);
1144 cache_entry_add_ref(entry);
1152 xmlHashRemoveEntry(This->cache, name, cache_free);
1153 xmlHashAddEntry(This->cache, name, entry);
1160 return E_INVALIDARG;
1167 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1170 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1173 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1178 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1179 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1182 /* TODO: this should be read-only */
1184 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1190 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1192 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1193 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1194 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1196 xmlHashRemoveEntry(This->cache, name, cache_free);
1201 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1203 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1204 TRACE("(%p)->(%p)\n", This, length);
1208 *length = xmlHashSize(This->cache);
1212 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1214 cache_index_data* index_data = (cache_index_data*)index;
1216 if (index_data->index-- == 0)
1217 *index_data->out = bstr_from_xmlChar(name);
1220 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1221 LONG index, BSTR* len)
1223 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1224 cache_index_data data = {index,len};
1225 TRACE("(%p)->(%i, %p)\n", This, index, len);
1231 if (index >= xmlHashSize(This->cache))
1234 xmlHashScan(This->cache, cache_index, &data);
1238 static void cache_copy(void* data, void* dest, xmlChar* name)
1240 schema_cache* This = (schema_cache*) dest;
1241 cache_entry* entry = (cache_entry*) data;
1243 if (xmlHashLookup(This->cache, name) == NULL)
1245 cache_entry_add_ref(entry);
1246 xmlHashAddEntry(This->cache, name, entry);
1250 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1251 IXMLDOMSchemaCollection* otherCollection)
1253 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1254 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1255 TRACE("(%p)->(%p)\n", This, That);
1257 if (!otherCollection)
1260 /* TODO: detect errors while copying & return E_FAIL */
1261 xmlHashScan(That->cache, cache_copy, This);
1266 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1275 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1281 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1282 VARIANT_BOOL validateOnLoad)
1288 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1289 VARIANT_BOOL* validateOnLoad)
1295 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1296 BSTR namespaceURI, ISchema** schema)
1304 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1305 IXMLDOMNode* node, ISchemaItem** item)
1313 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1315 schema_cache_QueryInterface,
1316 schema_cache_AddRef,
1317 schema_cache_Release,
1318 schema_cache_GetTypeInfoCount,
1319 schema_cache_GetTypeInfo,
1320 schema_cache_GetIDsOfNames,
1321 schema_cache_Invoke,
1324 schema_cache_remove,
1325 schema_cache_get_length,
1326 schema_cache_get_namespaceURI,
1327 schema_cache_addCollection,
1328 schema_cache_get__newEnum,
1329 schema_cache_validate,
1330 schema_cache_put_validateOnLoad,
1331 schema_cache_get_validateOnLoad,
1332 schema_cache_getSchema,
1333 schema_cache_getDeclaration
1336 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1338 xmlSchemaElementPtr decl = NULL;
1339 xmlChar const* nsURI = get_node_nsURI(node);
1341 TRACE("(%p, %p)\n", schema, node);
1343 if (xmlStrEqual(nsURI, schema->targetNamespace))
1344 decl = xmlHashLookup(schema->elemDecl, node->name);
1346 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1348 FIXME("declaration not found in main schema - need to check schema imports!\n");
1349 /*xmlSchemaImportPtr import;
1351 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1353 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1356 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1362 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1364 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1365 while (decl != NULL && decl->refDecl != NULL)
1366 decl = decl->refDecl;
1367 return (decl != NULL)? decl->node : NULL;
1370 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1372 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1373 xmlSchemaPtr schema;
1375 TRACE("(%p, %p)\n", This, tree);
1380 if (tree->type == XML_DOCUMENT_NODE)
1381 tree = xmlDocGetRootElement(tree->doc);
1383 schema = get_node_schema(This, tree);
1384 /* TODO: if the ns is not in the cache, and it's a URL,
1385 * do we try to load from that? */
1387 return Schema_validate_tree(schema, tree);
1389 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1394 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1396 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1397 xmlSchemaPtr schema = get_node_schema(This, node);
1398 XDR_DT dt = DT_INVALID;
1400 TRACE("(%p, %p)\n", This, node);
1402 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1404 dt = str_to_dt(node->name, -1);
1409 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1411 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1414 dt = str_to_dt(str, -1);
1422 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* pUnkOuter, void** ppObj)
1424 schema_cache* This = heap_alloc(sizeof(schema_cache));
1426 return E_OUTOFMEMORY;
1428 This->lpVtbl = &schema_cache_vtbl;
1429 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1431 This->version = version;
1433 *ppObj = &This->lpVtbl;
1439 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* pUnkOuter, void** ppObj)
1441 MESSAGE("This program tried to use a SchemaCache object, but\n"
1442 "libxml2 support was not present at compile time.\n");