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