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