Release 1.6-rc2.
[wine/testsucceed.git] / dlls / msxml3 / schema.c
blob1c11349cef7b2284924fb771256152e40fd26f59
1 /*
2 * Schema cache implementation
4 * Copyright 2007 Huw Davies
5 * Copyright 2010 Adam Martinson for CodeWeavers
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.
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.
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
22 #define COBJMACROS
24 #include "config.h"
26 #include <assert.h>
27 #include <stdarg.h>
28 #ifdef HAVE_LIBXML2
29 # include <libxml/xmlerror.h>
30 # include <libxml/tree.h>
31 # include <libxml/xmlschemas.h>
32 # include <libxml/schemasInternals.h>
33 # include <libxml/hash.h>
34 # include <libxml/parser.h>
35 # include <libxml/parserInternals.h>
36 # include <libxml/xmlIO.h>
37 # include <libxml/xmlversion.h>
38 # include <libxml/xpath.h>
39 #endif
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winuser.h"
44 #include "ole2.h"
45 #include "msxml6.h"
47 #include "wine/debug.h"
49 #include "msxml_private.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
53 /* We use a chained hashtable, which can hold any number of schemas
54 * TODO: grow/shrink hashtable depending on load factor
55 * TODO: implement read-only where appropriate
58 /* This is just the number of buckets, should be prime */
59 #define DEFAULT_HASHTABLE_SIZE 17
61 #ifdef HAVE_LIBXML2
63 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
65 static const xmlChar XSD_schema[] = "schema";
66 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
67 static const xmlChar XDR_schema[] = "Schema";
68 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
69 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
71 static xmlChar * datatypes_src;
72 static int datatypes_len;
73 static HGLOBAL datatypes_handle;
74 static HRSRC datatypes_rsrc;
75 static xmlSchemaPtr datatypes_schema;
77 static const WCHAR emptyW[] = {0};
79 /* Supported types:
80 * msxml3 - XDR only
81 * msxml4 - XDR & XSD
82 * msxml5 - XDR & XSD
83 * mxsml6 - XSD only
85 * CacheType_NS is a special type used for read-only collection build with
86 * IXMLDOMDocument2::namespaces()
88 typedef enum {
89 CacheEntryType_Invalid,
90 CacheEntryType_XDR,
91 CacheEntryType_XSD,
92 CacheEntryType_NS
93 } CacheEntryType;
95 typedef struct
97 DispatchEx dispex;
98 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface;
99 LONG ref;
101 MSXML_VERSION version;
102 xmlHashTablePtr cache;
103 xmlChar **uris;
104 int allocated;
105 int count;
107 VARIANT_BOOL validateOnLoad;
108 int read_only;
109 } schema_cache;
111 typedef struct
113 CacheEntryType type;
114 xmlSchemaPtr schema;
115 xmlDocPtr doc;
116 LONG ref;
117 } cache_entry;
119 /* datatypes lookup stuff
120 * generated with help from gperf */
121 #define DT_MIN_STR_LEN 2
122 #define DT_MAX_STR_LEN 11
123 #define DT_MIN_HASH_VALUE 2
124 #define DT_MAX_HASH_VALUE 115
126 static const xmlChar DT_bin_base64[] = "bin.base64";
127 static const xmlChar DT_bin_hex[] = "bin.hex";
128 static const xmlChar DT_boolean[] = "boolean";
129 static const xmlChar DT_char[] = "char";
130 static const xmlChar DT_date[] = "date";
131 static const xmlChar DT_date_tz[] = "date.tz";
132 static const xmlChar DT_dateTime[] = "dateTime";
133 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
134 static const xmlChar DT_entity[] = "entity";
135 static const xmlChar DT_entities[] = "entities";
136 static const xmlChar DT_enumeration[] = "enumeration";
137 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
138 static const xmlChar DT_float[] = "float";
139 static const xmlChar DT_i1[] = "i1";
140 static const xmlChar DT_i2[] = "i2";
141 static const xmlChar DT_i4[] = "i4";
142 static const xmlChar DT_i8[] = "i8";
143 static const xmlChar DT_id[] = "id";
144 static const xmlChar DT_idref[] = "idref";
145 static const xmlChar DT_idrefs[] = "idrefs";
146 static const xmlChar DT_int[] = "int";
147 static const xmlChar DT_nmtoken[] = "nmtoken";
148 static const xmlChar DT_nmtokens[] = "nmtokens";
149 static const xmlChar DT_notation[] = "notation";
150 static const xmlChar DT_number[] = "number";
151 static const xmlChar DT_r4[] = "r4";
152 static const xmlChar DT_r8[] = "r8";
153 static const xmlChar DT_string[] = "string";
154 static const xmlChar DT_time[] = "time";
155 static const xmlChar DT_time_tz[] = "time.tz";
156 static const xmlChar DT_ui1[] = "ui1";
157 static const xmlChar DT_ui2[] = "ui2";
158 static const xmlChar DT_ui4[] = "ui4";
159 static const xmlChar DT_ui8[] = "ui8";
160 static const xmlChar DT_uri[] = "uri";
161 static const xmlChar DT_uuid[] = "uuid";
163 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
164 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
165 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
166 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
167 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
168 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
169 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
170 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
171 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
172 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
173 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
174 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
175 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
176 static const OLECHAR wDT_i1[] = {'i','1',0};
177 static const OLECHAR wDT_i2[] = {'i','2',0};
178 static const OLECHAR wDT_i4[] = {'i','4',0};
179 static const OLECHAR wDT_i8[] = {'i','8',0};
180 static const OLECHAR wDT_id[] = {'i','d',0};
181 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
182 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
183 static const OLECHAR wDT_int[] = {'i','n','t',0};
184 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
185 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
186 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
187 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
188 static const OLECHAR wDT_r4[] = {'r','4',0};
189 static const OLECHAR wDT_r8[] = {'r','8',0};
190 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
191 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
192 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
193 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
194 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
195 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
196 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
197 static const OLECHAR wDT_uri[] = {'u','r','i',0};
198 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
200 static const BYTE hash_assoc_values[] =
202 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
203 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
206 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
207 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
208 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
209 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
210 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
211 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
212 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
213 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
214 0, 0, 10, 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, 116, 116, 116, 116,
221 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
222 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
223 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
224 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
225 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
226 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
227 116, 116, 116, 116, 116, 116
230 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
232 va_list ap;
233 va_start(ap, msg);
234 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
235 va_end(ap);
238 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
240 va_list ap;
241 va_start(ap, msg);
242 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
243 va_end(ap);
246 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
247 static void parser_serror(void* ctx, xmlErrorPtr err)
249 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
251 #endif
253 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
255 TRACE("(%p)\n", spctx);
257 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
258 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
259 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
260 #endif
262 return xmlSchemaParse(spctx);
265 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
267 va_list ap;
268 va_start(ap, msg);
269 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
270 va_end(ap);
273 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
275 va_list ap;
276 va_start(ap, msg);
277 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
278 va_end(ap);
281 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
282 static void validate_serror(void* ctx, xmlErrorPtr err)
284 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
286 #endif
288 static HRESULT schema_cache_get_item(IUnknown *iface, LONG index, VARIANT *item)
290 V_VT(item) = VT_BSTR;
291 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2*)iface, index, &V_BSTR(item));
294 static const struct enumvariant_funcs schemacache_enumvariant = {
295 schema_cache_get_item,
296 NULL
299 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
301 xmlSchemaValidCtxtPtr svctx;
302 int err;
304 TRACE("(%p, %p)\n", schema, tree);
305 /* TODO: if validateOnLoad property is false,
306 * we probably need to validate the schema here. */
307 svctx = xmlSchemaNewValidCtxt(schema);
308 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
309 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
310 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
311 #endif
313 if (tree->type == XML_DOCUMENT_NODE)
314 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
315 else
316 err = xmlSchemaValidateOneElement(svctx, tree);
318 xmlSchemaFreeValidCtxt(svctx);
319 return err? S_FALSE : S_OK;
322 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
324 DWORD hval = (len == -1)? xmlStrlen(str) : len;
326 switch (hval)
328 default:
329 hval += hash_assoc_values[str[10]];
330 /*FALLTHROUGH*/
331 case 10:
332 hval += hash_assoc_values[str[9]];
333 /*FALLTHROUGH*/
334 case 9:
335 hval += hash_assoc_values[str[8]];
336 /*FALLTHROUGH*/
337 case 8:
338 hval += hash_assoc_values[str[7]];
339 /*FALLTHROUGH*/
340 case 7:
341 hval += hash_assoc_values[str[6]];
342 /*FALLTHROUGH*/
343 case 6:
344 hval += hash_assoc_values[str[5]];
345 /*FALLTHROUGH*/
346 case 5:
347 hval += hash_assoc_values[str[4]];
348 /*FALLTHROUGH*/
349 case 4:
350 hval += hash_assoc_values[str[3]];
351 /*FALLTHROUGH*/
352 case 3:
353 hval += hash_assoc_values[str[2]];
354 /*FALLTHROUGH*/
355 case 2:
356 hval += hash_assoc_values[str[1]];
357 /*FALLTHROUGH*/
358 case 1:
359 hval += hash_assoc_values[str[0]];
360 break;
362 return hval;
365 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
367 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
369 switch (hval)
371 default:
372 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
373 /*FALLTHROUGH*/
374 case 10:
375 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
376 /*FALLTHROUGH*/
377 case 9:
378 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
379 /*FALLTHROUGH*/
380 case 8:
381 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
382 /*FALLTHROUGH*/
383 case 7:
384 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
385 /*FALLTHROUGH*/
386 case 6:
387 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
388 /*FALLTHROUGH*/
389 case 5:
390 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
391 /*FALLTHROUGH*/
392 case 4:
393 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
394 /*FALLTHROUGH*/
395 case 3:
396 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
397 /*FALLTHROUGH*/
398 case 2:
399 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
400 /*FALLTHROUGH*/
401 case 1:
402 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
403 break;
405 return hval;
408 static const xmlChar *const DT_string_table[LAST_DT] =
410 DT_bin_base64,
411 DT_bin_hex,
412 DT_boolean,
413 DT_char,
414 DT_date,
415 DT_date_tz,
416 DT_dateTime,
417 DT_dateTime_tz,
418 DT_entity,
419 DT_entities,
420 DT_enumeration,
421 DT_fixed_14_4,
422 DT_float,
423 DT_i1,
424 DT_i2,
425 DT_i4,
426 DT_i8,
427 DT_id,
428 DT_idref,
429 DT_idrefs,
430 DT_int,
431 DT_nmtoken,
432 DT_nmtokens,
433 DT_notation,
434 DT_number,
435 DT_r4,
436 DT_r8,
437 DT_string,
438 DT_time,
439 DT_time_tz,
440 DT_ui1,
441 DT_ui2,
442 DT_ui4,
443 DT_ui8,
444 DT_uri,
445 DT_uuid
448 static const WCHAR *const DT_wstring_table[LAST_DT] =
450 wDT_bin_base64,
451 wDT_bin_hex,
452 wDT_boolean,
453 wDT_char,
454 wDT_date,
455 wDT_date_tz,
456 wDT_dateTime,
457 wDT_dateTime_tz,
458 wDT_entity,
459 wDT_entities,
460 wDT_enumeration,
461 wDT_fixed_14_4,
462 wDT_float,
463 wDT_i1,
464 wDT_i2,
465 wDT_i4,
466 wDT_i8,
467 wDT_id,
468 wDT_idref,
469 wDT_idrefs,
470 wDT_int,
471 wDT_nmtoken,
472 wDT_nmtokens,
473 wDT_notation,
474 wDT_number,
475 wDT_r4,
476 wDT_r8,
477 wDT_string,
478 wDT_time,
479 wDT_time_tz,
480 wDT_ui1,
481 wDT_ui2,
482 wDT_ui4,
483 wDT_ui8,
484 wDT_uri,
485 wDT_uuid
488 static const XDR_DT DT_lookup_table[] =
490 -1, -1,
491 DT_I8,
492 DT_UI8,
493 DT_TIME,
494 -1, -1,
495 DT_I4,
496 DT_UI4,
497 -1, -1, -1,
498 DT_R8,
499 DT_URI,
501 DT_FLOAT,
503 DT_R4,
504 DT_INT,
505 DT_CHAR,
507 DT_ENTITY,
508 DT_ID,
509 DT_ENTITIES,
510 DT_UUID,
511 -1, -1,
512 DT_TIME_TZ,
514 DT_DATE,
516 DT_NUMBER,
517 DT_BIN_HEX,
518 DT_DATETIME,
520 DT_IDREF,
521 DT_IDREFS,
522 DT_BOOLEAN,
523 -1, -1, -1,
524 DT_STRING,
525 DT_NMTOKEN,
526 DT_NMTOKENS,
528 DT_BIN_BASE64,
530 DT_I2,
531 DT_UI2,
532 -1, -1, -1,
533 DT_DATE_TZ,
534 DT_NOTATION,
535 -1, -1,
536 DT_DATETIME_TZ,
537 DT_I1,
538 DT_UI1,
539 -1, -1,
540 DT_ENUMERATION,
541 -1, -1, -1, -1, -1, -1, -1, -1, -1,
542 -1, -1, -1, -1, -1, -1, -1, -1, -1,
543 -1, -1, -1, -1, -1, -1, -1, -1, -1,
544 -1, -1, -1, -1, -1, -1, -1, -1, -1,
545 -1, -1, -1, -1, -1, -1, -1, -1, -1,
546 -1, -1, -1, -1, -1, -1, -1, -1,
547 DT_FIXED_14_4
550 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
552 DWORD hash = dt_hash(str, len);
553 XDR_DT dt = DT_INVALID;
555 if (hash <= DT_MAX_HASH_VALUE)
556 dt = DT_lookup_table[hash];
558 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
559 return dt;
561 return DT_INVALID;
564 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
566 DWORD hash = dt_hash_bstr(bstr, len);
567 XDR_DT dt = DT_INVALID;
569 if (hash <= DT_MAX_HASH_VALUE)
570 dt = DT_lookup_table[hash];
572 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
573 return dt;
575 return DT_INVALID;
578 xmlChar const* dt_to_str(XDR_DT dt)
580 if (dt == DT_INVALID)
581 return NULL;
583 return DT_string_table[dt];
586 OLECHAR const* dt_to_bstr(XDR_DT dt)
588 if (dt == DT_INVALID)
589 return NULL;
591 return DT_wstring_table[dt];
594 const char* debugstr_dt(XDR_DT dt)
596 return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL);
599 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
601 xmlDocPtr tmp_doc;
602 xmlNodePtr node;
603 xmlNsPtr ns;
604 HRESULT hr;
606 TRACE("(dt:%s, %s)\n", debugstr_dt(dt), debugstr_a((char const*)content));
608 if (!datatypes_schema)
610 xmlSchemaParserCtxtPtr spctx;
611 assert(datatypes_src != NULL);
612 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
613 datatypes_schema = Schema_parse(spctx);
614 xmlSchemaFreeParserCtxt(spctx);
617 switch (dt)
619 case DT_INVALID:
620 return E_FAIL;
621 case DT_BIN_BASE64:
622 case DT_BIN_HEX:
623 case DT_BOOLEAN:
624 case DT_CHAR:
625 case DT_DATE:
626 case DT_DATE_TZ:
627 case DT_DATETIME:
628 case DT_DATETIME_TZ:
629 case DT_FIXED_14_4:
630 case DT_FLOAT:
631 case DT_I1:
632 case DT_I2:
633 case DT_I4:
634 case DT_I8:
635 case DT_INT:
636 case DT_NMTOKEN:
637 case DT_NMTOKENS:
638 case DT_NUMBER:
639 case DT_R4:
640 case DT_R8:
641 case DT_STRING:
642 case DT_TIME:
643 case DT_TIME_TZ:
644 case DT_UI1:
645 case DT_UI2:
646 case DT_UI4:
647 case DT_UI8:
648 case DT_URI:
649 case DT_UUID:
650 if (!datatypes_schema)
652 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
653 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n");
655 /* Hopefully they don't need much in the way of XDR datatypes support... */
656 return S_OK;
659 if (content && xmlStrlen(content))
661 tmp_doc = xmlNewDoc(NULL);
662 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
663 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
664 xmlSetNs(node, ns);
665 xmlDocSetRootElement(tmp_doc, node);
667 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
668 xmlFreeDoc(tmp_doc);
670 else
671 { /* probably the node is being created manually and has no content yet */
672 hr = S_OK;
674 return hr;
675 default:
676 FIXME("need to handle dt:%s\n", debugstr_dt(dt));
677 return S_OK;
681 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
683 return (node->ns != NULL)? node->ns->href : NULL;
686 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
688 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
689 xmlHashLookup(This->cache, nsURI);
692 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
694 cache_entry* entry = get_entry(This, get_node_nsURI(node));
695 return (!entry)? NULL : entry->schema;
698 static xmlExternalEntityLoader _external_entity_loader;
700 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
701 xmlParserCtxtPtr ctxt)
703 xmlParserInputPtr input;
705 TRACE("(%s %s %p)\n", debugstr_a(URL), debugstr_a(ID), ctxt);
707 assert(MSXML_hInstance != NULL);
708 assert(datatypes_rsrc != NULL);
709 assert(datatypes_handle != NULL);
710 assert(datatypes_src != NULL);
712 /* TODO: if the desired schema is in the cache, load it from there */
713 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
715 TRACE("loading built-in schema for %s\n", URL);
716 input = xmlNewStringInputStream(ctxt, datatypes_src);
718 else
720 input = _external_entity_loader(URL, ID, ctxt);
723 return input;
726 void schemasInit(void)
728 xmlChar* buf;
729 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
731 FIXME("failed to find resource for %s\n", DT_nsURI);
732 return;
735 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
737 FIXME("failed to load resource for %s\n", DT_nsURI);
738 return;
740 buf = LockResource(datatypes_handle);
741 datatypes_len = SizeofResource(MSXML_hInstance, datatypes_rsrc);
743 /* Resource is loaded as raw data,
744 * need a null-terminated string */
745 while (buf[datatypes_len - 1] != '>') datatypes_len--;
746 datatypes_src = HeapAlloc(GetProcessHeap(), 0, datatypes_len + 1);
747 memcpy(datatypes_src, buf, datatypes_len);
748 datatypes_src[datatypes_len] = 0;
750 if (xmlGetExternalEntityLoader() != external_entity_loader)
752 _external_entity_loader = xmlGetExternalEntityLoader();
753 xmlSetExternalEntityLoader(external_entity_loader);
757 void schemasCleanup(void)
759 xmlSchemaFree(datatypes_schema);
760 HeapFree(GetProcessHeap(), 0, datatypes_src);
761 xmlSetExternalEntityLoader(_external_entity_loader);
764 static LONG cache_entry_add_ref(cache_entry* entry)
766 LONG ref = InterlockedIncrement(&entry->ref);
767 TRACE("(%p)->(%d)\n", entry, ref);
768 return ref;
771 static LONG cache_entry_release(cache_entry* entry)
773 LONG ref = InterlockedDecrement(&entry->ref);
774 TRACE("(%p)->(%d)\n", entry, ref);
776 if (ref == 0)
778 if (entry->type == CacheEntryType_XSD)
780 xmldoc_release(entry->doc);
781 entry->schema->doc = NULL;
782 xmlSchemaFree(entry->schema);
784 else if (entry->type == CacheEntryType_XDR)
786 xmldoc_release(entry->doc);
787 xmldoc_release(entry->schema->doc);
788 entry->schema->doc = NULL;
789 xmlSchemaFree(entry->schema);
792 heap_free(entry);
794 return ref;
797 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl;
799 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
801 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
804 static inline schema_cache* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection* iface)
806 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
809 static inline schema_cache* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
811 return iface->lpVtbl == (void*)&XMLDOMSchemaCollection2Vtbl ? impl_from_IXMLDOMSchemaCollection(iface) : NULL;
814 static inline CacheEntryType cache_type_from_xmlDocPtr(xmlDocPtr schema)
816 xmlNodePtr root = NULL;
817 if (schema)
818 root = xmlDocGetRootElement(schema);
819 if (root && root->ns)
822 if (xmlStrEqual(root->name, XDR_schema) &&
823 xmlStrEqual(root->ns->href, XDR_nsURI))
825 return CacheEntryType_XDR;
827 else if (xmlStrEqual(root->name, XSD_schema) &&
828 xmlStrEqual(root->ns->href, XSD_nsURI))
830 return CacheEntryType_XSD;
833 return CacheEntryType_Invalid;
836 static BOOL link_datatypes(xmlDocPtr schema)
838 xmlNodePtr root, next, child;
839 xmlNsPtr ns;
841 assert(xmlGetExternalEntityLoader() == external_entity_loader);
842 root = xmlDocGetRootElement(schema);
843 if (!root)
844 return FALSE;
846 for (ns = root->nsDef; ns != NULL; ns = ns->next)
848 if (xmlStrEqual(ns->href, DT_nsURI))
849 break;
852 if (!ns)
853 return FALSE;
855 next = xmlFirstElementChild(root);
856 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
857 if (next) child = xmlAddPrevSibling(next, child);
858 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
859 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
861 return TRUE;
864 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
866 cache_entry* entry = heap_alloc(sizeof(cache_entry));
867 xmlSchemaParserCtxtPtr spctx;
868 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
870 link_datatypes(new_doc);
872 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
873 * do we need to do something special here? */
874 entry->type = CacheEntryType_XSD;
875 entry->ref = 0;
876 spctx = xmlSchemaNewDocParserCtxt(new_doc);
878 if ((entry->schema = Schema_parse(spctx)))
880 xmldoc_init(entry->schema->doc, v);
881 entry->doc = entry->schema->doc;
882 xmldoc_add_ref(entry->doc);
884 else
886 FIXME("failed to parse doc\n");
887 xmlFreeDoc(new_doc);
888 heap_free(entry);
889 entry = NULL;
891 xmlSchemaFreeParserCtxt(spctx);
892 return entry;
895 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version)
897 cache_entry* entry = heap_alloc(sizeof(cache_entry));
898 xmlSchemaParserCtxtPtr spctx;
899 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
901 link_datatypes(xsd_doc);
903 entry->type = CacheEntryType_XDR;
904 entry->ref = 0;
905 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
907 if ((entry->schema = Schema_parse(spctx)))
909 entry->doc = new_doc;
910 xmldoc_init(entry->schema->doc, version);
911 xmldoc_init(entry->doc, version);
912 xmldoc_add_ref(entry->doc);
913 xmldoc_add_ref(entry->schema->doc);
915 else
917 FIXME("failed to parse doc\n");
918 xmlFreeDoc(new_doc);
919 xmlFreeDoc(xsd_doc);
920 heap_free(entry);
921 entry = NULL;
923 xmlSchemaFreeParserCtxt(spctx);
925 return entry;
928 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version)
930 cache_entry* entry;
931 IXMLDOMDocument3* domdoc = NULL;
932 xmlDocPtr doc = NULL;
933 HRESULT hr = DOMDocument_create(version, NULL, (void**)&domdoc);
934 VARIANT_BOOL b = VARIANT_FALSE;
935 CacheEntryType type = CacheEntryType_Invalid;
937 if (hr != S_OK)
939 FIXME("failed to create domdoc\n");
940 return NULL;
942 assert(domdoc != NULL);
943 assert(V_VT(&url) == VT_BSTR);
945 hr = IXMLDOMDocument3_load(domdoc, url, &b);
946 if (hr != S_OK)
948 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
949 if (b != VARIANT_TRUE)
951 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url)));
952 IXMLDOMDocument3_Release(domdoc);
953 return NULL;
956 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
957 type = cache_type_from_xmlDocPtr(doc);
959 switch (type)
961 case CacheEntryType_XSD:
962 entry = cache_entry_from_xsd_doc(doc, nsURI, version);
963 break;
964 case CacheEntryType_XDR:
965 entry = cache_entry_from_xdr_doc(doc, nsURI, version);
966 break;
967 default:
968 entry = NULL;
969 FIXME("invalid schema\n");
970 break;
972 IXMLDOMDocument3_Release(domdoc);
974 return entry;
977 static void cache_free(void* data, xmlChar* name /* ignored */)
979 cache_entry_release((cache_entry*)data);
982 /* returns index or -1 if not found */
983 static int cache_free_uri(schema_cache *cache, const xmlChar *uri)
985 int i;
987 for (i = 0; i < cache->count; i++)
988 if (xmlStrEqual(cache->uris[i], uri))
990 heap_free(cache->uris[i]);
991 return i;
994 return -1;
997 static void cache_add_entry(schema_cache *cache, const xmlChar *uri, cache_entry *entry)
999 int i;
1001 /* meaning no entry found with this name */
1002 if (xmlHashRemoveEntry(cache->cache, uri, cache_free))
1004 if (cache->count == cache->allocated)
1006 cache->allocated *= 2;
1007 cache->uris = heap_realloc(cache->uris, cache->allocated*sizeof(xmlChar*));
1009 i = cache->count++;
1011 else
1012 i = cache_free_uri(cache, uri);
1014 cache->uris[i] = heap_strdupxmlChar(uri);
1015 xmlHashAddEntry(cache->cache, uri, entry);
1018 static void cache_remove_entry(schema_cache *cache, const xmlChar *uri)
1020 /* adjust index if entry was really removed */
1021 if (xmlHashRemoveEntry(cache->cache, uri, cache_free) == 0)
1023 int i = cache_free_uri(cache, uri);
1024 if (i == -1) return;
1025 /* shift array */
1026 if (i != --cache->count)
1027 memmove(&cache->uris[i], &cache->uris[i+1], (cache->count-i)*sizeof(xmlChar*));
1031 /* This one adds all namespaces defined in document to a cache, without anything
1032 associated with uri obviously.
1033 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
1034 it uses additional node type to describe namespace definition attribute while
1035 in msxml it's expected to be a normal attribute - as a workaround document is
1036 queried at libxml2 level here. */
1037 HRESULT cache_from_doc_ns(IXMLDOMSchemaCollection2 *iface, xmlnode *node)
1039 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1040 static const xmlChar query[] = "//*/namespace::*";
1041 xmlXPathObjectPtr nodeset;
1042 xmlXPathContextPtr ctxt;
1044 This->read_only = 1;
1046 ctxt = xmlXPathNewContext(node->node->doc);
1048 nodeset = xmlXPathEvalExpression(query, ctxt);
1049 xmlXPathFreeContext(ctxt);
1051 if (nodeset)
1053 int pos = 0, len = xmlXPathNodeSetGetLength(nodeset->nodesetval);
1055 if (len == 0) return S_OK;
1057 while (pos < len)
1059 xmlNodePtr node = xmlXPathNodeSetItem(nodeset->nodesetval, pos);
1060 if (node->type == XML_NAMESPACE_DECL)
1062 static const xmlChar defns[] = "http://www.w3.org/XML/1998/namespace";
1063 xmlNsPtr ns = (xmlNsPtr)node;
1064 cache_entry *entry;
1066 /* filter out default uri */
1067 if (xmlStrEqual(ns->href, defns))
1069 pos++;
1070 continue;
1073 entry = heap_alloc(sizeof(cache_entry));
1074 entry->type = CacheEntryType_NS;
1075 entry->ref = 1;
1076 entry->schema = NULL;
1077 entry->doc = NULL;
1079 cache_add_entry(This, ns->href, entry);
1081 pos++;
1084 xmlXPathFreeObject(nodeset);
1087 return S_OK;
1090 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
1091 REFIID riid, void** ppvObject)
1093 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1095 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1097 if ( IsEqualIID(riid, &IID_IUnknown) ||
1098 IsEqualIID(riid, &IID_IDispatch) ||
1099 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
1100 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
1102 *ppvObject = iface;
1104 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
1106 return *ppvObject ? S_OK : E_NOINTERFACE;
1108 else
1110 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1111 *ppvObject = NULL;
1112 return E_NOINTERFACE;
1115 IXMLDOMSchemaCollection2_AddRef(iface);
1117 return S_OK;
1120 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
1122 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1123 LONG ref = InterlockedIncrement(&This->ref);
1124 TRACE("(%p)->(%d)\n", This, ref);
1125 return ref;
1128 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
1130 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1131 LONG ref = InterlockedDecrement(&This->ref);
1132 TRACE("(%p)->(%d)\n", This, ref);
1134 if (ref == 0)
1136 int i;
1138 for (i = 0; i < This->count; i++)
1139 heap_free(This->uris[i]);
1140 heap_free(This->uris);
1141 xmlHashFree(This->cache, cache_free);
1142 release_dispex(&This->dispex);
1143 heap_free(This);
1146 return ref;
1149 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1150 UINT* pctinfo)
1152 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1153 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1156 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1157 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1159 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1160 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
1161 iTInfo, lcid, ppTInfo);
1164 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1165 REFIID riid, LPOLESTR* rgszNames,
1166 UINT cNames, LCID lcid, DISPID* rgDispId)
1168 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1169 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1170 riid, rgszNames, cNames, lcid, rgDispId);
1173 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1174 DISPID dispIdMember, REFIID riid, LCID lcid,
1175 WORD wFlags, DISPPARAMS* pDispParams,
1176 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1177 UINT* puArgErr)
1179 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1180 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1181 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1184 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1186 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1187 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1188 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1190 if (This->read_only) return E_FAIL;
1192 switch (V_VT(&var))
1194 case VT_NULL:
1196 cache_remove_entry(This, name);
1198 break;
1200 case VT_BSTR:
1202 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1204 if (entry)
1206 cache_entry_add_ref(entry);
1208 else
1210 heap_free(name);
1211 return E_FAIL;
1214 cache_add_entry(This, name, entry);
1216 break;
1218 case VT_DISPATCH:
1220 xmlDocPtr doc = NULL;
1221 cache_entry* entry;
1222 CacheEntryType type;
1223 IXMLDOMNode* domnode = NULL;
1224 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
1226 if (domnode)
1227 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1229 if (!doc)
1231 IXMLDOMNode_Release(domnode);
1232 heap_free(name);
1233 return E_INVALIDARG;
1235 type = cache_type_from_xmlDocPtr(doc);
1237 if (type == CacheEntryType_XSD)
1239 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1241 else if (type == CacheEntryType_XDR)
1243 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1245 else
1247 WARN("invalid schema!\n");
1248 entry = NULL;
1251 IXMLDOMNode_Release(domnode);
1253 if (entry)
1255 cache_entry_add_ref(entry);
1257 else
1259 heap_free(name);
1260 return E_FAIL;
1263 cache_add_entry(This, name, entry);
1265 break;
1267 default:
1269 heap_free(name);
1270 return E_INVALIDARG;
1273 heap_free(name);
1274 return S_OK;
1277 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1278 IXMLDOMNode** node)
1280 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1281 cache_entry* entry;
1282 xmlChar* name;
1284 TRACE("(%p)->(%s %p)\n", This, debugstr_w(uri), node);
1286 if (This->version == MSXML6) return E_NOTIMPL;
1288 if (!node)
1289 return E_POINTER;
1291 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1292 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1293 heap_free(name);
1295 /* TODO: this should be read-only */
1296 if (entry && entry->doc)
1297 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1299 *node = NULL;
1300 return S_OK;
1303 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1305 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1306 xmlChar* name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1307 TRACE("(%p)->(%s)\n", This, debugstr_w(uri));
1309 if (This->version == MSXML6) return E_NOTIMPL;
1311 cache_remove_entry(This, name);
1312 heap_free(name);
1313 return S_OK;
1316 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1318 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1319 TRACE("(%p)->(%p)\n", This, length);
1321 if (!length)
1322 return E_POINTER;
1324 *length = This->count;
1325 return S_OK;
1328 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1329 LONG index, BSTR* uri)
1331 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1333 TRACE("(%p)->(%i %p)\n", This, index, uri);
1335 if (!uri)
1336 return E_POINTER;
1338 if (index >= This->count)
1339 return E_FAIL;
1341 *uri = bstr_from_xmlChar(This->uris[index]);
1342 return S_OK;
1345 static void cache_copy(void* data, void* dest, xmlChar* name)
1347 schema_cache* This = (schema_cache*) dest;
1348 cache_entry* entry = (cache_entry*) data;
1350 if (xmlHashLookup(This->cache, name) == NULL)
1352 cache_entry_add_ref(entry);
1353 cache_add_entry(This, name, entry);
1357 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1358 IXMLDOMSchemaCollection* collection)
1360 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1361 schema_cache* That;
1363 TRACE("(%p)->(%p)\n", This, collection);
1365 if (!collection)
1366 return E_POINTER;
1368 That = unsafe_impl_from_IXMLDOMSchemaCollection(collection);
1369 if (!That)
1371 ERR("external collection implementation\n");
1372 return E_FAIL;
1375 /* TODO: detect errors while copying & return E_FAIL */
1376 xmlHashScan(That->cache, cache_copy, This);
1378 return S_OK;
1381 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface, IUnknown** enumv)
1383 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1384 TRACE("(%p)->(%p)\n", This, enumv);
1385 return create_enumvariant((IUnknown*)iface, TRUE, &schemacache_enumvariant, (IEnumVARIANT**)enumv);
1388 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1390 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1391 FIXME("(%p): stub\n", This);
1392 return E_NOTIMPL;
1395 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1396 VARIANT_BOOL value)
1398 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1399 FIXME("(%p)->(%d): stub\n", This, value);
1401 This->validateOnLoad = value;
1402 /* it's ok to disable it, cause we don't validate on load anyway */
1403 if (value == VARIANT_FALSE) return S_OK;
1405 return E_NOTIMPL;
1408 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1409 VARIANT_BOOL* value)
1411 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1412 TRACE("(%p)->(%p)\n", This, value);
1414 if (!value) return E_POINTER;
1415 *value = This->validateOnLoad;
1417 return S_OK;
1420 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1421 BSTR namespaceURI, ISchema** schema)
1423 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1424 FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(namespaceURI), schema);
1425 if (schema)
1426 *schema = NULL;
1427 return E_NOTIMPL;
1430 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1431 IXMLDOMNode* node, ISchemaItem** item)
1433 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1434 FIXME("(%p)->(%p %p): stub\n", This, node, item);
1435 if (item)
1436 *item = NULL;
1437 return E_NOTIMPL;
1440 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl =
1442 schema_cache_QueryInterface,
1443 schema_cache_AddRef,
1444 schema_cache_Release,
1445 schema_cache_GetTypeInfoCount,
1446 schema_cache_GetTypeInfo,
1447 schema_cache_GetIDsOfNames,
1448 schema_cache_Invoke,
1449 schema_cache_add,
1450 schema_cache_get,
1451 schema_cache_remove,
1452 schema_cache_get_length,
1453 schema_cache_get_namespaceURI,
1454 schema_cache_addCollection,
1455 schema_cache_get__newEnum,
1456 schema_cache_validate,
1457 schema_cache_put_validateOnLoad,
1458 schema_cache_get_validateOnLoad,
1459 schema_cache_getSchema,
1460 schema_cache_getDeclaration
1463 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1465 xmlSchemaElementPtr decl = NULL;
1466 xmlChar const* nsURI = get_node_nsURI(node);
1468 TRACE("(%p, %p)\n", schema, node);
1470 if (xmlStrEqual(nsURI, schema->targetNamespace))
1471 decl = xmlHashLookup(schema->elemDecl, node->name);
1473 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1475 FIXME("declaration not found in main schema - need to check schema imports!\n");
1476 /*xmlSchemaImportPtr import;
1477 if (nsURI == NULL)
1478 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1479 else
1480 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1482 if (import != NULL)
1483 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1486 return decl;
1489 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1491 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1492 while (decl != NULL && decl->refDecl != NULL)
1493 decl = decl->refDecl;
1494 return (decl != NULL)? decl->node : NULL;
1497 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1499 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1500 xmlSchemaPtr schema;
1502 TRACE("(%p, %p)\n", This, tree);
1504 if (!tree)
1505 return E_POINTER;
1507 if (tree->type == XML_DOCUMENT_NODE)
1508 tree = xmlDocGetRootElement(tree->doc);
1510 schema = get_node_schema(This, tree);
1511 /* TODO: if the ns is not in the cache, and it's a URL,
1512 * do we try to load from that? */
1513 if (schema)
1514 return Schema_validate_tree(schema, tree);
1515 else
1516 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1518 return E_FAIL;
1521 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1523 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1524 xmlSchemaPtr schema = get_node_schema(This, node);
1525 XDR_DT dt = DT_INVALID;
1527 TRACE("(%p, %p)\n", This, node);
1529 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1531 dt = str_to_dt(node->name, -1);
1533 else if (schema)
1535 xmlChar* str;
1536 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1538 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1539 if (str)
1541 dt = str_to_dt(str, -1);
1542 xmlFree(str);
1546 return dt;
1549 static const tid_t schemacache_iface_tids[] = {
1550 IXMLDOMSchemaCollection2_tid,
1554 static dispex_static_data_t schemacache_dispex = {
1555 NULL,
1556 IXMLDOMSchemaCollection2_tid,
1557 NULL,
1558 schemacache_iface_tids
1561 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1563 schema_cache* This = heap_alloc(sizeof(schema_cache));
1564 if (!This)
1565 return E_OUTOFMEMORY;
1567 TRACE("(%d %p %p)\n", version, outer, obj);
1569 This->IXMLDOMSchemaCollection2_iface.lpVtbl = &XMLDOMSchemaCollection2Vtbl;
1570 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1571 This->allocated = 10;
1572 This->count = 0;
1573 This->uris = heap_alloc(This->allocated*sizeof(xmlChar*));
1574 This->ref = 1;
1575 This->version = version;
1576 This->validateOnLoad = VARIANT_TRUE;
1577 This->read_only = 0;
1578 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSchemaCollection2_iface, &schemacache_dispex);
1580 *obj = &This->IXMLDOMSchemaCollection2_iface;
1581 return S_OK;
1584 #else
1586 HRESULT SchemaCache_create(MSXML_VERSION version, IUnknown* outer, void** obj)
1588 MESSAGE("This program tried to use a SchemaCache object, but\n"
1589 "libxml2 support was not present at compile time.\n");
1590 return E_NOTIMPL;
1593 #endif