2 * Schema cache implementation
4 * Copyright 2007 Huw Davies
5 * Copyright 2010 Adam Martinson for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include "wine/debug.h"
36 #include "msxml_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
40 /* We use a chained hashtable, which can hold any number of schemas
41 * TODO: grow/shrink hashtable depending on load factor
42 * TODO: implement read-only where appropriate
45 /* This is just the number of buckets, should be prime */
46 #define DEFAULT_HASHTABLE_SIZE 17
50 #include <libxml/tree.h>
51 #include <libxml/xmlschemas.h>
52 #include <libxml/schemasInternals.h>
53 #include <libxml/hash.h>
54 #include <libxml/parser.h>
55 #include <libxml/parserInternals.h>
56 #include <libxml/xmlIO.h>
58 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
60 static const xmlChar XSD_schema[] = "schema";
61 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
62 static const xmlChar XDR_schema[] = "Schema";
63 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
64 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
66 static xmlChar const* datatypes_src;
67 static int datatypes_len;
68 static HGLOBAL datatypes_handle;
69 static HRSRC datatypes_rsrc;
70 static xmlSchemaPtr datatypes_schema;
72 static const WCHAR emptyW[] = {0};
80 typedef enum _SCHEMA_TYPE {
86 typedef struct _schema_cache
88 const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
89 MSXML_VERSION version;
90 xmlHashTablePtr cache;
94 typedef struct _cache_entry
102 typedef struct _cache_index_data
108 /* datatypes lookup stuff
109 * generated with help from gperf */
110 #define DT_MIN_STR_LEN 2
111 #define DT_MAX_STR_LEN 11
112 #define DT_MIN_HASH_VALUE 2
113 #define DT_MAX_HASH_VALUE 115
115 static const xmlChar DT_bin_base64[] = "bin.base64";
116 static const xmlChar DT_bin_hex[] = "bin.hex";
117 static const xmlChar DT_boolean[] = "boolean";
118 static const xmlChar DT_char[] = "char";
119 static const xmlChar DT_date[] = "date";
120 static const xmlChar DT_date_tz[] = "date.tz";
121 static const xmlChar DT_dateTime[] = "dateTime";
122 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
123 static const xmlChar DT_entity[] = "entity";
124 static const xmlChar DT_entities[] = "entities";
125 static const xmlChar DT_enumeration[] = "enumeration";
126 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
127 static const xmlChar DT_float[] = "float";
128 static const xmlChar DT_i1[] = "i1";
129 static const xmlChar DT_i2[] = "i2";
130 static const xmlChar DT_i4[] = "i4";
131 static const xmlChar DT_i8[] = "i8";
132 static const xmlChar DT_id[] = "id";
133 static const xmlChar DT_idref[] = "idref";
134 static const xmlChar DT_idrefs[] = "idrefs";
135 static const xmlChar DT_int[] = "int";
136 static const xmlChar DT_nmtoken[] = "nmtoken";
137 static const xmlChar DT_nmtokens[] = "nmtokens";
138 static const xmlChar DT_notation[] = "notation";
139 static const xmlChar DT_number[] = "number";
140 static const xmlChar DT_r4[] = "r4";
141 static const xmlChar DT_r8[] = "r8";
142 static const xmlChar DT_string[] = "string";
143 static const xmlChar DT_time[] = "time";
144 static const xmlChar DT_time_tz[] = "time.tz";
145 static const xmlChar DT_ui1[] = "ui1";
146 static const xmlChar DT_ui2[] = "ui2";
147 static const xmlChar DT_ui4[] = "ui4";
148 static const xmlChar DT_ui8[] = "ui8";
149 static const xmlChar DT_uri[] = "uri";
150 static const xmlChar DT_uuid[] = "uuid";
152 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
153 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
154 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
155 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
156 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
157 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
158 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
159 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
160 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
161 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
162 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
163 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
164 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
165 static const OLECHAR wDT_i1[] = {'i','1',0};
166 static const OLECHAR wDT_i2[] = {'i','2',0};
167 static const OLECHAR wDT_i4[] = {'i','4',0};
168 static const OLECHAR wDT_i8[] = {'i','8',0};
169 static const OLECHAR wDT_id[] = {'i','d',0};
170 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
171 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
172 static const OLECHAR wDT_int[] = {'i','n','t',0};
173 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
174 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
175 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
176 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
177 static const OLECHAR wDT_r4[] = {'r','4',0};
178 static const OLECHAR wDT_r8[] = {'r','8',0};
179 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
180 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
181 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
182 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
183 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
184 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
185 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
186 static const OLECHAR wDT_uri[] = {'u','r','i',0};
187 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
189 static const BYTE hash_assoc_values[] =
191 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
192 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
193 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
194 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
195 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
196 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
197 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
198 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
199 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
200 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
201 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
202 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
203 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
206 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
207 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
208 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
209 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
213 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
214 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
216 116, 116, 116, 116, 116, 116
219 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
223 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
227 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
231 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
235 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
236 static void parser_serror(void* ctx, xmlErrorPtr err)
238 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
242 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
244 TRACE("(%p)\n", spctx);
246 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
247 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
248 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
251 return xmlSchemaParse(spctx);
254 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
258 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
262 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
266 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
270 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
271 static void validate_serror(void* ctx, xmlErrorPtr err)
273 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
277 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
279 xmlSchemaValidCtxtPtr svctx;
282 TRACE("(%p, %p)\n", schema, tree);
283 /* TODO: if validateOnLoad property is false,
284 * we probably need to validate the schema here. */
285 svctx = xmlSchemaNewValidCtxt(schema);
286 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
287 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
288 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
291 if (tree->type == XML_DOCUMENT_NODE)
292 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
294 err = xmlSchemaValidateOneElement(svctx, tree);
296 xmlSchemaFreeValidCtxt(svctx);
297 return err? S_FALSE : S_OK;
300 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
302 DWORD hval = (len == -1)? xmlStrlen(str) : len;
307 hval += hash_assoc_values[str[10]];
310 hval += hash_assoc_values[str[9]];
313 hval += hash_assoc_values[str[8]];
316 hval += hash_assoc_values[str[7]];
319 hval += hash_assoc_values[str[6]];
322 hval += hash_assoc_values[str[5]];
325 hval += hash_assoc_values[str[4]];
328 hval += hash_assoc_values[str[3]];
331 hval += hash_assoc_values[str[2]];
334 hval += hash_assoc_values[str[1]];
337 hval += hash_assoc_values[str[0]];
343 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
345 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
350 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
353 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
356 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
359 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
362 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
365 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
368 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
371 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
374 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
377 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
380 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
386 static const xmlChar const* DT_string_table[DT__N_TYPES] =
426 static const WCHAR const* DT_wstring_table[DT__N_TYPES] =
466 static const XDR_DT DT_lookup_table[] =
519 -1, -1, -1, -1, -1, -1, -1, -1, -1,
520 -1, -1, -1, -1, -1, -1, -1, -1, -1,
521 -1, -1, -1, -1, -1, -1, -1, -1, -1,
522 -1, -1, -1, -1, -1, -1, -1, -1, -1,
523 -1, -1, -1, -1, -1, -1, -1, -1, -1,
524 -1, -1, -1, -1, -1, -1, -1, -1,
528 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
530 DWORD hash = dt_hash(str, len);
531 XDR_DT dt = DT_INVALID;
533 if (hash <= DT_MAX_HASH_VALUE)
534 dt = DT_lookup_table[hash];
536 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
542 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
544 DWORD hash = dt_hash_bstr(bstr, len);
545 XDR_DT dt = DT_INVALID;
547 if (hash <= DT_MAX_HASH_VALUE)
548 dt = DT_lookup_table[hash];
550 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
556 xmlChar const* dt_to_str(XDR_DT dt)
558 if (dt == DT_INVALID)
561 return DT_string_table[dt];
564 OLECHAR const* dt_to_bstr(XDR_DT dt)
566 if (dt == DT_INVALID)
569 return DT_wstring_table[dt];
572 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
579 TRACE("(dt:%s, %s)\n", dt_to_str(dt), wine_dbgstr_a((char const*)content));
581 if (!datatypes_schema)
583 xmlSchemaParserCtxtPtr spctx;
584 assert(datatypes_src != NULL);
585 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
586 datatypes_schema = Schema_parse(spctx);
587 xmlSchemaFreeParserCtxt(spctx);
623 assert(datatypes_schema != NULL);
624 if (content && xmlStrlen(content))
626 tmp_doc = xmlNewDoc(NULL);
627 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
628 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
630 xmlDocSetRootElement(tmp_doc, node);
632 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
636 { /* probably the node is being created manually and has no content yet */
641 FIXME("need to handle dt:%s\n", dt_to_str(dt));
646 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
648 return (node->ns != NULL)? node->ns->href : NULL;
651 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
653 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
654 xmlHashLookup(This->cache, nsURI);
657 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
659 cache_entry* entry = get_entry(This, get_node_nsURI(node));
660 return (!entry)? NULL : entry->schema;
663 static xmlExternalEntityLoader _external_entity_loader;
665 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
666 xmlParserCtxtPtr ctxt)
668 xmlParserInputPtr input;
670 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
672 assert(MSXML_hInstance != NULL);
673 assert(datatypes_rsrc != NULL);
674 assert(datatypes_handle != NULL);
675 assert(datatypes_src != NULL);
677 /* TODO: if the desired schema is in the cache, load it from there */
678 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
680 TRACE("loading built-in schema for %s\n", URL);
681 input = xmlNewStringInputStream(ctxt, datatypes_src);
685 input = _external_entity_loader(URL, ID, ctxt);
691 void schemasInit(void)
695 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
697 FIXME("failed to find resource for %s\n", DT_nsURI);
701 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
703 FIXME("failed to load resource for %s\n", DT_nsURI);
706 buf = LockResource(datatypes_handle);
707 len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
709 /* Resource is loaded as raw data,
710 * need a null-terminated string */
711 while (buf[len] != '>')
713 datatypes_src = BAD_CAST buf;
714 datatypes_len = len + 1;
716 if (xmlGetExternalEntityLoader() != external_entity_loader)
718 _external_entity_loader = xmlGetExternalEntityLoader();
719 xmlSetExternalEntityLoader(external_entity_loader);
723 void schemasCleanup(void)
725 xmlSchemaFree(datatypes_schema);
726 xmlSetExternalEntityLoader(_external_entity_loader);
729 static LONG cache_entry_add_ref(cache_entry* entry)
731 LONG ref = InterlockedIncrement(&entry->ref);
732 TRACE("%p new ref %d\n", entry, ref);
736 static LONG cache_entry_release(cache_entry* entry)
738 LONG ref = InterlockedDecrement(&entry->ref);
739 TRACE("%p new ref %d\n", entry, ref);
743 if (entry->type == SCHEMA_TYPE_XSD)
745 xmldoc_release(entry->doc);
746 entry->schema->doc = NULL;
747 xmlSchemaFree(entry->schema);
750 else /* SCHEMA_TYPE_XDR */
752 xmldoc_release(entry->doc);
753 xmldoc_release(entry->schema->doc);
754 entry->schema->doc = NULL;
755 xmlSchemaFree(entry->schema);
762 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
764 return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
767 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
769 xmlNodePtr root = NULL;
771 root = xmlDocGetRootElement(schema);
772 if (root && root->ns)
775 if (xmlStrEqual(root->name, XDR_schema) &&
776 xmlStrEqual(root->ns->href, XDR_nsURI))
778 return SCHEMA_TYPE_XDR;
780 else if (xmlStrEqual(root->name, XSD_schema) &&
781 xmlStrEqual(root->ns->href, XSD_nsURI))
783 return SCHEMA_TYPE_XSD;
786 return SCHEMA_TYPE_INVALID;
789 static BOOL link_datatypes(xmlDocPtr schema)
791 xmlNodePtr root, next, child;
794 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
795 root = xmlDocGetRootElement(schema);
799 for (ns = root->nsDef; ns != NULL; ns = ns->next)
801 if (xmlStrEqual(ns->href, DT_nsURI))
808 next = xmlFirstElementChild(root);
809 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
810 if (next) child = xmlAddPrevSibling(next, child);
811 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
812 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
817 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
819 cache_entry* entry = heap_alloc(sizeof(cache_entry));
820 xmlSchemaParserCtxtPtr spctx;
821 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
823 link_datatypes(new_doc);
825 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
826 * do we need to do something special here? */
827 entry->type = SCHEMA_TYPE_XSD;
829 spctx = xmlSchemaNewDocParserCtxt(new_doc);
831 if ((entry->schema = Schema_parse(spctx)))
833 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
834 entry->doc = entry->schema->doc;
835 xmldoc_add_ref(entry->doc);
839 FIXME("failed to parse doc\n");
844 xmlSchemaFreeParserCtxt(spctx);
848 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
850 cache_entry* entry = heap_alloc(sizeof(cache_entry));
851 xmlSchemaParserCtxtPtr spctx;
852 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
854 link_datatypes(xsd_doc);
856 entry->type = SCHEMA_TYPE_XDR;
858 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
860 if ((entry->schema = Schema_parse(spctx)))
862 entry->doc = new_doc;
863 xmldoc_init(entry->schema->doc, DOMDocument_version(v));
864 xmldoc_init(entry->doc, DOMDocument_version(v));
865 xmldoc_add_ref(entry->doc);
866 xmldoc_add_ref(entry->schema->doc);
870 FIXME("failed to parse doc\n");
876 xmlSchemaFreeParserCtxt(spctx);
881 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION v)
884 IXMLDOMDocument3* domdoc = NULL;
885 xmlDocPtr doc = NULL;
886 HRESULT hr = DOMDocument_create(DOMDocument_version(v), NULL, (void**)&domdoc);
887 VARIANT_BOOL b = VARIANT_FALSE;
888 SCHEMA_TYPE type = SCHEMA_TYPE_INVALID;
892 FIXME("failed to create domdoc\n");
895 assert(domdoc != NULL);
896 assert(V_VT(&url) == VT_BSTR);
898 hr = IXMLDOMDocument3_load(domdoc, url, &b);
901 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
902 if (b != VARIANT_TRUE)
904 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url)));
905 IXMLDOMDocument3_Release(domdoc);
909 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
910 type = schema_type_from_xmlDocPtr(doc);
914 case SCHEMA_TYPE_XSD:
915 entry = cache_entry_from_xsd_doc(doc, nsURI, v);
917 case SCHEMA_TYPE_XDR:
918 entry = cache_entry_from_xdr_doc(doc, nsURI, v);
920 case SCHEMA_TYPE_INVALID:
922 FIXME("invalid schema\n");
925 IXMLDOMDocument3_Release(domdoc);
930 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
931 REFIID riid, void** ppvObject)
933 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
935 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
937 if ( IsEqualIID(riid, &IID_IUnknown) ||
938 IsEqualIID(riid, &IID_IDispatch) ||
939 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
940 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
946 FIXME("interface %s not implemented\n", debugstr_guid(riid));
947 return E_NOINTERFACE;
950 IXMLDOMSchemaCollection2_AddRef(iface);
955 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
957 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
958 LONG ref = InterlockedIncrement(&This->ref);
959 TRACE("%p new ref %d\n", This, ref);
963 static void cache_free(void* data, xmlChar* name /* ignored */)
965 cache_entry_release((cache_entry*)data);
968 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
970 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
971 LONG ref = InterlockedDecrement(&This->ref);
972 TRACE("%p new ref %d\n", This, ref);
976 xmlHashFree(This->cache, cache_free);
983 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
986 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
988 TRACE("(%p)->(%p)\n", This, pctinfo);
995 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
996 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
998 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1001 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1003 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1008 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1009 REFIID riid, LPOLESTR* rgszNames,
1010 UINT cNames, LCID lcid, DISPID* rgDispId)
1012 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1013 ITypeInfo* typeinfo;
1016 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1019 if(!rgszNames || cNames == 0 || !rgDispId)
1020 return E_INVALIDARG;
1022 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1025 hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1026 ITypeInfo_Release(typeinfo);
1032 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1033 DISPID dispIdMember, REFIID riid, LCID lcid,
1034 WORD wFlags, DISPPARAMS* pDispParams,
1035 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1038 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1039 ITypeInfo* typeinfo;
1042 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1043 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1045 hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1048 hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1049 pVarResult, pExcepInfo, puArgErr);
1050 ITypeInfo_Release(typeinfo);
1056 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1058 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1059 xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1060 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1066 xmlHashRemoveEntry(This->cache, name, cache_free);
1072 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1076 cache_entry_add_ref(entry);
1084 xmlHashRemoveEntry(This->cache, name, cache_free);
1085 xmlHashAddEntry(This->cache, name, entry);
1091 xmlDocPtr doc = NULL;
1094 IXMLDOMNode* domnode = NULL;
1095 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1098 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1102 IXMLDOMNode_Release(domnode);
1104 return E_INVALIDARG;
1106 type = schema_type_from_xmlDocPtr(doc);
1108 if (type == SCHEMA_TYPE_XSD)
1110 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1112 else if (type == SCHEMA_TYPE_XDR)
1114 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1118 WARN("invalid schema!\n");
1122 IXMLDOMNode_Release(domnode);
1126 cache_entry_add_ref(entry);
1134 xmlHashRemoveEntry(This->cache, name, cache_free);
1135 xmlHashAddEntry(This->cache, name, entry);
1142 return E_INVALIDARG;
1149 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1152 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1155 TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1160 name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1161 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1164 /* TODO: this should be read-only */
1166 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1172 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1174 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1175 xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1176 TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1178 xmlHashRemoveEntry(This->cache, name, cache_free);
1183 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1185 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1186 TRACE("(%p)->(%p)\n", This, length);
1190 *length = xmlHashSize(This->cache);
1194 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1196 cache_index_data* index_data = (cache_index_data*)index;
1198 if (index_data->index-- == 0)
1199 *index_data->out = bstr_from_xmlChar(name);
1202 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1203 LONG index, BSTR* len)
1205 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1206 cache_index_data data = {index,len};
1207 TRACE("(%p)->(%i, %p)\n", This, index, len);
1213 if (index >= xmlHashSize(This->cache))
1216 xmlHashScan(This->cache, cache_index, &data);
1220 static void cache_copy(void* data, void* dest, xmlChar* name)
1222 schema_cache* This = (schema_cache*) dest;
1223 cache_entry* entry = (cache_entry*) data;
1225 if (xmlHashLookup(This->cache, name) == NULL)
1227 cache_entry_add_ref(entry);
1228 xmlHashAddEntry(This->cache, name, entry);
1232 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1233 IXMLDOMSchemaCollection* otherCollection)
1235 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1236 schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1237 TRACE("(%p)->(%p)\n", This, That);
1239 if (!otherCollection)
1242 /* TODO: detect errors while copying & return E_FAIL */
1243 xmlHashScan(That->cache, cache_copy, This);
1248 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1257 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1263 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1264 VARIANT_BOOL validateOnLoad)
1270 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1271 VARIANT_BOOL* validateOnLoad)
1277 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1278 BSTR namespaceURI, ISchema** schema)
1286 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1287 IXMLDOMNode* node, ISchemaItem** item)
1295 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1297 schema_cache_QueryInterface,
1298 schema_cache_AddRef,
1299 schema_cache_Release,
1300 schema_cache_GetTypeInfoCount,
1301 schema_cache_GetTypeInfo,
1302 schema_cache_GetIDsOfNames,
1303 schema_cache_Invoke,
1306 schema_cache_remove,
1307 schema_cache_get_length,
1308 schema_cache_get_namespaceURI,
1309 schema_cache_addCollection,
1310 schema_cache_get__newEnum,
1311 schema_cache_validate,
1312 schema_cache_put_validateOnLoad,
1313 schema_cache_get_validateOnLoad,
1314 schema_cache_getSchema,
1315 schema_cache_getDeclaration
1318 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1320 xmlSchemaElementPtr decl = NULL;
1321 xmlChar const* nsURI = get_node_nsURI(node);
1323 TRACE("(%p, %p)\n", schema, node);
1325 if (xmlStrEqual(nsURI, schema->targetNamespace))
1326 decl = xmlHashLookup(schema->elemDecl, node->name);
1328 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1330 FIXME("declaration not found in main schema - need to check schema imports!\n");
1331 /*xmlSchemaImportPtr import;
1333 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1335 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1338 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1344 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1346 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1347 while (decl != NULL && decl->refDecl != NULL)
1348 decl = decl->refDecl;
1349 return (decl != NULL)? decl->node : NULL;
1352 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1354 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1355 xmlSchemaPtr schema;
1357 TRACE("(%p, %p)\n", This, tree);
1362 if (tree->type == XML_DOCUMENT_NODE)
1363 tree = xmlDocGetRootElement(tree->doc);
1365 schema = get_node_schema(This, tree);
1366 /* TODO: if the ns is not in the cache, and it's a URL,
1367 * do we try to load from that? */
1369 return Schema_validate_tree(schema, tree);
1371 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1376 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1378 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1379 xmlSchemaPtr schema = get_node_schema(This, node);
1380 XDR_DT dt = DT_INVALID;
1382 TRACE("(%p, %p)\n", This, node);
1384 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1386 dt = str_to_dt(node->name, -1);
1391 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1393 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1396 dt = str_to_dt(str, -1);
1404 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1406 schema_cache* This = heap_alloc(sizeof(schema_cache));
1408 return E_OUTOFMEMORY;
1410 This->lpVtbl = &schema_cache_vtbl;
1411 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1414 if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1415 This->version = MSXML3;
1416 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1417 This->version = MSXML4;
1418 else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1419 This->version = MSXML6;
1421 This->version = MSXML_DEFAULT;
1423 *ppObj = &This->lpVtbl;
1429 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1431 MESSAGE("This program tried to use a SchemaCache object, but\n"
1432 "libxml2 support was not present at compile time.\n");