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