wined3d: wined3d_device_set_viewport() never fails.
[wine] / dlls / msxml3 / schema.c
1 /*
2  * Schema cache implementation
3  *
4  * Copyright 2007 Huw Davies
5  * Copyright 2010 Adam Martinson for CodeWeavers
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #define COBJMACROS
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <stdarg.h>
28 #ifdef HAVE_LIBXML2
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>
39 #endif
40
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winuser.h"
44 #include "ole2.h"
45 #include "msxml6.h"
46
47 #include "wine/debug.h"
48
49 #include "msxml_private.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
52
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
56  */
57
58 /* This is just the number of buckets, should be prime */
59 #define DEFAULT_HASHTABLE_SIZE 17
60
61 #ifdef HAVE_LIBXML2
62
63 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
64
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";
70
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;
76
77 static const WCHAR      emptyW[] = {0};
78
79 /* Supported types:
80  * msxml3 - XDR only
81  * msxml4 - XDR & XSD
82  * msxml5 - XDR & XSD
83  * mxsml6 - XSD only
84  *
85  * CacheType_NS is a special type used for read-only collection build with
86  * IXMLDOMDocument2::namespaces()
87  */
88 typedef enum  {
89     CacheEntryType_Invalid,
90     CacheEntryType_XDR,
91     CacheEntryType_XSD,
92     CacheEntryType_NS
93 } CacheEntryType;
94
95 typedef struct
96 {
97     DispatchEx dispex;
98     IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface;
99     LONG ref;
100
101     MSXML_VERSION version;
102     xmlHashTablePtr cache;
103
104     VARIANT_BOOL validateOnLoad;
105     int read_only;
106 } schema_cache;
107
108 typedef struct
109 {
110     CacheEntryType type;
111     xmlSchemaPtr schema;
112     xmlDocPtr doc;
113     LONG ref;
114 } cache_entry;
115
116 typedef struct
117 {
118     LONG index;
119     BSTR* out;
120 } cache_index_data;
121
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
128
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";
165
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};
202
203 static const BYTE hash_assoc_values[] =
204 {
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
231 };
232
233 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
234 {
235     va_list ap;
236     va_start(ap, msg);
237     LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
238     va_end(ap);
239 }
240
241 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
242 {
243     va_list ap;
244     va_start(ap, msg);
245     LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
246     va_end(ap);
247 }
248
249 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
250 static void parser_serror(void* ctx, xmlErrorPtr err)
251 {
252     LIBXML2_CALLBACK_SERROR(Schema_parse, err);
253 }
254 #endif
255
256 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
257 {
258     TRACE("(%p)\n", spctx);
259
260     xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
261 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
262     xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
263 #endif
264
265     return xmlSchemaParse(spctx);
266 }
267
268 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
269 {
270     va_list ap;
271     va_start(ap, msg);
272     LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
273     va_end(ap);
274 }
275
276 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
277 {
278     va_list ap;
279     va_start(ap, msg);
280     LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
281     va_end(ap);
282 }
283
284 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
285 static void validate_serror(void* ctx, xmlErrorPtr err)
286 {
287     LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
288 }
289 #endif
290
291 static HRESULT schema_cache_get_item(IUnknown *iface, LONG index, VARIANT *item)
292 {
293     V_VT(item) = VT_BSTR;
294     return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2*)iface, index, &V_BSTR(item));
295 }
296
297 static const struct enumvariant_funcs schemacache_enumvariant = {
298     schema_cache_get_item,
299     NULL
300 };
301
302 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
303 {
304     xmlSchemaValidCtxtPtr svctx;
305     int err;
306
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);
314 #endif
315
316     if (tree->type == XML_DOCUMENT_NODE)
317         err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
318     else
319         err = xmlSchemaValidateOneElement(svctx, tree);
320
321     xmlSchemaFreeValidCtxt(svctx);
322     return err? S_FALSE : S_OK;
323 }
324
325 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
326 {
327     DWORD hval = (len == -1)? xmlStrlen(str) : len;
328
329     switch (hval)
330     {
331         default:
332             hval += hash_assoc_values[str[10]];
333             /*FALLTHROUGH*/
334         case 10:
335             hval += hash_assoc_values[str[9]];
336             /*FALLTHROUGH*/
337         case 9:
338             hval += hash_assoc_values[str[8]];
339             /*FALLTHROUGH*/
340         case 8:
341             hval += hash_assoc_values[str[7]];
342             /*FALLTHROUGH*/
343         case 7:
344             hval += hash_assoc_values[str[6]];
345             /*FALLTHROUGH*/
346         case 6:
347             hval += hash_assoc_values[str[5]];
348             /*FALLTHROUGH*/
349         case 5:
350             hval += hash_assoc_values[str[4]];
351             /*FALLTHROUGH*/
352         case 4:
353             hval += hash_assoc_values[str[3]];
354             /*FALLTHROUGH*/
355         case 3:
356             hval += hash_assoc_values[str[2]];
357             /*FALLTHROUGH*/
358         case 2:
359             hval += hash_assoc_values[str[1]];
360             /*FALLTHROUGH*/
361         case 1:
362             hval += hash_assoc_values[str[0]];
363             break;
364     }
365     return hval;
366 }
367
368 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
369 {
370     DWORD hval = (len == -1)? lstrlenW(bstr) : len;
371
372     switch (hval)
373     {
374         default:
375             hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
376             /*FALLTHROUGH*/
377         case 10:
378             hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
379             /*FALLTHROUGH*/
380         case 9:
381             hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
382             /*FALLTHROUGH*/
383         case 8:
384             hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
385             /*FALLTHROUGH*/
386         case 7:
387             hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
388             /*FALLTHROUGH*/
389         case 6:
390             hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
391             /*FALLTHROUGH*/
392         case 5:
393             hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
394             /*FALLTHROUGH*/
395         case 4:
396             hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
397             /*FALLTHROUGH*/
398         case 3:
399             hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
400             /*FALLTHROUGH*/
401         case 2:
402             hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
403             /*FALLTHROUGH*/
404         case 1:
405             hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
406             break;
407     }
408     return hval;
409 }
410
411 static const xmlChar *const DT_string_table[LAST_DT] =
412 {
413     DT_bin_base64,
414     DT_bin_hex,
415     DT_boolean,
416     DT_char,
417     DT_date,
418     DT_date_tz,
419     DT_dateTime,
420     DT_dateTime_tz,
421     DT_entity,
422     DT_entities,
423     DT_enumeration,
424     DT_fixed_14_4,
425     DT_float,
426     DT_i1,
427     DT_i2,
428     DT_i4,
429     DT_i8,
430     DT_id,
431     DT_idref,
432     DT_idrefs,
433     DT_int,
434     DT_nmtoken,
435     DT_nmtokens,
436     DT_notation,
437     DT_number,
438     DT_r4,
439     DT_r8,
440     DT_string,
441     DT_time,
442     DT_time_tz,
443     DT_ui1,
444     DT_ui2,
445     DT_ui4,
446     DT_ui8,
447     DT_uri,
448     DT_uuid
449 };
450
451 static const WCHAR *const DT_wstring_table[LAST_DT] =
452 {
453     wDT_bin_base64,
454     wDT_bin_hex,
455     wDT_boolean,
456     wDT_char,
457     wDT_date,
458     wDT_date_tz,
459     wDT_dateTime,
460     wDT_dateTime_tz,
461     wDT_entity,
462     wDT_entities,
463     wDT_enumeration,
464     wDT_fixed_14_4,
465     wDT_float,
466     wDT_i1,
467     wDT_i2,
468     wDT_i4,
469     wDT_i8,
470     wDT_id,
471     wDT_idref,
472     wDT_idrefs,
473     wDT_int,
474     wDT_nmtoken,
475     wDT_nmtokens,
476     wDT_notation,
477     wDT_number,
478     wDT_r4,
479     wDT_r8,
480     wDT_string,
481     wDT_time,
482     wDT_time_tz,
483     wDT_ui1,
484     wDT_ui2,
485     wDT_ui4,
486     wDT_ui8,
487     wDT_uri,
488     wDT_uuid
489 };
490
491 static const XDR_DT DT_lookup_table[] =
492 {
493     -1, -1,
494     DT_I8,
495     DT_UI8,
496     DT_TIME,
497     -1, -1,
498     DT_I4,
499     DT_UI4,
500     -1, -1, -1,
501     DT_R8,
502     DT_URI,
503     -1,
504     DT_FLOAT,
505     -1,
506     DT_R4,
507     DT_INT,
508     DT_CHAR,
509     -1,
510     DT_ENTITY,
511     DT_ID,
512     DT_ENTITIES,
513     DT_UUID,
514     -1, -1,
515     DT_TIME_TZ,
516     -1,
517     DT_DATE,
518     -1,
519     DT_NUMBER,
520     DT_BIN_HEX,
521     DT_DATETIME,
522     -1,
523     DT_IDREF,
524     DT_IDREFS,
525     DT_BOOLEAN,
526     -1, -1, -1,
527     DT_STRING,
528     DT_NMTOKEN,
529     DT_NMTOKENS,
530     -1,
531     DT_BIN_BASE64,
532     -1,
533     DT_I2,
534     DT_UI2,
535     -1, -1, -1,
536     DT_DATE_TZ,
537     DT_NOTATION,
538     -1, -1,
539     DT_DATETIME_TZ,
540     DT_I1,
541     DT_UI1,
542     -1, -1,
543     DT_ENUMERATION,
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,
550     DT_FIXED_14_4
551 };
552
553 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
554 {
555     DWORD hash = dt_hash(str, len);
556     XDR_DT dt = DT_INVALID;
557
558     if (hash <= DT_MAX_HASH_VALUE)
559         dt = DT_lookup_table[hash];
560
561     if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
562         return dt;
563
564     return DT_INVALID;
565 }
566
567 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
568 {
569     DWORD hash = dt_hash_bstr(bstr, len);
570     XDR_DT dt = DT_INVALID;
571
572     if (hash <= DT_MAX_HASH_VALUE)
573         dt = DT_lookup_table[hash];
574
575     if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
576         return dt;
577
578     return DT_INVALID;
579 }
580
581 xmlChar const* dt_to_str(XDR_DT dt)
582 {
583     if (dt == DT_INVALID)
584         return NULL;
585
586     return DT_string_table[dt];
587 }
588
589 OLECHAR const* dt_to_bstr(XDR_DT dt)
590 {
591     if (dt == DT_INVALID)
592         return NULL;
593
594     return DT_wstring_table[dt];
595 }
596
597 const char* debugstr_dt(XDR_DT dt)
598 {
599     return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL);
600 }
601
602 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
603 {
604     xmlDocPtr tmp_doc;
605     xmlNodePtr node;
606     xmlNsPtr ns;
607     HRESULT hr;
608
609     TRACE("(dt:%s, %s)\n", debugstr_dt(dt), debugstr_a((char const*)content));
610
611     if (!datatypes_schema)
612     {
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);
618     }
619
620     switch (dt)
621     {
622         case DT_INVALID:
623             return E_FAIL;
624         case DT_BIN_BASE64:
625         case DT_BIN_HEX:
626         case DT_BOOLEAN:
627         case DT_CHAR:
628         case DT_DATE:
629         case DT_DATE_TZ:
630         case DT_DATETIME:
631         case DT_DATETIME_TZ:
632         case DT_FIXED_14_4:
633         case DT_FLOAT:
634         case DT_I1:
635         case DT_I2:
636         case DT_I4:
637         case DT_I8:
638         case DT_INT:
639         case DT_NMTOKEN:
640         case DT_NMTOKENS:
641         case DT_NUMBER:
642         case DT_R4:
643         case DT_R8:
644         case DT_STRING:
645         case DT_TIME:
646         case DT_TIME_TZ:
647         case DT_UI1:
648         case DT_UI2:
649         case DT_UI4:
650         case DT_UI8:
651         case DT_URI:
652         case DT_UUID:
653             if (!datatypes_schema)
654             {
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");
657
658                 /* Hopefully they don't need much in the way of XDR datatypes support... */
659                 return S_OK;
660             }
661
662             if (content && xmlStrlen(content))
663             {
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");
667                 xmlSetNs(node, ns);
668                 xmlDocSetRootElement(tmp_doc, node);
669
670                 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
671                 xmlFreeDoc(tmp_doc);
672             }
673             else
674             {   /* probably the node is being created manually and has no content yet */
675                 hr = S_OK;
676             }
677             return hr;
678         default:
679             FIXME("need to handle dt:%s\n", debugstr_dt(dt));
680             return S_OK;
681     }
682 }
683
684 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
685 {
686     return (node->ns != NULL)? node->ns->href : NULL;
687 }
688
689 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
690 {
691     return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
692                      xmlHashLookup(This->cache, nsURI);
693 }
694
695 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
696 {
697     cache_entry* entry = get_entry(This, get_node_nsURI(node));
698     return (!entry)? NULL : entry->schema;
699 }
700
701 static xmlExternalEntityLoader _external_entity_loader;
702
703 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
704                                                 xmlParserCtxtPtr ctxt)
705 {
706     xmlParserInputPtr input;
707
708     TRACE("(%s %s %p)\n", debugstr_a(URL), debugstr_a(ID), ctxt);
709
710     assert(MSXML_hInstance != NULL);
711     assert(datatypes_rsrc != NULL);
712     assert(datatypes_handle != NULL);
713     assert(datatypes_src != NULL);
714
715     /* TODO: if the desired schema is in the cache, load it from there */
716     if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
717     {
718         TRACE("loading built-in schema for %s\n", URL);
719         input = xmlNewStringInputStream(ctxt, datatypes_src);
720     }
721     else
722     {
723         input = _external_entity_loader(URL, ID, ctxt);
724     }
725
726     return input;
727 }
728
729 void schemasInit(void)
730 {
731     int len;
732     xmlChar* buf;
733     if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
734     {
735         FIXME("failed to find resource for %s\n", DT_nsURI);
736         return;
737     }
738
739     if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
740     {
741         FIXME("failed to load resource for %s\n", DT_nsURI);
742         return;
743     }
744     buf = LockResource(datatypes_handle);
745     len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
746
747     /* Resource is loaded as raw data,
748      * need a null-terminated string */
749     while (buf[len] != '>')
750         buf[len--] = 0;
751     datatypes_src = buf;
752     datatypes_len = len + 1;
753
754     if (xmlGetExternalEntityLoader() != external_entity_loader)
755     {
756         _external_entity_loader = xmlGetExternalEntityLoader();
757         xmlSetExternalEntityLoader(external_entity_loader);
758     }
759 }
760
761 void schemasCleanup(void)
762 {
763     xmlSchemaFree(datatypes_schema);
764     xmlSetExternalEntityLoader(_external_entity_loader);
765 }
766
767 static LONG cache_entry_add_ref(cache_entry* entry)
768 {
769     LONG ref = InterlockedIncrement(&entry->ref);
770     TRACE("(%p)->(%d)\n", entry, ref);
771     return ref;
772 }
773
774 static LONG cache_entry_release(cache_entry* entry)
775 {
776     LONG ref = InterlockedDecrement(&entry->ref);
777     TRACE("(%p)->(%d)\n", entry, ref);
778
779     if (ref == 0)
780     {
781         if (entry->type == CacheEntryType_XSD)
782         {
783             xmldoc_release(entry->doc);
784             entry->schema->doc = NULL;
785             xmlSchemaFree(entry->schema);
786         }
787         else if (entry->type == CacheEntryType_XDR)
788         {
789             xmldoc_release(entry->doc);
790             xmldoc_release(entry->schema->doc);
791             entry->schema->doc = NULL;
792             xmlSchemaFree(entry->schema);
793         }
794
795         heap_free(entry);
796     }
797     return ref;
798 }
799
800 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl;
801
802 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
803 {
804     return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
805 }
806
807 static inline schema_cache* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection* iface)
808 {
809     return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
810 }
811
812 static inline schema_cache* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
813 {
814     return iface->lpVtbl == (void*)&XMLDOMSchemaCollection2Vtbl ? impl_from_IXMLDOMSchemaCollection(iface) : NULL;
815 }
816
817 static inline CacheEntryType cache_type_from_xmlDocPtr(xmlDocPtr schema)
818 {
819     xmlNodePtr root = NULL;
820     if (schema)
821         root = xmlDocGetRootElement(schema);
822     if (root && root->ns)
823     {
824
825         if (xmlStrEqual(root->name, XDR_schema) &&
826             xmlStrEqual(root->ns->href, XDR_nsURI))
827         {
828             return CacheEntryType_XDR;
829         }
830         else if (xmlStrEqual(root->name, XSD_schema) &&
831                  xmlStrEqual(root->ns->href, XSD_nsURI))
832         {
833             return CacheEntryType_XSD;
834         }
835     }
836     return CacheEntryType_Invalid;
837 }
838
839 static BOOL link_datatypes(xmlDocPtr schema)
840 {
841     xmlNodePtr root, next, child;
842     xmlNsPtr ns;
843
844     assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
845     root = xmlDocGetRootElement(schema);
846     if (!root)
847         return FALSE;
848
849     for (ns = root->nsDef; ns != NULL; ns = ns->next)
850     {
851         if (xmlStrEqual(ns->href, DT_nsURI))
852             break;
853     }
854
855     if (!ns)
856         return FALSE;
857
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);
863
864     return TRUE;
865 }
866
867 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
868 {
869     cache_entry* entry = heap_alloc(sizeof(cache_entry));
870     xmlSchemaParserCtxtPtr spctx;
871     xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
872
873     link_datatypes(new_doc);
874
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;
878     entry->ref = 0;
879     spctx = xmlSchemaNewDocParserCtxt(new_doc);
880
881     if ((entry->schema = Schema_parse(spctx)))
882     {
883         xmldoc_init(entry->schema->doc, v);
884         entry->doc = entry->schema->doc;
885         xmldoc_add_ref(entry->doc);
886     }
887     else
888     {
889         FIXME("failed to parse doc\n");
890         xmlFreeDoc(new_doc);
891         heap_free(entry);
892         entry = NULL;
893     }
894     xmlSchemaFreeParserCtxt(spctx);
895     return entry;
896 }
897
898 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version)
899 {
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);
903
904     link_datatypes(xsd_doc);
905
906     entry->type = CacheEntryType_XDR;
907     entry->ref = 0;
908     spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
909
910     if ((entry->schema = Schema_parse(spctx)))
911     {
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);
917     }
918     else
919     {
920         FIXME("failed to parse doc\n");
921         xmlFreeDoc(new_doc);
922         xmlFreeDoc(xsd_doc);
923         heap_free(entry);
924         entry = NULL;
925     }
926     xmlSchemaFreeParserCtxt(spctx);
927
928     return entry;
929 }
930
931 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version)
932 {
933     cache_entry* entry;
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;
939
940     if (hr != S_OK)
941     {
942         FIXME("failed to create domdoc\n");
943         return NULL;
944     }
945     assert(domdoc != NULL);
946     assert(V_VT(&url) == VT_BSTR);
947
948     hr = IXMLDOMDocument3_load(domdoc, url, &b);
949     if (hr != S_OK)
950     {
951         ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
952         if (b != VARIANT_TRUE)
953         {
954             FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url)));
955             IXMLDOMDocument3_Release(domdoc);
956             return NULL;
957         }
958     }
959     doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
960     type = cache_type_from_xmlDocPtr(doc);
961
962     switch (type)
963     {
964         case CacheEntryType_XSD:
965             entry = cache_entry_from_xsd_doc(doc, nsURI, version);
966             break;
967         case CacheEntryType_XDR:
968             entry = cache_entry_from_xdr_doc(doc, nsURI, version);
969             break;
970         default:
971             entry = NULL;
972             FIXME("invalid schema\n");
973             break;
974     }
975     IXMLDOMDocument3_Release(domdoc);
976
977     return entry;
978 }
979
980 static void cache_free(void* data, xmlChar* name /* ignored */)
981 {
982     cache_entry_release((cache_entry*)data);
983 }
984
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)
992 {
993     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
994     static const xmlChar query[] = "//*/namespace::*";
995     xmlXPathObjectPtr nodeset;
996     xmlXPathContextPtr ctxt;
997
998     This->read_only = 1;
999
1000     ctxt = xmlXPathNewContext(node->node->doc);
1001
1002     nodeset = xmlXPathEvalExpression(query, ctxt);
1003     xmlXPathFreeContext(ctxt);
1004
1005     if (nodeset)
1006     {
1007         int pos = 0, len = xmlXPathNodeSetGetLength(nodeset->nodesetval);
1008
1009         if (len == 0) return S_OK;
1010
1011         while (pos < len)
1012         {
1013             xmlNodePtr node = xmlXPathNodeSetItem(nodeset->nodesetval, pos);
1014             if (node->type == XML_NAMESPACE_DECL)
1015             {
1016                 static const xmlChar defns[] = "http://www.w3.org/XML/1998/namespace";
1017                 xmlNsPtr ns = (xmlNsPtr)node;
1018                 cache_entry *entry;
1019
1020                 /* filter out default uri */
1021                 if (xmlStrEqual(ns->href, defns))
1022                 {
1023                     pos++;
1024                     continue;
1025                 }
1026
1027                 entry = heap_alloc(sizeof(cache_entry));
1028                 entry->type = CacheEntryType_NS;
1029                 entry->ref = 1;
1030                 entry->schema = NULL;
1031                 entry->doc = NULL;
1032
1033                 xmlHashRemoveEntry(This->cache, ns->href, cache_free);
1034                 xmlHashAddEntry(This->cache, ns->href, entry);
1035             }
1036             pos++;
1037         }
1038
1039         xmlXPathFreeObject(nodeset);
1040     }
1041
1042     return S_OK;
1043 }
1044
1045 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
1046                                                   REFIID riid, void** ppvObject)
1047 {
1048     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1049
1050     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1051
1052     if ( IsEqualIID(riid, &IID_IUnknown) ||
1053          IsEqualIID(riid, &IID_IDispatch) ||
1054          IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
1055          IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
1056     {
1057         *ppvObject = iface;
1058     }
1059     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
1060     {
1061         return *ppvObject ? S_OK : E_NOINTERFACE;
1062     }
1063     else
1064     {
1065         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1066         *ppvObject = NULL;
1067         return E_NOINTERFACE;
1068     }
1069
1070     IXMLDOMSchemaCollection2_AddRef(iface);
1071
1072     return S_OK;
1073 }
1074
1075 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
1076 {
1077     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1078     LONG ref = InterlockedIncrement(&This->ref);
1079     TRACE("(%p)->(%d)\n", This, ref);
1080     return ref;
1081 }
1082
1083 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
1084 {
1085     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1086     LONG ref = InterlockedDecrement(&This->ref);
1087     TRACE("(%p)->(%d)\n", This, ref);
1088
1089     if (ref == 0)
1090     {
1091         xmlHashFree(This->cache, cache_free);
1092         release_dispex(&This->dispex);
1093         heap_free(This);
1094     }
1095
1096     return ref;
1097 }
1098
1099 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1100                                                     UINT* pctinfo)
1101 {
1102     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1103     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1104 }
1105
1106 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1107                                                UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1108 {
1109     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1110     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
1111         iTInfo, lcid, ppTInfo);
1112 }
1113
1114 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1115                                                  REFIID riid, LPOLESTR* rgszNames,
1116                                                  UINT cNames, LCID lcid, DISPID* rgDispId)
1117 {
1118     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1119     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1120         riid, rgszNames, cNames, lcid, rgDispId);
1121 }
1122
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,
1127                                           UINT* puArgErr)
1128 {
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);
1132 }
1133
1134 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1135 {
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));
1139
1140     if (This->read_only) return E_FAIL;
1141
1142     switch (V_VT(&var))
1143     {
1144         case VT_NULL:
1145             {
1146                 xmlHashRemoveEntry(This->cache, name, cache_free);
1147             }
1148             break;
1149
1150         case VT_BSTR:
1151             {
1152                 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1153
1154                 if (entry)
1155                 {
1156                     cache_entry_add_ref(entry);
1157                 }
1158                 else
1159                 {
1160                     heap_free(name);
1161                     return E_FAIL;
1162                 }
1163
1164                 xmlHashRemoveEntry(This->cache, name, cache_free);
1165                 xmlHashAddEntry(This->cache, name, entry);
1166             }
1167             break;
1168
1169         case VT_DISPATCH:
1170             {
1171                 xmlDocPtr doc = NULL;
1172                 cache_entry* entry;
1173                 CacheEntryType type;
1174                 IXMLDOMNode* domnode = NULL;
1175                 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1176
1177                 if (domnode)
1178                     doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1179
1180                 if (!doc)
1181                 {
1182                     IXMLDOMNode_Release(domnode);
1183                     heap_free(name);
1184                     return E_INVALIDARG;
1185                 }
1186                 type = cache_type_from_xmlDocPtr(doc);
1187
1188                 if (type == CacheEntryType_XSD)
1189                 {
1190                     entry = cache_entry_from_xsd_doc(doc, name, This->version);
1191                 }
1192                 else if (type == CacheEntryType_XDR)
1193                 {
1194                     entry = cache_entry_from_xdr_doc(doc, name, This->version);
1195                 }
1196                 else
1197                 {
1198                     WARN("invalid schema!\n");
1199                     entry = NULL;
1200                 }
1201
1202                 IXMLDOMNode_Release(domnode);
1203
1204                 if (entry)
1205                 {
1206                     cache_entry_add_ref(entry);
1207                 }
1208                 else
1209                 {
1210                     heap_free(name);
1211                     return E_FAIL;
1212                 }
1213
1214                 xmlHashRemoveEntry(This->cache, name, cache_free);
1215                 xmlHashAddEntry(This->cache, name, entry);
1216             }
1217             break;
1218
1219         default:
1220             {
1221                 heap_free(name);
1222                 return E_INVALIDARG;
1223             }
1224     }
1225     heap_free(name);
1226     return S_OK;
1227 }
1228
1229 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1230                                        IXMLDOMNode** node)
1231 {
1232     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1233     cache_entry* entry;
1234     xmlChar* name;
1235
1236     TRACE("(%p)->(%s %p)\n", This, debugstr_w(uri), node);
1237
1238     if (This->version == MSXML6) return E_NOTIMPL;
1239
1240     if (!node)
1241         return E_POINTER;
1242
1243     name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1244     entry = (cache_entry*) xmlHashLookup(This->cache, name);
1245     heap_free(name);
1246
1247     /* TODO: this should be read-only */
1248     if (entry && entry->doc)
1249         return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1250
1251     *node = NULL;
1252     return S_OK;
1253 }
1254
1255 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1256 {
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));
1260
1261     if (This->version == MSXML6) return E_NOTIMPL;
1262
1263     xmlHashRemoveEntry(This->cache, name, cache_free);
1264     heap_free(name);
1265     return S_OK;
1266 }
1267
1268 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1269 {
1270     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1271     TRACE("(%p)->(%p)\n", This, length);
1272
1273     if (!length)
1274         return E_POINTER;
1275     *length = xmlHashSize(This->cache);
1276     return S_OK;
1277 }
1278
1279 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1280 {
1281     cache_index_data* index_data = (cache_index_data*)index;
1282
1283     if (index_data->index-- == 0)
1284         *index_data->out = bstr_from_xmlChar(name);
1285 }
1286
1287 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1288                                                     LONG index, BSTR* len)
1289 {
1290     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1291     cache_index_data data = {index, len};
1292     TRACE("(%p)->(%i %p)\n", This, index, len);
1293
1294     if (!len)
1295         return E_POINTER;
1296
1297     if (index >= xmlHashSize(This->cache))
1298         return E_FAIL;
1299
1300     *len = NULL;
1301     xmlHashScan(This->cache, cache_index, &data);
1302     return S_OK;
1303 }
1304
1305 static void cache_copy(void* data, void* dest, xmlChar* name)
1306 {
1307     schema_cache* This = (schema_cache*) dest;
1308     cache_entry* entry = (cache_entry*) data;
1309
1310     if (xmlHashLookup(This->cache, name) == NULL)
1311     {
1312         cache_entry_add_ref(entry);
1313         xmlHashAddEntry(This->cache, name, entry);
1314     }
1315 }
1316
1317 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1318                                                  IXMLDOMSchemaCollection* collection)
1319 {
1320     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1321     schema_cache* That;
1322
1323     TRACE("(%p)->(%p)\n", This, collection);
1324
1325     if (!collection)
1326         return E_POINTER;
1327
1328     That = unsafe_impl_from_IXMLDOMSchemaCollection(collection);
1329     if (!That)
1330     {
1331         ERR("external collection implementation\n");
1332         return E_FAIL;
1333     }
1334
1335     /* TODO: detect errors while copying & return E_FAIL */
1336     xmlHashScan(That->cache, cache_copy, This);
1337
1338     return S_OK;
1339 }
1340
1341 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface, IUnknown** enumv)
1342 {
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);
1346 }
1347
1348 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1349 {
1350     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1351     FIXME("(%p): stub\n", This);
1352     return E_NOTIMPL;
1353 }
1354
1355 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1356                                                       VARIANT_BOOL value)
1357 {
1358     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1359     FIXME("(%p)->(%d): stub\n", This, value);
1360
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;
1364
1365     return E_NOTIMPL;
1366 }
1367
1368 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1369                                                       VARIANT_BOOL* value)
1370 {
1371     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1372     TRACE("(%p)->(%p)\n", This, value);
1373
1374     if (!value) return E_POINTER;
1375     *value = This->validateOnLoad;
1376
1377     return S_OK;
1378 }
1379
1380 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1381                                              BSTR namespaceURI, ISchema** schema)
1382 {
1383     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1384     FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(namespaceURI), schema);
1385     if (schema)
1386         *schema = NULL;
1387     return E_NOTIMPL;
1388 }
1389
1390 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1391                                                   IXMLDOMNode* node, ISchemaItem** item)
1392 {
1393     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1394     FIXME("(%p)->(%p %p): stub\n", This, node, item);
1395     if (item)
1396         *item = NULL;
1397     return E_NOTIMPL;
1398 }
1399
1400 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl =
1401 {
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,
1409     schema_cache_add,
1410     schema_cache_get,
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
1421 };
1422
1423 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1424 {
1425     xmlSchemaElementPtr decl = NULL;
1426     xmlChar const* nsURI = get_node_nsURI(node);
1427
1428     TRACE("(%p, %p)\n", schema, node);
1429
1430     if (xmlStrEqual(nsURI, schema->targetNamespace))
1431         decl = xmlHashLookup(schema->elemDecl, node->name);
1432
1433     if (!decl && xmlHashSize(schema->schemasImports) > 1)
1434     {
1435         FIXME("declaration not found in main schema - need to check schema imports!\n");
1436         /*xmlSchemaImportPtr import;
1437         if (nsURI == NULL)
1438             import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1439         else
1440             import = xmlHashLookup(schema->schemasImports, node->ns->href);
1441
1442         if (import != NULL)
1443             decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1444     }
1445
1446     return decl;
1447 }
1448
1449 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1450 {
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;
1455 }
1456
1457 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1458 {
1459     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1460     xmlSchemaPtr schema;
1461
1462     TRACE("(%p, %p)\n", This, tree);
1463
1464     if (!tree)
1465         return E_POINTER;
1466
1467     if (tree->type == XML_DOCUMENT_NODE)
1468         tree = xmlDocGetRootElement(tree->doc);
1469
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? */
1473     if (schema)
1474         return Schema_validate_tree(schema, tree);
1475     else
1476         WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1477
1478     return E_FAIL;
1479 }
1480
1481 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1482 {
1483     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1484     xmlSchemaPtr schema = get_node_schema(This, node);
1485     XDR_DT dt = DT_INVALID;
1486
1487     TRACE("(%p, %p)\n", This, node);
1488
1489     if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1490     {
1491         dt = str_to_dt(node->name, -1);
1492     }
1493     else if (schema)
1494     {
1495         xmlChar* str;
1496         xmlNodePtr schema_node = lookup_schema_element(schema, node);
1497
1498         str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1499         if (str)
1500         {
1501             dt = str_to_dt(str, -1);
1502             xmlFree(str);
1503         }
1504     }
1505
1506     return dt;
1507 }
1508
1509 static const tid_t schemacache_iface_tids[] = {
1510     IXMLDOMSchemaCollection2_tid,
1511     0
1512 };
1513
1514 static dispex_static_data_t schemacache_dispex = {
1515     NULL,
1516     IXMLDOMSchemaCollection2_tid,
1517     NULL,
1518     schemacache_iface_tids
1519 };
1520
1521 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1522 {
1523     schema_cache* This = heap_alloc(sizeof(schema_cache));
1524     if (!This)
1525         return E_OUTOFMEMORY;
1526
1527     TRACE("(%d %p %p)\n", version, outer, obj);
1528
1529     This->IXMLDOMSchemaCollection2_iface.lpVtbl = &XMLDOMSchemaCollection2Vtbl;
1530     This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1531     This->ref = 1;
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);
1536
1537     *obj = &This->IXMLDOMSchemaCollection2_iface;
1538     return S_OK;
1539 }
1540
1541 #else
1542
1543 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1544 {
1545     MESSAGE("This program tried to use a SchemaCache object, but\n"
1546             "libxml2 support was not present at compile time.\n");
1547     return E_NOTIMPL;
1548 }
1549
1550 #endif