ntdll: Make sure we don't try to attach the main exe a second time.
[wine/zf.git] / dlls / msxml3 / schema.c
blobe385b9b563577cfa94c836d8bdcfbcc0067301bb
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 #ifdef HAVE_LIBXML2
53 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
55 #if LIBXML_VERSION >= 20908
56 #define XMLHASH_CONST const
57 #else
58 #define XMLHASH_CONST
59 #endif
61 /* We use a chained hashtable, which can hold any number of schemas
62 * TODO: grow/shrink hashtable depending on load factor
63 * TODO: implement read-only where appropriate
66 /* This is just the number of buckets, should be prime */
67 #define DEFAULT_HASHTABLE_SIZE 17
69 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
71 static const xmlChar XSD_schema[] = "schema";
72 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
73 static const xmlChar XDR_schema[] = "Schema";
74 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
75 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
77 static xmlChar * datatypes_src;
78 static int datatypes_len;
79 static HGLOBAL datatypes_handle;
80 static HRSRC datatypes_rsrc;
81 static xmlSchemaPtr datatypes_schema;
83 static const WCHAR emptyW[] = {0};
85 /* Supported types:
86 * msxml3 - XDR only
87 * msxml4 - XDR & XSD
88 * msxml5 - XDR & XSD
89 * mxsml6 - XSD only
91 * CacheType_NS is a special type used for read-only collection build with
92 * IXMLDOMDocument2::namespaces()
94 typedef enum {
95 CacheEntryType_Invalid,
96 CacheEntryType_XDR,
97 CacheEntryType_XSD,
98 CacheEntryType_NS
99 } CacheEntryType;
101 typedef struct
103 DispatchEx dispex;
104 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface;
105 LONG ref;
107 MSXML_VERSION version;
108 xmlHashTablePtr cache;
109 xmlChar **uris;
110 int allocated;
111 int count;
113 VARIANT_BOOL validateOnLoad;
114 int read_only;
115 } schema_cache;
117 typedef struct
119 CacheEntryType type;
120 xmlSchemaPtr schema;
121 xmlDocPtr doc;
122 LONG ref;
123 } cache_entry;
125 static const tid_t schema_cache_se_tids[] = {
126 IXMLDOMSchemaCollection_tid,
127 IXMLDOMSchemaCollection2_tid,
128 NULL_tid
131 /* datatypes lookup stuff
132 * generated with help from gperf */
133 #define DT_MIN_STR_LEN 2
134 #define DT_MAX_STR_LEN 11
135 #define DT_MIN_HASH_VALUE 2
136 #define DT_MAX_HASH_VALUE 115
138 static const xmlChar DT_bin_base64[] = "bin.base64";
139 static const xmlChar DT_bin_hex[] = "bin.hex";
140 static const xmlChar DT_boolean[] = "boolean";
141 static const xmlChar DT_char[] = "char";
142 static const xmlChar DT_date[] = "date";
143 static const xmlChar DT_date_tz[] = "date.tz";
144 static const xmlChar DT_dateTime[] = "dateTime";
145 static const xmlChar DT_dateTime_tz[] = "dateTime.tz";
146 static const xmlChar DT_entity[] = "entity";
147 static const xmlChar DT_entities[] = "entities";
148 static const xmlChar DT_enumeration[] = "enumeration";
149 static const xmlChar DT_fixed_14_4[] = "fixed.14.4";
150 static const xmlChar DT_float[] = "float";
151 static const xmlChar DT_i1[] = "i1";
152 static const xmlChar DT_i2[] = "i2";
153 static const xmlChar DT_i4[] = "i4";
154 static const xmlChar DT_i8[] = "i8";
155 static const xmlChar DT_id[] = "id";
156 static const xmlChar DT_idref[] = "idref";
157 static const xmlChar DT_idrefs[] = "idrefs";
158 static const xmlChar DT_int[] = "int";
159 static const xmlChar DT_nmtoken[] = "nmtoken";
160 static const xmlChar DT_nmtokens[] = "nmtokens";
161 static const xmlChar DT_notation[] = "notation";
162 static const xmlChar DT_number[] = "number";
163 static const xmlChar DT_r4[] = "r4";
164 static const xmlChar DT_r8[] = "r8";
165 static const xmlChar DT_string[] = "string";
166 static const xmlChar DT_time[] = "time";
167 static const xmlChar DT_time_tz[] = "time.tz";
168 static const xmlChar DT_ui1[] = "ui1";
169 static const xmlChar DT_ui2[] = "ui2";
170 static const xmlChar DT_ui4[] = "ui4";
171 static const xmlChar DT_ui8[] = "ui8";
172 static const xmlChar DT_uri[] = "uri";
173 static const xmlChar DT_uuid[] = "uuid";
175 static const OLECHAR wDT_bin_base64[] = {'b','i','n','.','b','a','s','e','6','4',0};
176 static const OLECHAR wDT_bin_hex[] = {'b','i','n','.','h','e','x',0};
177 static const OLECHAR wDT_boolean[] = {'b','o','o','l','e','a','n',0};
178 static const OLECHAR wDT_char[] = {'c','h','a','r',0};
179 static const OLECHAR wDT_date[] = {'d','a','t','e',0};
180 static const OLECHAR wDT_date_tz[] = {'d','a','t','e','.','t','z',0};
181 static const OLECHAR wDT_dateTime[] = {'d','a','t','e','T','i','m','e',0};
182 static const OLECHAR wDT_dateTime_tz[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
183 static const OLECHAR wDT_entity[] = {'e','n','t','i','t','y',0};
184 static const OLECHAR wDT_entities[] = {'e','n','t','i','t','i','e','s',0};
185 static const OLECHAR wDT_enumeration[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
186 static const OLECHAR wDT_fixed_14_4[] = {'f','i','x','e','d','.','1','4','.','4',0};
187 static const OLECHAR wDT_float[] = {'f','l','o','a','t',0};
188 static const OLECHAR wDT_i1[] = {'i','1',0};
189 static const OLECHAR wDT_i2[] = {'i','2',0};
190 static const OLECHAR wDT_i4[] = {'i','4',0};
191 static const OLECHAR wDT_i8[] = {'i','8',0};
192 static const OLECHAR wDT_id[] = {'i','d',0};
193 static const OLECHAR wDT_idref[] = {'i','d','r','e','f',0};
194 static const OLECHAR wDT_idrefs[] = {'i','d','r','e','f','s',0};
195 static const OLECHAR wDT_int[] = {'i','n','t',0};
196 static const OLECHAR wDT_nmtoken[] = {'n','m','t','o','k','e','n',0};
197 static const OLECHAR wDT_nmtokens[] = {'n','m','t','o','k','e','n','s',0};
198 static const OLECHAR wDT_notation[] = {'n','o','t','a','t','i','o','n',0};
199 static const OLECHAR wDT_number[] = {'n','u','m','b','e','r',0};
200 static const OLECHAR wDT_r4[] = {'r','4',0};
201 static const OLECHAR wDT_r8[] = {'r','8',0};
202 static const OLECHAR wDT_string[] = {'s','t','r','i','n','g',0};
203 static const OLECHAR wDT_time[] = {'t','i','m','e',0};
204 static const OLECHAR wDT_time_tz[] = {'t','i','m','e','.','t','z',0};
205 static const OLECHAR wDT_ui1[] = {'u','i','1',0};
206 static const OLECHAR wDT_ui2[] = {'u','i','2',0};
207 static const OLECHAR wDT_ui4[] = {'u','i','4',0};
208 static const OLECHAR wDT_ui8[] = {'u','i','8',0};
209 static const OLECHAR wDT_uri[] = {'u','r','i',0};
210 static const OLECHAR wDT_uuid[] = {'u','u','i','d',0};
212 static const BYTE hash_assoc_values[] =
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, 10, 116, 116, 55,
219 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
220 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
221 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
222 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
223 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
224 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
225 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
226 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
227 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
228 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
229 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
230 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
231 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
232 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
233 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
234 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
235 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
236 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
237 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
238 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
239 116, 116, 116, 116, 116, 116
242 static void LIBXML2_LOG_CALLBACK parser_error(void* ctx, char const* msg, ...)
244 va_list ap;
245 va_start(ap, msg);
246 LIBXML2_CALLBACK_ERR(Schema_parse, msg, ap);
247 va_end(ap);
250 static void LIBXML2_LOG_CALLBACK parser_warning(void* ctx, char const* msg, ...)
252 va_list ap;
253 va_start(ap, msg);
254 LIBXML2_CALLBACK_WARN(Schema_parse, msg, ap);
255 va_end(ap);
258 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
259 static void parser_serror(void* ctx, xmlErrorPtr err)
261 LIBXML2_CALLBACK_SERROR(Schema_parse, err);
263 #endif
265 static inline xmlSchemaPtr Schema_parse(xmlSchemaParserCtxtPtr spctx)
267 TRACE("(%p)\n", spctx);
269 xmlSchemaSetParserErrors(spctx, parser_error, parser_warning, NULL);
270 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
271 xmlSchemaSetParserStructuredErrors(spctx, parser_serror, NULL);
272 #endif
274 return xmlSchemaParse(spctx);
277 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
279 va_list ap;
280 va_start(ap, msg);
281 LIBXML2_CALLBACK_ERR(Schema_validate_tree, msg, ap);
282 va_end(ap);
285 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
287 va_list ap;
288 va_start(ap, msg);
289 LIBXML2_CALLBACK_WARN(Schema_validate_tree, msg, ap);
290 va_end(ap);
293 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
294 static void validate_serror(void* ctx, xmlErrorPtr err)
296 LIBXML2_CALLBACK_SERROR(Schema_validate_tree, err);
298 #endif
300 static HRESULT schema_cache_get_item(IUnknown *iface, LONG index, VARIANT *item)
302 V_VT(item) = VT_BSTR;
303 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2*)iface, index, &V_BSTR(item));
306 static const struct enumvariant_funcs schemacache_enumvariant = {
307 schema_cache_get_item,
308 NULL
311 static inline HRESULT Schema_validate_tree(xmlSchemaPtr schema, xmlNodePtr tree)
313 xmlSchemaValidCtxtPtr svctx;
314 int err;
316 TRACE("(%p, %p)\n", schema, tree);
317 /* TODO: if validateOnLoad property is false,
318 * we probably need to validate the schema here. */
319 svctx = xmlSchemaNewValidCtxt(schema);
320 xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
321 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
322 xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
323 #endif
325 if (tree->type == XML_DOCUMENT_NODE)
326 err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
327 else
328 err = xmlSchemaValidateOneElement(svctx, tree);
330 xmlSchemaFreeValidCtxt(svctx);
331 return err? S_FALSE : S_OK;
334 static DWORD dt_hash(xmlChar const* str, int len /* calculated if -1 */)
336 DWORD hval = (len == -1)? xmlStrlen(str) : len;
338 switch (hval)
340 default:
341 hval += hash_assoc_values[str[10]];
342 /*FALLTHROUGH*/
343 case 10:
344 hval += hash_assoc_values[str[9]];
345 /*FALLTHROUGH*/
346 case 9:
347 hval += hash_assoc_values[str[8]];
348 /*FALLTHROUGH*/
349 case 8:
350 hval += hash_assoc_values[str[7]];
351 /*FALLTHROUGH*/
352 case 7:
353 hval += hash_assoc_values[str[6]];
354 /*FALLTHROUGH*/
355 case 6:
356 hval += hash_assoc_values[str[5]];
357 /*FALLTHROUGH*/
358 case 5:
359 hval += hash_assoc_values[str[4]];
360 /*FALLTHROUGH*/
361 case 4:
362 hval += hash_assoc_values[str[3]];
363 /*FALLTHROUGH*/
364 case 3:
365 hval += hash_assoc_values[str[2]];
366 /*FALLTHROUGH*/
367 case 2:
368 hval += hash_assoc_values[str[1]];
369 /*FALLTHROUGH*/
370 case 1:
371 hval += hash_assoc_values[str[0]];
372 break;
374 return hval;
377 static DWORD dt_hash_bstr(OLECHAR const* bstr, int len /* calculated if -1 */)
379 DWORD hval = (len == -1)? lstrlenW(bstr) : len;
381 switch (hval)
383 default:
384 hval += (bstr[10] & 0xFF00)? 116 : hash_assoc_values[bstr[10]];
385 /*FALLTHROUGH*/
386 case 10:
387 hval += (bstr[9] & 0xFF00)? 116 : hash_assoc_values[bstr[9]];
388 /*FALLTHROUGH*/
389 case 9:
390 hval += (bstr[8] & 0xFF00)? 116 : hash_assoc_values[bstr[8]];
391 /*FALLTHROUGH*/
392 case 8:
393 hval += (bstr[7] & 0xFF00)? 116 : hash_assoc_values[bstr[7]];
394 /*FALLTHROUGH*/
395 case 7:
396 hval += (bstr[6] & 0xFF00)? 116 : hash_assoc_values[bstr[6]];
397 /*FALLTHROUGH*/
398 case 6:
399 hval += (bstr[5] & 0xFF00)? 116 : hash_assoc_values[bstr[5]];
400 /*FALLTHROUGH*/
401 case 5:
402 hval += (bstr[4] & 0xFF00)? 116 : hash_assoc_values[bstr[4]];
403 /*FALLTHROUGH*/
404 case 4:
405 hval += (bstr[3] & 0xFF00)? 116 : hash_assoc_values[bstr[3]];
406 /*FALLTHROUGH*/
407 case 3:
408 hval += (bstr[2] & 0xFF00)? 116 : hash_assoc_values[bstr[2]];
409 /*FALLTHROUGH*/
410 case 2:
411 hval += (bstr[1] & 0xFF00)? 116 : hash_assoc_values[bstr[1]];
412 /*FALLTHROUGH*/
413 case 1:
414 hval += (bstr[0] & 0xFF00)? 116 : hash_assoc_values[bstr[0]];
415 break;
417 return hval;
420 static const xmlChar *const DT_string_table[LAST_DT] =
422 DT_bin_base64,
423 DT_bin_hex,
424 DT_boolean,
425 DT_char,
426 DT_date,
427 DT_date_tz,
428 DT_dateTime,
429 DT_dateTime_tz,
430 DT_entity,
431 DT_entities,
432 DT_enumeration,
433 DT_fixed_14_4,
434 DT_float,
435 DT_i1,
436 DT_i2,
437 DT_i4,
438 DT_i8,
439 DT_id,
440 DT_idref,
441 DT_idrefs,
442 DT_int,
443 DT_nmtoken,
444 DT_nmtokens,
445 DT_notation,
446 DT_number,
447 DT_r4,
448 DT_r8,
449 DT_string,
450 DT_time,
451 DT_time_tz,
452 DT_ui1,
453 DT_ui2,
454 DT_ui4,
455 DT_ui8,
456 DT_uri,
457 DT_uuid
460 static const WCHAR *const DT_wstring_table[LAST_DT] =
462 wDT_bin_base64,
463 wDT_bin_hex,
464 wDT_boolean,
465 wDT_char,
466 wDT_date,
467 wDT_date_tz,
468 wDT_dateTime,
469 wDT_dateTime_tz,
470 wDT_entity,
471 wDT_entities,
472 wDT_enumeration,
473 wDT_fixed_14_4,
474 wDT_float,
475 wDT_i1,
476 wDT_i2,
477 wDT_i4,
478 wDT_i8,
479 wDT_id,
480 wDT_idref,
481 wDT_idrefs,
482 wDT_int,
483 wDT_nmtoken,
484 wDT_nmtokens,
485 wDT_notation,
486 wDT_number,
487 wDT_r4,
488 wDT_r8,
489 wDT_string,
490 wDT_time,
491 wDT_time_tz,
492 wDT_ui1,
493 wDT_ui2,
494 wDT_ui4,
495 wDT_ui8,
496 wDT_uri,
497 wDT_uuid
500 static const XDR_DT DT_lookup_table[] =
502 -1, -1,
503 DT_I8,
504 DT_UI8,
505 DT_TIME,
506 -1, -1,
507 DT_I4,
508 DT_UI4,
509 -1, -1, -1,
510 DT_R8,
511 DT_URI,
513 DT_FLOAT,
515 DT_R4,
516 DT_INT,
517 DT_CHAR,
519 DT_ENTITY,
520 DT_ID,
521 DT_ENTITIES,
522 DT_UUID,
523 -1, -1,
524 DT_TIME_TZ,
526 DT_DATE,
528 DT_NUMBER,
529 DT_BIN_HEX,
530 DT_DATETIME,
532 DT_IDREF,
533 DT_IDREFS,
534 DT_BOOLEAN,
535 -1, -1, -1,
536 DT_STRING,
537 DT_NMTOKEN,
538 DT_NMTOKENS,
540 DT_BIN_BASE64,
542 DT_I2,
543 DT_UI2,
544 -1, -1, -1,
545 DT_DATE_TZ,
546 DT_NOTATION,
547 -1, -1,
548 DT_DATETIME_TZ,
549 DT_I1,
550 DT_UI1,
551 -1, -1,
552 DT_ENUMERATION,
553 -1, -1, -1, -1, -1, -1, -1, -1, -1,
554 -1, -1, -1, -1, -1, -1, -1, -1, -1,
555 -1, -1, -1, -1, -1, -1, -1, -1, -1,
556 -1, -1, -1, -1, -1, -1, -1, -1, -1,
557 -1, -1, -1, -1, -1, -1, -1, -1, -1,
558 -1, -1, -1, -1, -1, -1, -1, -1,
559 DT_FIXED_14_4
562 XDR_DT str_to_dt(xmlChar const* str, int len /* calculated if -1 */)
564 DWORD hash = dt_hash(str, len);
565 XDR_DT dt = DT_INVALID;
567 if (hash <= DT_MAX_HASH_VALUE)
568 dt = DT_lookup_table[hash];
570 if (dt != DT_INVALID && xmlStrcasecmp(str, DT_string_table[dt]) == 0)
571 return dt;
573 return DT_INVALID;
576 XDR_DT bstr_to_dt(OLECHAR const* bstr, int len /* calculated if -1 */)
578 DWORD hash = dt_hash_bstr(bstr, len);
579 XDR_DT dt = DT_INVALID;
581 if (hash <= DT_MAX_HASH_VALUE)
582 dt = DT_lookup_table[hash];
584 if (dt != DT_INVALID && lstrcmpiW(bstr, DT_wstring_table[dt]) == 0)
585 return dt;
587 return DT_INVALID;
590 xmlChar const* dt_to_str(XDR_DT dt)
592 if (dt == DT_INVALID)
593 return NULL;
595 return DT_string_table[dt];
598 OLECHAR const* dt_to_bstr(XDR_DT dt)
600 if (dt == DT_INVALID)
601 return NULL;
603 return DT_wstring_table[dt];
606 const char* debugstr_dt(XDR_DT dt)
608 return debugstr_a(dt != DT_INVALID ? (const char*)DT_string_table[dt] : NULL);
611 HRESULT dt_validate(XDR_DT dt, xmlChar const* content)
613 xmlDocPtr tmp_doc;
614 xmlNodePtr node;
615 xmlNsPtr ns;
616 HRESULT hr;
618 TRACE("(dt:%s, %s)\n", debugstr_dt(dt), debugstr_a((char const*)content));
620 if (!datatypes_schema)
622 xmlSchemaParserCtxtPtr spctx;
623 assert(datatypes_src != NULL);
624 spctx = xmlSchemaNewMemParserCtxt((char const*)datatypes_src, datatypes_len);
625 datatypes_schema = Schema_parse(spctx);
626 xmlSchemaFreeParserCtxt(spctx);
629 switch (dt)
631 case DT_INVALID:
632 return E_FAIL;
633 case DT_BIN_BASE64:
634 case DT_BIN_HEX:
635 case DT_BOOLEAN:
636 case DT_CHAR:
637 case DT_DATE:
638 case DT_DATE_TZ:
639 case DT_DATETIME:
640 case DT_DATETIME_TZ:
641 case DT_FIXED_14_4:
642 case DT_FLOAT:
643 case DT_I1:
644 case DT_I2:
645 case DT_I4:
646 case DT_I8:
647 case DT_INT:
648 case DT_NMTOKEN:
649 case DT_NMTOKENS:
650 case DT_NUMBER:
651 case DT_R4:
652 case DT_R8:
653 case DT_STRING:
654 case DT_TIME:
655 case DT_TIME_TZ:
656 case DT_UI1:
657 case DT_UI2:
658 case DT_UI4:
659 case DT_UI8:
660 case DT_URI:
661 case DT_UUID:
662 if (!datatypes_schema)
664 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
665 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION "\n");
667 /* Hopefully they don't need much in the way of XDR datatypes support... */
668 return S_OK;
671 if (content && xmlStrlen(content))
673 tmp_doc = xmlNewDoc(NULL);
674 node = xmlNewChild((xmlNodePtr)tmp_doc, NULL, dt_to_str(dt), content);
675 ns = xmlNewNs(node, DT_nsURI, BAD_CAST "dt");
676 xmlSetNs(node, ns);
677 xmlDocSetRootElement(tmp_doc, node);
679 hr = Schema_validate_tree(datatypes_schema, (xmlNodePtr)tmp_doc);
680 xmlFreeDoc(tmp_doc);
682 else
683 { /* probably the node is being created manually and has no content yet */
684 hr = S_OK;
686 return hr;
687 default:
688 FIXME("need to handle dt:%s\n", debugstr_dt(dt));
689 return S_OK;
693 static inline xmlChar const* get_node_nsURI(xmlNodePtr node)
695 return (node->ns != NULL)? node->ns->href : NULL;
698 static inline cache_entry* get_entry(schema_cache* This, xmlChar const* nsURI)
700 return (!nsURI)? xmlHashLookup(This->cache, BAD_CAST "") :
701 xmlHashLookup(This->cache, nsURI);
704 static inline xmlSchemaPtr get_node_schema(schema_cache* This, xmlNodePtr node)
706 cache_entry* entry = get_entry(This, get_node_nsURI(node));
707 return (!entry)? NULL : entry->schema;
710 static xmlExternalEntityLoader _external_entity_loader;
712 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
713 xmlParserCtxtPtr ctxt)
715 xmlParserInputPtr input;
717 TRACE("(%s %s %p)\n", debugstr_a(URL), debugstr_a(ID), ctxt);
719 assert(MSXML_hInstance != NULL);
720 assert(datatypes_rsrc != NULL);
721 assert(datatypes_handle != NULL);
722 assert(datatypes_src != NULL);
724 /* TODO: if the desired schema is in the cache, load it from there */
725 if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
727 TRACE("loading built-in schema for %s\n", URL);
728 input = xmlNewStringInputStream(ctxt, datatypes_src);
730 else
732 input = _external_entity_loader(URL, ID, ctxt);
735 return input;
738 void schemasInit(void)
740 xmlChar* buf;
741 if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
743 FIXME("failed to find resource for %s\n", DT_nsURI);
744 return;
747 if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
749 FIXME("failed to load resource for %s\n", DT_nsURI);
750 return;
752 buf = LockResource(datatypes_handle);
753 datatypes_len = SizeofResource(MSXML_hInstance, datatypes_rsrc);
755 /* Resource is loaded as raw data,
756 * need a null-terminated string */
757 while (buf[datatypes_len - 1] != '>') datatypes_len--;
758 datatypes_src = heap_alloc(datatypes_len + 1);
759 memcpy(datatypes_src, buf, datatypes_len);
760 datatypes_src[datatypes_len] = 0;
762 if (xmlGetExternalEntityLoader() != external_entity_loader)
764 _external_entity_loader = xmlGetExternalEntityLoader();
765 xmlSetExternalEntityLoader(external_entity_loader);
769 void schemasCleanup(void)
771 xmlSchemaFree(datatypes_schema);
772 heap_free(datatypes_src);
773 xmlSetExternalEntityLoader(_external_entity_loader);
776 static LONG cache_entry_add_ref(cache_entry* entry)
778 LONG ref = InterlockedIncrement(&entry->ref);
779 TRACE("(%p)->(%d)\n", entry, ref);
780 return ref;
783 static LONG cache_entry_release(cache_entry* entry)
785 LONG ref = InterlockedDecrement(&entry->ref);
786 TRACE("(%p)->(%d)\n", entry, ref);
788 if (ref == 0)
790 if (entry->type == CacheEntryType_XSD)
792 xmldoc_release(entry->doc);
793 entry->schema->doc = NULL;
794 xmlSchemaFree(entry->schema);
796 else if (entry->type == CacheEntryType_XDR)
798 xmldoc_release(entry->doc);
799 xmldoc_release(entry->schema->doc);
800 entry->schema->doc = NULL;
801 xmlSchemaFree(entry->schema);
804 heap_free(entry);
806 return ref;
809 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl;
811 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
813 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
816 static inline schema_cache* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection* iface)
818 return CONTAINING_RECORD(iface, schema_cache, IXMLDOMSchemaCollection2_iface);
821 static inline schema_cache* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection *iface)
823 return iface->lpVtbl == (void*)&XMLDOMSchemaCollection2Vtbl ? impl_from_IXMLDOMSchemaCollection(iface) : NULL;
826 static inline CacheEntryType cache_type_from_xmlDocPtr(xmlDocPtr schema)
828 xmlNodePtr root = NULL;
829 if (schema)
830 root = xmlDocGetRootElement(schema);
831 if (root && root->ns)
834 if (xmlStrEqual(root->name, XDR_schema) &&
835 xmlStrEqual(root->ns->href, XDR_nsURI))
837 return CacheEntryType_XDR;
839 else if (xmlStrEqual(root->name, XSD_schema) &&
840 xmlStrEqual(root->ns->href, XSD_nsURI))
842 return CacheEntryType_XSD;
845 return CacheEntryType_Invalid;
848 static BOOL link_datatypes(xmlDocPtr schema)
850 xmlNodePtr root, next, child;
851 xmlNsPtr ns;
853 assert(xmlGetExternalEntityLoader() == external_entity_loader);
854 root = xmlDocGetRootElement(schema);
855 if (!root)
856 return FALSE;
858 for (ns = root->nsDef; ns != NULL; ns = ns->next)
860 if (xmlStrEqual(ns->href, DT_nsURI))
861 break;
864 if (!ns)
865 return FALSE;
867 next = xmlFirstElementChild(root);
868 child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
869 if (next) child = xmlAddPrevSibling(next, child);
870 xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
871 xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
873 return TRUE;
876 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION v)
878 cache_entry* entry = heap_alloc(sizeof(cache_entry));
879 xmlSchemaParserCtxtPtr spctx;
880 xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
882 link_datatypes(new_doc);
884 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
885 * do we need to do something special here? */
886 entry->type = CacheEntryType_XSD;
887 entry->ref = 0;
888 spctx = xmlSchemaNewDocParserCtxt(new_doc);
890 if ((entry->schema = Schema_parse(spctx)))
892 xmldoc_init(entry->schema->doc, v);
893 entry->doc = entry->schema->doc;
894 xmldoc_add_ref(entry->doc);
896 else
898 FIXME("failed to parse doc\n");
899 xmlFreeDoc(new_doc);
900 heap_free(entry);
901 entry = NULL;
903 xmlSchemaFreeParserCtxt(spctx);
904 return entry;
907 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI, MSXML_VERSION version)
909 cache_entry* entry = heap_alloc(sizeof(cache_entry));
910 xmlSchemaParserCtxtPtr spctx;
911 xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
913 link_datatypes(xsd_doc);
915 entry->type = CacheEntryType_XDR;
916 entry->ref = 0;
917 spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
919 if ((entry->schema = Schema_parse(spctx)))
921 entry->doc = new_doc;
922 xmldoc_init(entry->schema->doc, version);
923 xmldoc_init(entry->doc, version);
924 xmldoc_add_ref(entry->doc);
925 xmldoc_add_ref(entry->schema->doc);
927 else
929 FIXME("failed to parse doc\n");
930 xmlFreeDoc(new_doc);
931 xmlFreeDoc(xsd_doc);
932 heap_free(entry);
933 entry = NULL;
935 xmlSchemaFreeParserCtxt(spctx);
937 return entry;
940 static cache_entry* cache_entry_from_url(VARIANT url, xmlChar const* nsURI, MSXML_VERSION version)
942 cache_entry* entry;
943 IXMLDOMDocument3* domdoc = NULL;
944 xmlDocPtr doc = NULL;
945 HRESULT hr = DOMDocument_create(version, (void**)&domdoc);
946 VARIANT_BOOL b = VARIANT_FALSE;
947 CacheEntryType type = CacheEntryType_Invalid;
949 if (hr != S_OK)
951 FIXME("failed to create domdoc\n");
952 return NULL;
954 assert(domdoc != NULL);
955 assert(V_VT(&url) == VT_BSTR);
957 hr = IXMLDOMDocument3_load(domdoc, url, &b);
958 if (hr != S_OK)
960 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr);
961 if (b != VARIANT_TRUE)
963 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url)));
964 IXMLDOMDocument3_Release(domdoc);
965 return NULL;
968 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
969 type = cache_type_from_xmlDocPtr(doc);
971 switch (type)
973 case CacheEntryType_XSD:
974 entry = cache_entry_from_xsd_doc(doc, nsURI, version);
975 break;
976 case CacheEntryType_XDR:
977 entry = cache_entry_from_xdr_doc(doc, nsURI, version);
978 break;
979 default:
980 entry = NULL;
981 FIXME("invalid schema\n");
982 break;
984 IXMLDOMDocument3_Release(domdoc);
986 return entry;
989 static void cache_free(void* data, XMLHASH_CONST xmlChar* name /* ignored */)
991 cache_entry_release((cache_entry*)data);
994 /* returns index or -1 if not found */
995 static int cache_free_uri(schema_cache *cache, const xmlChar *uri)
997 int i;
999 for (i = 0; i < cache->count; i++)
1000 if (xmlStrEqual(cache->uris[i], uri))
1002 heap_free(cache->uris[i]);
1003 return i;
1006 return -1;
1009 static void cache_add_entry(schema_cache *cache, const xmlChar *uri, cache_entry *entry)
1011 int i;
1013 /* meaning no entry found with this name */
1014 if (xmlHashRemoveEntry(cache->cache, uri, cache_free))
1016 if (cache->count == cache->allocated)
1018 cache->allocated *= 2;
1019 cache->uris = heap_realloc(cache->uris, cache->allocated*sizeof(xmlChar*));
1021 i = cache->count++;
1023 else
1024 i = cache_free_uri(cache, uri);
1026 cache->uris[i] = heap_strdupxmlChar(uri);
1027 xmlHashAddEntry(cache->cache, uri, entry);
1030 static void cache_remove_entry(schema_cache *cache, const xmlChar *uri)
1032 /* adjust index if entry was really removed */
1033 if (xmlHashRemoveEntry(cache->cache, uri, cache_free) == 0)
1035 int i = cache_free_uri(cache, uri);
1036 if (i == -1) return;
1037 /* shift array */
1038 if (i != --cache->count)
1039 memmove(&cache->uris[i], &cache->uris[i+1], (cache->count-i)*sizeof(xmlChar*));
1043 /* This one adds all namespaces defined in document to a cache, without anything
1044 associated with uri obviously.
1045 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
1046 it uses additional node type to describe namespace definition attribute while
1047 in msxml it's expected to be a normal attribute - as a workaround document is
1048 queried at libxml2 level here. */
1049 HRESULT cache_from_doc_ns(IXMLDOMSchemaCollection2 *iface, xmlnode *node)
1051 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1052 static const xmlChar query[] = "//*/namespace::*";
1053 xmlXPathObjectPtr nodeset;
1054 xmlXPathContextPtr ctxt;
1056 This->read_only = 1;
1058 ctxt = xmlXPathNewContext(node->node->doc);
1060 nodeset = xmlXPathEvalExpression(query, ctxt);
1061 xmlXPathFreeContext(ctxt);
1063 if (nodeset)
1065 int pos = 0, len = xmlXPathNodeSetGetLength(nodeset->nodesetval);
1067 while (pos < len)
1069 xmlNodePtr node = xmlXPathNodeSetItem(nodeset->nodesetval, pos);
1070 if (node->type == XML_NAMESPACE_DECL)
1072 static const xmlChar defns[] = "http://www.w3.org/XML/1998/namespace";
1073 xmlNsPtr ns = (xmlNsPtr)node;
1074 cache_entry *entry;
1076 /* filter out default uri */
1077 if (xmlStrEqual(ns->href, defns))
1079 pos++;
1080 continue;
1083 entry = heap_alloc(sizeof(cache_entry));
1084 entry->type = CacheEntryType_NS;
1085 entry->ref = 1;
1086 entry->schema = NULL;
1087 entry->doc = NULL;
1089 cache_add_entry(This, ns->href, entry);
1091 pos++;
1094 xmlXPathFreeObject(nodeset);
1097 return S_OK;
1100 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
1101 REFIID riid, void** ppvObject)
1103 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1105 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1107 if ( IsEqualIID(riid, &IID_IUnknown) ||
1108 IsEqualIID(riid, &IID_IDispatch) ||
1109 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
1110 IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
1112 *ppvObject = iface;
1114 else if(This->version == MSXML6 && IsEqualIID(riid, &CLSID_XMLSchemaCache60))
1117 * Version 6 can be queried for an interface with IID equal to CLSID.
1118 * There is no public interface with that IID and returned pointer
1119 * is equal to returned IXMLDOMSchemaCollection2 iface. We assume
1120 * that it's just another way for querying IXMLDOMSchemaCollection2
1121 * interface. Office 2013 ClickToRun installer uses this.
1123 WARN("riid CLSID_XMLSchemaCache60, returning IXMLDOMSchemaCollection2 interface.\n");
1124 *ppvObject = iface;
1126 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
1128 return *ppvObject ? S_OK : E_NOINTERFACE;
1130 else if(IsEqualGUID( riid, &IID_ISupportErrorInfo ))
1132 return node_create_supporterrorinfo(schema_cache_se_tids, ppvObject);
1134 else
1136 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1137 *ppvObject = NULL;
1138 return E_NOINTERFACE;
1141 IXMLDOMSchemaCollection2_AddRef(iface);
1143 return S_OK;
1146 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
1148 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1149 LONG ref = InterlockedIncrement(&This->ref);
1150 TRACE("(%p)->(%d)\n", This, ref);
1151 return ref;
1154 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
1156 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1157 LONG ref = InterlockedDecrement(&This->ref);
1158 TRACE("(%p)->(%d)\n", This, ref);
1160 if (ref == 0)
1162 int i;
1164 for (i = 0; i < This->count; i++)
1165 heap_free(This->uris[i]);
1166 heap_free(This->uris);
1167 xmlHashFree(This->cache, cache_free);
1168 heap_free(This);
1171 return ref;
1174 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
1175 UINT* pctinfo)
1177 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1178 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1181 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
1182 UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
1184 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1185 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
1186 iTInfo, lcid, ppTInfo);
1189 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
1190 REFIID riid, LPOLESTR* rgszNames,
1191 UINT cNames, LCID lcid, DISPID* rgDispId)
1193 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1194 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
1195 riid, rgszNames, cNames, lcid, rgDispId);
1198 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
1199 DISPID dispIdMember, REFIID riid, LCID lcid,
1200 WORD wFlags, DISPPARAMS* pDispParams,
1201 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
1202 UINT* puArgErr)
1204 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1205 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
1206 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1209 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
1211 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1212 xmlChar* name;
1214 TRACE("(%p)->(%s %s)\n", This, debugstr_w(uri), debugstr_variant(&var));
1216 if (This->read_only) return E_FAIL;
1218 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1220 switch (V_VT(&var))
1222 case VT_NULL:
1224 cache_remove_entry(This, name);
1226 break;
1228 case VT_BSTR:
1230 cache_entry* entry = cache_entry_from_url(var, name, This->version);
1232 if (entry)
1234 cache_entry_add_ref(entry);
1236 else
1238 heap_free(name);
1239 return E_FAIL;
1242 cache_add_entry(This, name, entry);
1244 break;
1246 case VT_DISPATCH:
1247 case VT_UNKNOWN:
1249 xmlDocPtr doc = NULL;
1250 cache_entry* entry;
1251 CacheEntryType type;
1252 IXMLDOMNode* domnode = NULL;
1253 IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IXMLDOMNode, (void**)&domnode);
1255 if (domnode)
1257 DOMNodeType type;
1259 IXMLDOMNode_get_nodeType(domnode, &type);
1260 switch (type)
1262 case NODE_ELEMENT:
1264 IXMLDOMDocument *domdoc;
1265 VARIANT_BOOL b;
1266 BSTR xml;
1268 IXMLDOMNode_get_xml(domnode, &xml);
1269 DOMDocument_create(This->version, (void**)&domdoc);
1270 IXMLDOMDocument_loadXML(domdoc, xml, &b);
1271 SysFreeString(xml);
1272 doc = xmlNodePtr_from_domnode((IXMLDOMNode*)domdoc, XML_DOCUMENT_NODE)->doc;
1273 break;
1275 default:
1276 doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
1277 break;
1281 if (!doc)
1283 IXMLDOMNode_Release(domnode);
1284 heap_free(name);
1285 return E_INVALIDARG;
1287 type = cache_type_from_xmlDocPtr(doc);
1289 if (type == CacheEntryType_XSD)
1291 entry = cache_entry_from_xsd_doc(doc, name, This->version);
1293 else if (type == CacheEntryType_XDR)
1295 entry = cache_entry_from_xdr_doc(doc, name, This->version);
1297 else
1299 WARN("invalid schema!\n");
1300 entry = NULL;
1303 IXMLDOMNode_Release(domnode);
1305 if (entry)
1307 cache_entry_add_ref(entry);
1309 else
1311 heap_free(name);
1312 return E_FAIL;
1315 cache_add_entry(This, name, entry);
1317 break;
1319 default:
1320 FIXME("arg type is not supported, %s\n", debugstr_variant(&var));
1321 heap_free(name);
1322 return E_INVALIDARG;
1324 heap_free(name);
1325 return S_OK;
1328 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
1329 IXMLDOMNode** node)
1331 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1332 cache_entry* entry;
1333 xmlChar* name;
1335 TRACE("(%p)->(%s %p)\n", This, debugstr_w(uri), node);
1337 if (This->version == MSXML6)
1339 if (node) *node = NULL;
1340 return E_NOTIMPL;
1343 if (!node)
1344 return E_POINTER;
1346 *node = NULL;
1348 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1349 entry = (cache_entry*) xmlHashLookup(This->cache, name);
1350 heap_free(name);
1352 /* TODO: this should be read-only */
1353 if (entry && entry->doc)
1354 return get_domdoc_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
1356 return S_OK;
1359 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
1361 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1362 xmlChar* name;
1364 TRACE("(%p)->(%s)\n", This, debugstr_w(uri));
1366 if (This->version == MSXML6) return E_NOTIMPL;
1368 name = uri ? xmlchar_from_wchar(uri) : xmlchar_from_wchar(emptyW);
1369 cache_remove_entry(This, name);
1370 heap_free(name);
1371 return S_OK;
1374 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
1376 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1377 TRACE("(%p)->(%p)\n", This, length);
1379 if (!length)
1380 return E_POINTER;
1382 *length = This->count;
1383 return S_OK;
1386 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
1387 LONG index, BSTR* uri)
1389 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1391 TRACE("(%p)->(%i %p)\n", This, index, uri);
1393 if (!uri)
1394 return E_POINTER;
1396 if (This->version == MSXML6)
1397 *uri = NULL;
1399 if (index >= This->count)
1400 return E_FAIL;
1402 *uri = bstr_from_xmlChar(This->uris[index]);
1403 return S_OK;
1406 static void cache_copy(void* data, void* dest, XMLHASH_CONST xmlChar* name)
1408 schema_cache* This = (schema_cache*) dest;
1409 cache_entry* entry = (cache_entry*) data;
1411 if (xmlHashLookup(This->cache, name) == NULL)
1413 cache_entry_add_ref(entry);
1414 cache_add_entry(This, name, entry);
1418 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
1419 IXMLDOMSchemaCollection* collection)
1421 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1422 schema_cache* That;
1424 TRACE("(%p)->(%p)\n", This, collection);
1426 if (!collection)
1427 return E_POINTER;
1429 That = unsafe_impl_from_IXMLDOMSchemaCollection(collection);
1430 if (!That)
1432 ERR("external collection implementation\n");
1433 return E_FAIL;
1436 /* TODO: detect errors while copying & return E_FAIL */
1437 xmlHashScan(That->cache, cache_copy, This);
1439 return S_OK;
1442 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface, IUnknown** enumv)
1444 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1445 TRACE("(%p)->(%p)\n", This, enumv);
1446 return create_enumvariant((IUnknown*)iface, TRUE, &schemacache_enumvariant, (IEnumVARIANT**)enumv);
1449 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
1451 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1452 FIXME("(%p): stub\n", This);
1453 return E_NOTIMPL;
1456 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1457 VARIANT_BOOL value)
1459 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1460 FIXME("(%p)->(%d): stub\n", This, value);
1462 This->validateOnLoad = value;
1463 /* it's ok to disable it, cause we don't validate on load anyway */
1464 if (value == VARIANT_FALSE) return S_OK;
1466 return E_NOTIMPL;
1469 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
1470 VARIANT_BOOL* value)
1472 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1473 TRACE("(%p)->(%p)\n", This, value);
1475 if (!value) return E_POINTER;
1476 *value = This->validateOnLoad;
1478 return S_OK;
1481 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
1482 BSTR namespaceURI, ISchema** schema)
1484 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1485 FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(namespaceURI), schema);
1486 if (schema)
1487 *schema = NULL;
1488 return E_NOTIMPL;
1491 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
1492 IXMLDOMNode* node, ISchemaItem** item)
1494 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1495 FIXME("(%p)->(%p %p): stub\n", This, node, item);
1496 if (item)
1497 *item = NULL;
1498 return E_NOTIMPL;
1501 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl =
1503 schema_cache_QueryInterface,
1504 schema_cache_AddRef,
1505 schema_cache_Release,
1506 schema_cache_GetTypeInfoCount,
1507 schema_cache_GetTypeInfo,
1508 schema_cache_GetIDsOfNames,
1509 schema_cache_Invoke,
1510 schema_cache_add,
1511 schema_cache_get,
1512 schema_cache_remove,
1513 schema_cache_get_length,
1514 schema_cache_get_namespaceURI,
1515 schema_cache_addCollection,
1516 schema_cache_get__newEnum,
1517 schema_cache_validate,
1518 schema_cache_put_validateOnLoad,
1519 schema_cache_get_validateOnLoad,
1520 schema_cache_getSchema,
1521 schema_cache_getDeclaration
1524 static xmlSchemaElementPtr lookup_schema_elemDecl(xmlSchemaPtr schema, xmlNodePtr node)
1526 xmlSchemaElementPtr decl = NULL;
1527 xmlChar const* nsURI = get_node_nsURI(node);
1529 TRACE("(%p, %p)\n", schema, node);
1531 if (xmlStrEqual(nsURI, schema->targetNamespace))
1532 decl = xmlHashLookup(schema->elemDecl, node->name);
1534 if (!decl && xmlHashSize(schema->schemasImports) > 1)
1536 FIXME("declaration not found in main schema - need to check schema imports!\n");
1537 /*xmlSchemaImportPtr import;
1538 if (nsURI == NULL)
1539 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1540 else
1541 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1543 if (import != NULL)
1544 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1547 return decl;
1550 static inline xmlNodePtr lookup_schema_element(xmlSchemaPtr schema, xmlNodePtr node)
1552 xmlSchemaElementPtr decl = lookup_schema_elemDecl(schema, node);
1553 while (decl != NULL && decl->refDecl != NULL)
1554 decl = decl->refDecl;
1555 return (decl != NULL)? decl->node : NULL;
1558 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
1560 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1561 xmlSchemaPtr schema;
1563 TRACE("(%p, %p)\n", This, tree);
1565 if (!tree)
1566 return E_POINTER;
1568 if (tree->type == XML_DOCUMENT_NODE)
1569 tree = xmlDocGetRootElement(tree->doc);
1571 schema = get_node_schema(This, tree);
1572 /* TODO: if the ns is not in the cache, and it's a URL,
1573 * do we try to load from that? */
1574 if (schema)
1575 return Schema_validate_tree(schema, tree);
1576 else
1577 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree));
1579 return E_FAIL;
1582 XDR_DT SchemaCache_get_node_dt(IXMLDOMSchemaCollection2* iface, xmlNodePtr node)
1584 schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
1585 xmlSchemaPtr schema = get_node_schema(This, node);
1586 XDR_DT dt = DT_INVALID;
1588 TRACE("(%p, %p)\n", This, node);
1590 if (node->ns && xmlStrEqual(node->ns->href, DT_nsURI))
1592 dt = str_to_dt(node->name, -1);
1594 else if (schema)
1596 xmlChar* str;
1597 xmlNodePtr schema_node = lookup_schema_element(schema, node);
1599 str = xmlGetNsProp(schema_node, BAD_CAST "dt", DT_nsURI);
1600 if (str)
1602 dt = str_to_dt(str, -1);
1603 xmlFree(str);
1607 return dt;
1610 static const tid_t schemacache_iface_tids[] = {
1611 IXMLDOMSchemaCollection2_tid,
1615 static dispex_static_data_t schemacache_dispex = {
1616 NULL,
1617 IXMLDOMSchemaCollection2_tid,
1618 NULL,
1619 schemacache_iface_tids
1622 HRESULT SchemaCache_create(MSXML_VERSION version, void** obj)
1624 schema_cache* This = heap_alloc(sizeof(schema_cache));
1625 if (!This)
1626 return E_OUTOFMEMORY;
1628 TRACE("(%d %p)\n", version, obj);
1630 This->IXMLDOMSchemaCollection2_iface.lpVtbl = &XMLDOMSchemaCollection2Vtbl;
1631 This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
1632 This->allocated = 10;
1633 This->count = 0;
1634 This->uris = heap_alloc(This->allocated*sizeof(xmlChar*));
1635 This->ref = 1;
1636 This->version = version;
1637 This->validateOnLoad = VARIANT_TRUE;
1638 This->read_only = 0;
1639 init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSchemaCollection2_iface, &schemacache_dispex);
1641 *obj = &This->IXMLDOMSchemaCollection2_iface;
1642 return S_OK;
1645 #else
1647 HRESULT SchemaCache_create(MSXML_VERSION version, void** obj)
1649 MESSAGE("This program tried to use a SchemaCache object, but\n"
1650 "libxml2 support was not present at compile time.\n");
1651 return E_NOTIMPL;
1654 #endif