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
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>
46 #include "wine/debug.h"
48 #include "msxml_private.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(msxml
);
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
57 /* This is just the number of buckets, should be prime */
58 #define DEFAULT_HASHTABLE_SIZE 17
62 xmlDocPtr
XDR_to_XSD_doc(xmlDocPtr xdr_doc
, xmlChar
const* nsURI
);
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";
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
;
76 static const WCHAR emptyW
[] = {0};
84 typedef enum _SCHEMA_TYPE
{
90 typedef struct _schema_cache
92 const struct IXMLDOMSchemaCollection2Vtbl
* lpVtbl
;
93 MSXML_VERSION version
;
94 xmlHashTablePtr cache
;
98 typedef struct _cache_entry
106 typedef struct _cache_index_data
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
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";
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};
193 static const BYTE hash_assoc_values
[] =
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
223 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
227 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
231 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
235 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
239 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
240 static void parser_serror(void* ctx
, xmlErrorPtr err
)
242 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
246 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
248 TRACE("(%p)\n", spctx
);
250 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
251 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
252 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
255 return xmlSchemaParse(spctx
);
258 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
262 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
266 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
270 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
274 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
275 static void validate_serror(void* ctx
, xmlErrorPtr err
)
277 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
281 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
283 xmlSchemaValidCtxtPtr svctx
;
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
);
295 if (tree
->type
== XML_DOCUMENT_NODE
)
296 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
298 err
= xmlSchemaValidateOneElement(svctx
, tree
);
300 xmlSchemaFreeValidCtxt(svctx
);
301 return err
? S_FALSE
: S_OK
;
304 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
306 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
311 hval
+= hash_assoc_values
[str
[10]];
314 hval
+= hash_assoc_values
[str
[9]];
317 hval
+= hash_assoc_values
[str
[8]];
320 hval
+= hash_assoc_values
[str
[7]];
323 hval
+= hash_assoc_values
[str
[6]];
326 hval
+= hash_assoc_values
[str
[5]];
329 hval
+= hash_assoc_values
[str
[4]];
332 hval
+= hash_assoc_values
[str
[3]];
335 hval
+= hash_assoc_values
[str
[2]];
338 hval
+= hash_assoc_values
[str
[1]];
341 hval
+= hash_assoc_values
[str
[0]];
347 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
349 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
354 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
357 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
360 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
363 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
366 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
369 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
372 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
375 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
378 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
381 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
384 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
390 static const xmlChar
*const DT_string_table
[DT__N_TYPES
] =
430 static const WCHAR
*const DT_wstring_table
[DT__N_TYPES
] =
470 static const XDR_DT DT_lookup_table
[] =
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,
532 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
534 DWORD hash
= dt_hash(str
, len
);
535 XDR_DT dt
= DT_INVALID
;
537 if (hash
<= DT_MAX_HASH_VALUE
)
538 dt
= DT_lookup_table
[hash
];
540 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
546 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
548 DWORD hash
= dt_hash_bstr(bstr
, len
);
549 XDR_DT dt
= DT_INVALID
;
551 if (hash
<= DT_MAX_HASH_VALUE
)
552 dt
= DT_lookup_table
[hash
];
554 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
560 xmlChar
const* dt_to_str(XDR_DT dt
)
562 if (dt
== DT_INVALID
)
565 return DT_string_table
[dt
];
568 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
570 if (dt
== DT_INVALID
)
573 return DT_wstring_table
[dt
];
576 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
583 TRACE("(dt:%s, %s)\n", dt_to_str(dt
), wine_dbgstr_a((char const*)content
));
585 if (!datatypes_schema
)
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
);
627 if (!datatypes_schema
)
629 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
630 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
632 /* Hopefully they don't need much in the way of XDR datatypes support... */
636 if (content
&& xmlStrlen(content
))
638 tmp_doc
= xmlNewDoc(NULL
);
639 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
640 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
642 xmlDocSetRootElement(tmp_doc
, node
);
644 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
648 { /* probably the node is being created manually and has no content yet */
653 FIXME("need to handle dt:%s\n", dt_to_str(dt
));
658 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
660 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
663 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
665 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
666 xmlHashLookup(This
->cache
, nsURI
);
669 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
671 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
672 return (!entry
)? NULL
: entry
->schema
;
675 static xmlExternalEntityLoader _external_entity_loader
;
677 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
678 xmlParserCtxtPtr ctxt
)
680 xmlParserInputPtr input
;
682 TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL
), wine_dbgstr_a(ID
), ctxt
);
684 assert(MSXML_hInstance
!= NULL
);
685 assert(datatypes_rsrc
!= NULL
);
686 assert(datatypes_handle
!= NULL
);
687 assert(datatypes_src
!= NULL
);
689 /* TODO: if the desired schema is in the cache, load it from there */
690 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
692 TRACE("loading built-in schema for %s\n", URL
);
693 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
697 input
= _external_entity_loader(URL
, ID
, ctxt
);
703 void schemasInit(void)
707 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
709 FIXME("failed to find resource for %s\n", DT_nsURI
);
713 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
715 FIXME("failed to load resource for %s\n", DT_nsURI
);
718 buf
= LockResource(datatypes_handle
);
719 len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
) - 1;
721 /* Resource is loaded as raw data,
722 * need a null-terminated string */
723 while (buf
[len
] != '>')
725 datatypes_src
= BAD_CAST buf
;
726 datatypes_len
= len
+ 1;
728 if (xmlGetExternalEntityLoader() != external_entity_loader
)
730 _external_entity_loader
= xmlGetExternalEntityLoader();
731 xmlSetExternalEntityLoader(external_entity_loader
);
735 void schemasCleanup(void)
737 xmlSchemaFree(datatypes_schema
);
738 xmlSetExternalEntityLoader(_external_entity_loader
);
741 static LONG
cache_entry_add_ref(cache_entry
* entry
)
743 LONG ref
= InterlockedIncrement(&entry
->ref
);
744 TRACE("%p new ref %d\n", entry
, ref
);
748 static LONG
cache_entry_release(cache_entry
* entry
)
750 LONG ref
= InterlockedDecrement(&entry
->ref
);
751 TRACE("%p new ref %d\n", entry
, ref
);
755 if (entry
->type
== SCHEMA_TYPE_XSD
)
757 xmldoc_release(entry
->doc
);
758 entry
->schema
->doc
= NULL
;
759 xmlSchemaFree(entry
->schema
);
762 else /* SCHEMA_TYPE_XDR */
764 xmldoc_release(entry
->doc
);
765 xmldoc_release(entry
->schema
->doc
);
766 entry
->schema
->doc
= NULL
;
767 xmlSchemaFree(entry
->schema
);
774 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
776 return (schema_cache
*)((char*)iface
- FIELD_OFFSET(schema_cache
, lpVtbl
));
779 static inline SCHEMA_TYPE
schema_type_from_xmlDocPtr(xmlDocPtr schema
)
781 xmlNodePtr root
= NULL
;
783 root
= xmlDocGetRootElement(schema
);
784 if (root
&& root
->ns
)
787 if (xmlStrEqual(root
->name
, XDR_schema
) &&
788 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
790 return SCHEMA_TYPE_XDR
;
792 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
793 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
795 return SCHEMA_TYPE_XSD
;
798 return SCHEMA_TYPE_INVALID
;
801 static BOOL
link_datatypes(xmlDocPtr schema
)
803 xmlNodePtr root
, next
, child
;
806 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader
);
807 root
= xmlDocGetRootElement(schema
);
811 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
813 if (xmlStrEqual(ns
->href
, DT_nsURI
))
820 next
= xmlFirstElementChild(root
);
821 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
822 if (next
) child
= xmlAddPrevSibling(next
, child
);
823 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
824 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
829 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
831 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
832 xmlSchemaParserCtxtPtr spctx
;
833 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
835 link_datatypes(new_doc
);
837 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
838 * do we need to do something special here? */
839 entry
->type
= SCHEMA_TYPE_XSD
;
841 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
843 if ((entry
->schema
= Schema_parse(spctx
)))
845 xmldoc_init(entry
->schema
->doc
, v
);
846 entry
->doc
= entry
->schema
->doc
;
847 xmldoc_add_ref(entry
->doc
);
851 FIXME("failed to parse doc\n");
856 xmlSchemaFreeParserCtxt(spctx
);
860 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
862 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
863 xmlSchemaParserCtxtPtr spctx
;
864 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
866 link_datatypes(xsd_doc
);
868 entry
->type
= SCHEMA_TYPE_XDR
;
870 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
872 if ((entry
->schema
= Schema_parse(spctx
)))
874 entry
->doc
= new_doc
;
875 xmldoc_init(entry
->schema
->doc
, version
);
876 xmldoc_init(entry
->doc
, version
);
877 xmldoc_add_ref(entry
->doc
);
878 xmldoc_add_ref(entry
->schema
->doc
);
882 FIXME("failed to parse doc\n");
888 xmlSchemaFreeParserCtxt(spctx
);
893 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
896 IXMLDOMDocument3
* domdoc
= NULL
;
897 xmlDocPtr doc
= NULL
;
898 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
899 VARIANT_BOOL b
= VARIANT_FALSE
;
900 SCHEMA_TYPE type
= SCHEMA_TYPE_INVALID
;
904 FIXME("failed to create domdoc\n");
907 assert(domdoc
!= NULL
);
908 assert(V_VT(&url
) == VT_BSTR
);
910 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
913 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
914 if (b
!= VARIANT_TRUE
)
916 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url
)));
917 IXMLDOMDocument3_Release(domdoc
);
921 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
922 type
= schema_type_from_xmlDocPtr(doc
);
926 case SCHEMA_TYPE_XSD
:
927 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
929 case SCHEMA_TYPE_XDR
:
930 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
932 case SCHEMA_TYPE_INVALID
:
934 FIXME("invalid schema\n");
937 IXMLDOMDocument3_Release(domdoc
);
942 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
943 REFIID riid
, void** ppvObject
)
945 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
947 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
949 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
950 IsEqualIID(riid
, &IID_IDispatch
) ||
951 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
952 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
958 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
960 return E_NOINTERFACE
;
963 IXMLDOMSchemaCollection2_AddRef(iface
);
968 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
970 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
971 LONG ref
= InterlockedIncrement(&This
->ref
);
972 TRACE("%p new ref %d\n", This
, ref
);
976 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
978 cache_entry_release((cache_entry
*)data
);
981 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
983 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
984 LONG ref
= InterlockedDecrement(&This
->ref
);
985 TRACE("%p new ref %d\n", This
, ref
);
989 xmlHashFree(This
->cache
, cache_free
);
996 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
999 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1001 TRACE("(%p)->(%p)\n", This
, pctinfo
);
1008 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1009 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1011 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1014 TRACE("(%p)->(%u %u %p)\n", This
, iTInfo
, lcid
, ppTInfo
);
1016 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, ppTInfo
);
1021 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1022 REFIID riid
, LPOLESTR
* rgszNames
,
1023 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1025 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1026 ITypeInfo
* typeinfo
;
1029 TRACE("(%p)->(%s %p %u %u %p)\n", This
, debugstr_guid(riid
), rgszNames
, cNames
,
1032 if(!rgszNames
|| cNames
== 0 || !rgDispId
)
1033 return E_INVALIDARG
;
1035 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, &typeinfo
);
1038 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, rgszNames
, cNames
, rgDispId
);
1039 ITypeInfo_Release(typeinfo
);
1045 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1046 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1047 WORD wFlags
, DISPPARAMS
* pDispParams
,
1048 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1051 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1052 ITypeInfo
* typeinfo
;
1055 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This
, dispIdMember
, debugstr_guid(riid
),
1056 lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1058 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, &typeinfo
);
1061 hr
= ITypeInfo_Invoke(typeinfo
, &(This
->lpVtbl
), dispIdMember
, wFlags
, pDispParams
,
1062 pVarResult
, pExcepInfo
, puArgErr
);
1063 ITypeInfo_Release(typeinfo
);
1069 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1071 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1072 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1073 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1079 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1085 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1089 cache_entry_add_ref(entry
);
1097 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1098 xmlHashAddEntry(This
->cache
, name
, entry
);
1104 xmlDocPtr doc
= NULL
;
1107 IXMLDOMNode
* domnode
= NULL
;
1108 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1111 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1115 IXMLDOMNode_Release(domnode
);
1117 return E_INVALIDARG
;
1119 type
= schema_type_from_xmlDocPtr(doc
);
1121 if (type
== SCHEMA_TYPE_XSD
)
1123 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1125 else if (type
== SCHEMA_TYPE_XDR
)
1127 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1131 WARN("invalid schema!\n");
1135 IXMLDOMNode_Release(domnode
);
1139 cache_entry_add_ref(entry
);
1147 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1148 xmlHashAddEntry(This
->cache
, name
, entry
);
1155 return E_INVALIDARG
;
1162 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1165 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1168 TRACE("(%p)->(%s, %p)\n", This
, wine_dbgstr_w(uri
), node
);
1173 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1174 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1177 /* TODO: this should be read-only */
1179 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1185 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1187 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1188 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1189 TRACE("(%p)->(%s)\n", This
, wine_dbgstr_w(uri
));
1191 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1196 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1198 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1199 TRACE("(%p)->(%p)\n", This
, length
);
1203 *length
= xmlHashSize(This
->cache
);
1207 static void cache_index(void* data
/* ignored */, void* index
, xmlChar
* name
)
1209 cache_index_data
* index_data
= (cache_index_data
*)index
;
1211 if (index_data
->index
-- == 0)
1212 *index_data
->out
= bstr_from_xmlChar(name
);
1215 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1216 LONG index
, BSTR
* len
)
1218 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1219 cache_index_data data
= {index
,len
};
1220 TRACE("(%p)->(%i, %p)\n", This
, index
, len
);
1226 if (index
>= xmlHashSize(This
->cache
))
1229 xmlHashScan(This
->cache
, cache_index
, &data
);
1233 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1235 schema_cache
* This
= (schema_cache
*) dest
;
1236 cache_entry
* entry
= (cache_entry
*) data
;
1238 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1240 cache_entry_add_ref(entry
);
1241 xmlHashAddEntry(This
->cache
, name
, entry
);
1245 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1246 IXMLDOMSchemaCollection
* otherCollection
)
1248 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1249 schema_cache
* That
= impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2
*)otherCollection
);
1250 TRACE("(%p)->(%p)\n", This
, That
);
1252 if (!otherCollection
)
1255 /* TODO: detect errors while copying & return E_FAIL */
1256 xmlHashScan(That
->cache
, cache_copy
, This
);
1261 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
1270 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1276 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1277 VARIANT_BOOL validateOnLoad
)
1283 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1284 VARIANT_BOOL
* validateOnLoad
)
1290 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1291 BSTR namespaceURI
, ISchema
** schema
)
1299 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1300 IXMLDOMNode
* node
, ISchemaItem
** item
)
1308 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl
=
1310 schema_cache_QueryInterface
,
1311 schema_cache_AddRef
,
1312 schema_cache_Release
,
1313 schema_cache_GetTypeInfoCount
,
1314 schema_cache_GetTypeInfo
,
1315 schema_cache_GetIDsOfNames
,
1316 schema_cache_Invoke
,
1319 schema_cache_remove
,
1320 schema_cache_get_length
,
1321 schema_cache_get_namespaceURI
,
1322 schema_cache_addCollection
,
1323 schema_cache_get__newEnum
,
1324 schema_cache_validate
,
1325 schema_cache_put_validateOnLoad
,
1326 schema_cache_get_validateOnLoad
,
1327 schema_cache_getSchema
,
1328 schema_cache_getDeclaration
1331 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1333 xmlSchemaElementPtr decl
= NULL
;
1334 xmlChar
const* nsURI
= get_node_nsURI(node
);
1336 TRACE("(%p, %p)\n", schema
, node
);
1338 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1339 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1341 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1343 FIXME("declaration not found in main schema - need to check schema imports!\n");
1344 /*xmlSchemaImportPtr import;
1346 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1348 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1351 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1357 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1359 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1360 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1361 decl
= decl
->refDecl
;
1362 return (decl
!= NULL
)? decl
->node
: NULL
;
1365 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1367 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1368 xmlSchemaPtr schema
;
1370 TRACE("(%p, %p)\n", This
, tree
);
1375 if (tree
->type
== XML_DOCUMENT_NODE
)
1376 tree
= xmlDocGetRootElement(tree
->doc
);
1378 schema
= get_node_schema(This
, tree
);
1379 /* TODO: if the ns is not in the cache, and it's a URL,
1380 * do we try to load from that? */
1382 return Schema_validate_tree(schema
, tree
);
1384 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1389 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1391 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1392 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1393 XDR_DT dt
= DT_INVALID
;
1395 TRACE("(%p, %p)\n", This
, node
);
1397 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1399 dt
= str_to_dt(node
->name
, -1);
1404 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1406 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1409 dt
= str_to_dt(str
, -1);
1417 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* pUnkOuter
, void** ppObj
)
1419 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1421 return E_OUTOFMEMORY
;
1423 This
->lpVtbl
= &schema_cache_vtbl
;
1424 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1426 This
->version
= version
;
1428 *ppObj
= &This
->lpVtbl
;
1434 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* pUnkOuter
, void** ppObj
)
1436 MESSAGE("This program tried to use a SchemaCache object, but\n"
1437 "libxml2 support was not present at compile time.\n");