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