mshtml: Reimplement IHTMLStyle::get_backgroundPositionY using background-position...
[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         return E_NOINTERFACE;
952     }
953
954     IXMLDOMSchemaCollection2_AddRef(iface);
955
956     return S_OK;
957 }
958
959 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
960 {
961     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
962     LONG ref = InterlockedIncrement(&This->ref);
963     TRACE("%p new ref %d\n", This, ref);
964     return ref;
965 }
966
967 static void cache_free(void* data, xmlChar* name /* ignored */)
968 {
969     cache_entry_release((cache_entry*)data);
970 }
971
972 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
973 {
974     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
975     LONG ref = InterlockedDecrement(&This->ref);
976     TRACE("%p new ref %d\n", This, ref);
977
978     if (ref == 0)
979     {
980         xmlHashFree(This->cache, cache_free);
981         heap_free(This);
982     }
983
984     return ref;
985 }
986
987 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
988                                                     UINT* pctinfo)
989 {
990     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
991
992     TRACE("(%p)->(%p)\n", This, pctinfo);
993
994     *pctinfo = 1;
995
996     return S_OK;
997 }
998
999 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1000                                                UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1001 {
1002     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1003     HRESULT hr;
1004
1005     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1006
1007     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
1008
1009     return hr;
1010 }
1011
1012 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1013                                                  REFIID riid, LPOLESTR* rgszNames,
1014                                                  UINT cNames, LCID lcid, DISPID* rgDispId)
1015 {
1016     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1017     ITypeInfo* typeinfo;
1018     HRESULT hr;
1019
1020     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1021           lcid, rgDispId);
1022
1023     if(!rgszNames || cNames == 0 || !rgDispId)
1024         return E_INVALIDARG;
1025
1026     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1027     if(SUCCEEDED(hr))
1028     {
1029         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1030         ITypeInfo_Release(typeinfo);
1031     }
1032
1033     return hr;
1034 }
1035
1036 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1037                                           DISPID dispIdMember, REFIID riid, LCID lcid,
1038                                           WORD wFlags, DISPPARAMS* pDispParams,
1039                                           VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1040                                           UINT* puArgErr)
1041 {
1042     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1043     ITypeInfo* typeinfo;
1044     HRESULT hr;
1045
1046     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1047           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1048
1049     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
1050     if(SUCCEEDED(hr))
1051     {
1052         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
1053                 pVarResult, pExcepInfo, puArgErr);
1054         ITypeInfo_Release(typeinfo);
1055     }
1056
1057     return hr;
1058 }
1059
1060 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1061 {
1062     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1063     xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1064     TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1065
1066     switch (V_VT(&var))
1067     {
1068         case VT_NULL:
1069             {
1070                 xmlHashRemoveEntry(This->cache, name, cache_free);
1071             }
1072             break;
1073
1074         case VT_BSTR:
1075             {
1076                 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1077
1078                 if (entry)
1079                 {
1080                     cache_entry_add_ref(entry);
1081                 }
1082                 else
1083                 {
1084                     heap_free(name);
1085                     return E_FAIL;
1086                 }
1087
1088                 xmlHashRemoveEntry(This->cache, name, cache_free);
1089                 xmlHashAddEntry(This->cache, name, entry);
1090             }
1091             break;
1092
1093         case VT_DISPATCH:
1094             {
1095                 xmlDocPtr doc = NULL;
1096                 cache_entry* entry;
1097                 SCHEMA_TYPE type;
1098                 IXMLDOMNode* domnode = NULL;
1099                 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1100
1101                 if (domnode)
1102                     doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1103
1104                 if (!doc)
1105                 {
1106                     IXMLDOMNode_Release(domnode);
1107                     heap_free(name);
1108                     return E_INVALIDARG;
1109                 }
1110                 type = schema_type_from_xmlDocPtr(doc);
1111
1112                 if (type == SCHEMA_TYPE_XSD)
1113                 {
1114                     entry = cache_entry_from_xsd_doc(doc, name, This->version);
1115                 }
1116                 else if (type == SCHEMA_TYPE_XDR)
1117                 {
1118                     entry = cache_entry_from_xdr_doc(doc, name, This->version);
1119                 }
1120                 else
1121                 {
1122                     WARN("invalid schema!\n");
1123                     entry = NULL;
1124                 }
1125
1126                 IXMLDOMNode_Release(domnode);
1127
1128                 if (entry)
1129                 {
1130                     cache_entry_add_ref(entry);
1131                 }
1132                 else
1133                 {
1134                     heap_free(name);
1135                     return E_FAIL;
1136                 }
1137
1138                 xmlHashRemoveEntry(This->cache, name, cache_free);
1139                 xmlHashAddEntry(This->cache, name, entry);
1140             }
1141             break;
1142
1143         default:
1144             {
1145                 heap_free(name);
1146                 return E_INVALIDARG;
1147             }
1148     }
1149     heap_free(name);
1150     return S_OK;
1151 }
1152
1153 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1154                                        IXMLDOMNode** node)
1155 {
1156     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1157     xmlChar* name;
1158     cache_entry* entry;
1159     TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
1160
1161     if (!node)
1162         return E_POINTER;
1163
1164     name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1165     entry = (cache_entry*) xmlHashLookup(This->cache, name);
1166     heap_free(name);
1167
1168     /* TODO: this should be read-only */
1169     if (entry)
1170         return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1171
1172     *node = NULL;
1173     return S_OK;
1174 }
1175
1176 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1177 {
1178     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1179     xmlChar* name = uri ? xmlChar_from_wchar(uri) : xmlChar_from_wchar(emptyW);
1180     TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
1181
1182     xmlHashRemoveEntry(This->cache, name, cache_free);
1183     heap_free(name);
1184     return S_OK;
1185 }
1186
1187 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1188 {
1189     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1190     TRACE("(%p)->(%p)\n", This, length);
1191
1192     if (!length)
1193         return E_POINTER;
1194     *length = xmlHashSize(This->cache);
1195     return S_OK;
1196 }
1197
1198 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
1199 {
1200     cache_index_data* index_data = (cache_index_data*)index;
1201
1202     if (index_data->index-- == 0)
1203         *index_data->out = bstr_from_xmlChar(name);
1204 }
1205
1206 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1207                                                     LONG index, BSTR* len)
1208 {
1209     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1210     cache_index_data data = {index,len};
1211     TRACE("(%p)->(%i, %p)\n", This, index, len);
1212
1213     if (!len)
1214         return E_POINTER;
1215     *len = NULL;
1216
1217     if (index >= xmlHashSize(This->cache))
1218         return E_FAIL;
1219
1220     xmlHashScan(This->cache, cache_index, &data);
1221     return S_OK;
1222 }
1223
1224 static void cache_copy(void* data, void* dest, xmlChar* name)
1225 {
1226     schema_cache* This = (schema_cache*) dest;
1227     cache_entry* entry = (cache_entry*) data;
1228
1229     if (xmlHashLookup(This->cache, name) == NULL)
1230     {
1231         cache_entry_add_ref(entry);
1232         xmlHashAddEntry(This->cache, name, entry);
1233     }
1234 }
1235
1236 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1237                                                  IXMLDOMSchemaCollection* otherCollection)
1238 {
1239     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1240     schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
1241     TRACE("(%p)->(%p)\n", This, That);
1242
1243     if (!otherCollection)
1244         return E_POINTER;
1245
1246     /* TODO: detect errors while copying & return E_FAIL */
1247     xmlHashScan(That->cache, cache_copy, This);
1248
1249     return S_OK;
1250 }
1251
1252 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
1253                                                 IUnknown** ppUnk)
1254 {
1255     FIXME("stub\n");
1256     if (ppUnk)
1257         *ppUnk = NULL;
1258     return E_NOTIMPL;
1259 }
1260
1261 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1262 {
1263     FIXME("stub\n");
1264     return E_NOTIMPL;
1265 }
1266
1267 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1268                                                       VARIANT_BOOL validateOnLoad)
1269 {
1270     FIXME("stub\n");
1271     return E_NOTIMPL;
1272 }
1273
1274 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1275                                                       VARIANT_BOOL* validateOnLoad)
1276 {
1277     FIXME("stub\n");
1278     return E_NOTIMPL;
1279 }
1280
1281 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1282                                              BSTR namespaceURI, ISchema** schema)
1283 {
1284     FIXME("stub\n");
1285     if (schema)
1286         *schema = NULL;
1287     return E_NOTIMPL;
1288 }
1289
1290 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1291                                                   IXMLDOMNode* node, ISchemaItem** item)
1292 {
1293     FIXME("stub\n");
1294     if (item)
1295         *item = NULL;
1296     return E_NOTIMPL;
1297 }
1298
1299 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
1300 {
1301     schema_cache_QueryInterface,
1302     schema_cache_AddRef,
1303     schema_cache_Release,
1304     schema_cache_GetTypeInfoCount,
1305     schema_cache_GetTypeInfo,
1306     schema_cache_GetIDsOfNames,
1307     schema_cache_Invoke,
1308     schema_cache_add,
1309     schema_cache_get,
1310     schema_cache_remove,
1311     schema_cache_get_length,
1312     schema_cache_get_namespaceURI,
1313     schema_cache_addCollection,
1314     schema_cache_get__newEnum,
1315     schema_cache_validate,
1316     schema_cache_put_validateOnLoad,
1317     schema_cache_get_validateOnLoad,
1318     schema_cache_getSchema,
1319     schema_cache_getDeclaration
1320 };
1321
1322 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1323 {
1324     xmlSchemaElementPtr decl = NULL;
1325     xmlChar const* nsURI = get_node_nsURI(node);
1326
1327     TRACE("(%p, %p)\n", schema, node);
1328
1329     if (xmlStrEqual(nsURI, schema->targetNamespace))
1330         decl = xmlHashLookup(schema->elemDecl, node->name);
1331
1332     if (!decl && xmlHashSize(schema->schemasImports) > 1)
1333     {
1334         FIXME("declaration not found in main schema - need to check schema imports!\n");
1335         /*xmlSchemaImportPtr import;
1336         if (nsURI == NULL)
1337             import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1338         else
1339             import = xmlHashLookup(schema->schemasImports, node->ns->href);
1340
1341         if (import != NULL)
1342             decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1343     }
1344
1345     return decl;
1346 }
1347
1348 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1349 {
1350     xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1351     while (decl != NULL && decl->refDecl != NULL)
1352         decl = decl->refDecl;
1353     return (decl != NULL)? decl->node : NULL;
1354 }
1355
1356 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1357 {
1358     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1359     xmlSchemaPtr schema;
1360
1361     TRACE("(%p, %p)\n", This, tree);
1362
1363     if (!tree)
1364         return E_POINTER;
1365
1366     if (tree->type == XML_DOCUMENT_NODE)
1367         tree = xmlDocGetRootElement(tree->doc);
1368
1369     schema = get_node_schema(This, tree);
1370     /* TODO: if the ns is not in the cache, and it's a URL,
1371      *       do we try to load from that? */
1372     if (schema)
1373         return Schema_validate_tree(schema, tree);
1374     else
1375         WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1376
1377     return E_FAIL;
1378 }
1379
1380 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1381 {
1382     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1383     xmlSchemaPtr schema = get_node_schema(This, node);
1384     XDR_DT dt = DT_INVALID;
1385
1386     TRACE("(%p, %p)\n", This, node);
1387
1388     if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1389     {
1390         dt = str_to_dt(node->name, -1);
1391     }
1392     else if (schema)
1393     {
1394         xmlChar* str;
1395         xmlNodePtr schema_node = lookup_schema_element(schema, node);
1396
1397         str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1398         if (str)
1399         {
1400             dt = str_to_dt(str, -1);
1401             xmlFree(str);
1402         }
1403     }
1404
1405     return dt;
1406 }
1407
1408 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1409 {
1410     schema_cache* This = heap_alloc(sizeof(schema_cache));
1411     if (!This)
1412         return E_OUTOFMEMORY;
1413
1414     This->lpVtbl = &schema_cache_vtbl;
1415     This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1416     This->ref = 1;
1417
1418     if (IsEqualCLSID(clsid, &CLSID_XMLSchemaCache30))
1419         This->version = MSXML3;
1420     else if (IsEqualCLSID(clsid, &CLSID_DOMDocument40))
1421         This->version = MSXML4;
1422     else if (IsEqualCLSID(clsid, &CLSID_DOMDocument60))
1423         This->version = MSXML6;
1424     else
1425         This->version = MSXML_DEFAULT;
1426
1427     *ppObj = &This->lpVtbl;
1428     return S_OK;
1429 }
1430
1431 #else
1432
1433 HRESULT SchemaCache_create(const GUID *clsid, IUnknown* pUnkOuter, void** ppObj)
1434 {
1435     MESSAGE("This program tried to use a SchemaCache object, but\n"
1436             "libxml2 support was not present at compile time.\n");
1437     return E_NOTIMPL;
1438 }
1439
1440 #endif