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>
38 # include <libxml/xpath.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
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};
85 * CacheType_NS is a special type used for read-only collection build with
86 * IXMLDOMDocument2::namespaces()
89 CacheEntryType_Invalid
,
98 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface
;
101 MSXML_VERSION version
;
102 xmlHashTablePtr cache
;
107 VARIANT_BOOL validateOnLoad
;
119 static const tid_t schema_cache_se_tids
[] = {
120 IXMLDOMSchemaCollection_tid
,
121 IXMLDOMSchemaCollection2_tid
,
125 /* datatypes lookup stuff
126 * generated with help from gperf */
127 #define DT_MIN_STR_LEN 2
128 #define DT_MAX_STR_LEN 11
129 #define DT_MIN_HASH_VALUE 2
130 #define DT_MAX_HASH_VALUE 115
132 static const xmlChar DT_bin_base64
[] = "bin.base64";
133 static const xmlChar DT_bin_hex
[] = "bin.hex";
134 static const xmlChar DT_boolean
[] = "boolean";
135 static const xmlChar DT_char
[] = "char";
136 static const xmlChar DT_date
[] = "date";
137 static const xmlChar DT_date_tz
[] = "date.tz";
138 static const xmlChar DT_dateTime
[] = "dateTime";
139 static const xmlChar DT_dateTime_tz
[] = "dateTime.tz";
140 static const xmlChar DT_entity
[] = "entity";
141 static const xmlChar DT_entities
[] = "entities";
142 static const xmlChar DT_enumeration
[] = "enumeration";
143 static const xmlChar DT_fixed_14_4
[] = "fixed.14.4";
144 static const xmlChar DT_float
[] = "float";
145 static const xmlChar DT_i1
[] = "i1";
146 static const xmlChar DT_i2
[] = "i2";
147 static const xmlChar DT_i4
[] = "i4";
148 static const xmlChar DT_i8
[] = "i8";
149 static const xmlChar DT_id
[] = "id";
150 static const xmlChar DT_idref
[] = "idref";
151 static const xmlChar DT_idrefs
[] = "idrefs";
152 static const xmlChar DT_int
[] = "int";
153 static const xmlChar DT_nmtoken
[] = "nmtoken";
154 static const xmlChar DT_nmtokens
[] = "nmtokens";
155 static const xmlChar DT_notation
[] = "notation";
156 static const xmlChar DT_number
[] = "number";
157 static const xmlChar DT_r4
[] = "r4";
158 static const xmlChar DT_r8
[] = "r8";
159 static const xmlChar DT_string
[] = "string";
160 static const xmlChar DT_time
[] = "time";
161 static const xmlChar DT_time_tz
[] = "time.tz";
162 static const xmlChar DT_ui1
[] = "ui1";
163 static const xmlChar DT_ui2
[] = "ui2";
164 static const xmlChar DT_ui4
[] = "ui4";
165 static const xmlChar DT_ui8
[] = "ui8";
166 static const xmlChar DT_uri
[] = "uri";
167 static const xmlChar DT_uuid
[] = "uuid";
169 static const OLECHAR wDT_bin_base64
[] = {'b','i','n','.','b','a','s','e','6','4',0};
170 static const OLECHAR wDT_bin_hex
[] = {'b','i','n','.','h','e','x',0};
171 static const OLECHAR wDT_boolean
[] = {'b','o','o','l','e','a','n',0};
172 static const OLECHAR wDT_char
[] = {'c','h','a','r',0};
173 static const OLECHAR wDT_date
[] = {'d','a','t','e',0};
174 static const OLECHAR wDT_date_tz
[] = {'d','a','t','e','.','t','z',0};
175 static const OLECHAR wDT_dateTime
[] = {'d','a','t','e','T','i','m','e',0};
176 static const OLECHAR wDT_dateTime_tz
[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
177 static const OLECHAR wDT_entity
[] = {'e','n','t','i','t','y',0};
178 static const OLECHAR wDT_entities
[] = {'e','n','t','i','t','i','e','s',0};
179 static const OLECHAR wDT_enumeration
[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
180 static const OLECHAR wDT_fixed_14_4
[] = {'f','i','x','e','d','.','1','4','.','4',0};
181 static const OLECHAR wDT_float
[] = {'f','l','o','a','t',0};
182 static const OLECHAR wDT_i1
[] = {'i','1',0};
183 static const OLECHAR wDT_i2
[] = {'i','2',0};
184 static const OLECHAR wDT_i4
[] = {'i','4',0};
185 static const OLECHAR wDT_i8
[] = {'i','8',0};
186 static const OLECHAR wDT_id
[] = {'i','d',0};
187 static const OLECHAR wDT_idref
[] = {'i','d','r','e','f',0};
188 static const OLECHAR wDT_idrefs
[] = {'i','d','r','e','f','s',0};
189 static const OLECHAR wDT_int
[] = {'i','n','t',0};
190 static const OLECHAR wDT_nmtoken
[] = {'n','m','t','o','k','e','n',0};
191 static const OLECHAR wDT_nmtokens
[] = {'n','m','t','o','k','e','n','s',0};
192 static const OLECHAR wDT_notation
[] = {'n','o','t','a','t','i','o','n',0};
193 static const OLECHAR wDT_number
[] = {'n','u','m','b','e','r',0};
194 static const OLECHAR wDT_r4
[] = {'r','4',0};
195 static const OLECHAR wDT_r8
[] = {'r','8',0};
196 static const OLECHAR wDT_string
[] = {'s','t','r','i','n','g',0};
197 static const OLECHAR wDT_time
[] = {'t','i','m','e',0};
198 static const OLECHAR wDT_time_tz
[] = {'t','i','m','e','.','t','z',0};
199 static const OLECHAR wDT_ui1
[] = {'u','i','1',0};
200 static const OLECHAR wDT_ui2
[] = {'u','i','2',0};
201 static const OLECHAR wDT_ui4
[] = {'u','i','4',0};
202 static const OLECHAR wDT_ui8
[] = {'u','i','8',0};
203 static const OLECHAR wDT_uri
[] = {'u','r','i',0};
204 static const OLECHAR wDT_uuid
[] = {'u','u','i','d',0};
206 static const BYTE hash_assoc_values
[] =
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, 10, 116, 116, 55,
213 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
214 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
215 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
216 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
217 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
218 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
219 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
220 0, 0, 10, 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, 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
236 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
240 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
244 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
248 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
252 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
253 static void parser_serror(void* ctx
, xmlErrorPtr err
)
255 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
259 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
261 TRACE("(%p)\n", spctx
);
263 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
264 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
265 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
268 return xmlSchemaParse(spctx
);
271 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
275 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
279 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
283 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
287 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
288 static void validate_serror(void* ctx
, xmlErrorPtr err
)
290 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
294 static HRESULT
schema_cache_get_item(IUnknown
*iface
, LONG index
, VARIANT
*item
)
296 V_VT(item
) = VT_BSTR
;
297 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2
*)iface
, index
, &V_BSTR(item
));
300 static const struct enumvariant_funcs schemacache_enumvariant
= {
301 schema_cache_get_item
,
305 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
307 xmlSchemaValidCtxtPtr svctx
;
310 TRACE("(%p, %p)\n", schema
, tree
);
311 /* TODO: if validateOnLoad property is false,
312 * we probably need to validate the schema here. */
313 svctx
= xmlSchemaNewValidCtxt(schema
);
314 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
315 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
316 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
319 if (tree
->type
== XML_DOCUMENT_NODE
)
320 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
322 err
= xmlSchemaValidateOneElement(svctx
, tree
);
324 xmlSchemaFreeValidCtxt(svctx
);
325 return err
? S_FALSE
: S_OK
;
328 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
330 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
335 hval
+= hash_assoc_values
[str
[10]];
338 hval
+= hash_assoc_values
[str
[9]];
341 hval
+= hash_assoc_values
[str
[8]];
344 hval
+= hash_assoc_values
[str
[7]];
347 hval
+= hash_assoc_values
[str
[6]];
350 hval
+= hash_assoc_values
[str
[5]];
353 hval
+= hash_assoc_values
[str
[4]];
356 hval
+= hash_assoc_values
[str
[3]];
359 hval
+= hash_assoc_values
[str
[2]];
362 hval
+= hash_assoc_values
[str
[1]];
365 hval
+= hash_assoc_values
[str
[0]];
371 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
373 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
378 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
381 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
384 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
387 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
390 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
393 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
396 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
399 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
402 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
405 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
408 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
414 static const xmlChar
*const DT_string_table
[LAST_DT
] =
454 static const WCHAR
*const DT_wstring_table
[LAST_DT
] =
494 static const XDR_DT DT_lookup_table
[] =
547 -1, -1, -1, -1, -1, -1, -1, -1, -1,
548 -1, -1, -1, -1, -1, -1, -1, -1, -1,
549 -1, -1, -1, -1, -1, -1, -1, -1, -1,
550 -1, -1, -1, -1, -1, -1, -1, -1, -1,
551 -1, -1, -1, -1, -1, -1, -1, -1, -1,
552 -1, -1, -1, -1, -1, -1, -1, -1,
556 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
558 DWORD hash
= dt_hash(str
, len
);
559 XDR_DT dt
= DT_INVALID
;
561 if (hash
<= DT_MAX_HASH_VALUE
)
562 dt
= DT_lookup_table
[hash
];
564 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
570 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
572 DWORD hash
= dt_hash_bstr(bstr
, len
);
573 XDR_DT dt
= DT_INVALID
;
575 if (hash
<= DT_MAX_HASH_VALUE
)
576 dt
= DT_lookup_table
[hash
];
578 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
584 xmlChar
const* dt_to_str(XDR_DT dt
)
586 if (dt
== DT_INVALID
)
589 return DT_string_table
[dt
];
592 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
594 if (dt
== DT_INVALID
)
597 return DT_wstring_table
[dt
];
600 const char* debugstr_dt(XDR_DT dt
)
602 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
605 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
612 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), debugstr_a((char const*)content
));
614 if (!datatypes_schema
)
616 xmlSchemaParserCtxtPtr spctx
;
617 assert(datatypes_src
!= NULL
);
618 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
619 datatypes_schema
= Schema_parse(spctx
);
620 xmlSchemaFreeParserCtxt(spctx
);
656 if (!datatypes_schema
)
658 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
659 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
661 /* Hopefully they don't need much in the way of XDR datatypes support... */
665 if (content
&& xmlStrlen(content
))
667 tmp_doc
= xmlNewDoc(NULL
);
668 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
669 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
671 xmlDocSetRootElement(tmp_doc
, node
);
673 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
677 { /* probably the node is being created manually and has no content yet */
682 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
687 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
689 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
692 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
694 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
695 xmlHashLookup(This
->cache
, nsURI
);
698 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
700 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
701 return (!entry
)? NULL
: entry
->schema
;
704 static xmlExternalEntityLoader _external_entity_loader
;
706 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
707 xmlParserCtxtPtr ctxt
)
709 xmlParserInputPtr input
;
711 TRACE("(%s %s %p)\n", debugstr_a(URL
), debugstr_a(ID
), ctxt
);
713 assert(MSXML_hInstance
!= NULL
);
714 assert(datatypes_rsrc
!= NULL
);
715 assert(datatypes_handle
!= NULL
);
716 assert(datatypes_src
!= NULL
);
718 /* TODO: if the desired schema is in the cache, load it from there */
719 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
721 TRACE("loading built-in schema for %s\n", URL
);
722 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
726 input
= _external_entity_loader(URL
, ID
, ctxt
);
732 void schemasInit(void)
735 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
737 FIXME("failed to find resource for %s\n", DT_nsURI
);
741 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
743 FIXME("failed to load resource for %s\n", DT_nsURI
);
746 buf
= LockResource(datatypes_handle
);
747 datatypes_len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
);
749 /* Resource is loaded as raw data,
750 * need a null-terminated string */
751 while (buf
[datatypes_len
- 1] != '>') datatypes_len
--;
752 datatypes_src
= HeapAlloc(GetProcessHeap(), 0, datatypes_len
+ 1);
753 memcpy(datatypes_src
, buf
, datatypes_len
);
754 datatypes_src
[datatypes_len
] = 0;
756 if (xmlGetExternalEntityLoader() != external_entity_loader
)
758 _external_entity_loader
= xmlGetExternalEntityLoader();
759 xmlSetExternalEntityLoader(external_entity_loader
);
763 void schemasCleanup(void)
765 xmlSchemaFree(datatypes_schema
);
766 HeapFree(GetProcessHeap(), 0, datatypes_src
);
767 xmlSetExternalEntityLoader(_external_entity_loader
);
770 static LONG
cache_entry_add_ref(cache_entry
* entry
)
772 LONG ref
= InterlockedIncrement(&entry
->ref
);
773 TRACE("(%p)->(%d)\n", entry
, ref
);
777 static LONG
cache_entry_release(cache_entry
* entry
)
779 LONG ref
= InterlockedDecrement(&entry
->ref
);
780 TRACE("(%p)->(%d)\n", entry
, ref
);
784 if (entry
->type
== CacheEntryType_XSD
)
786 xmldoc_release(entry
->doc
);
787 entry
->schema
->doc
= NULL
;
788 xmlSchemaFree(entry
->schema
);
790 else if (entry
->type
== CacheEntryType_XDR
)
792 xmldoc_release(entry
->doc
);
793 xmldoc_release(entry
->schema
->doc
);
794 entry
->schema
->doc
= NULL
;
795 xmlSchemaFree(entry
->schema
);
803 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
;
805 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
807 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
810 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
* iface
)
812 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
815 static inline schema_cache
* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
*iface
)
817 return iface
->lpVtbl
== (void*)&XMLDOMSchemaCollection2Vtbl
? impl_from_IXMLDOMSchemaCollection(iface
) : NULL
;
820 static inline CacheEntryType
cache_type_from_xmlDocPtr(xmlDocPtr schema
)
822 xmlNodePtr root
= NULL
;
824 root
= xmlDocGetRootElement(schema
);
825 if (root
&& root
->ns
)
828 if (xmlStrEqual(root
->name
, XDR_schema
) &&
829 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
831 return CacheEntryType_XDR
;
833 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
834 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
836 return CacheEntryType_XSD
;
839 return CacheEntryType_Invalid
;
842 static BOOL
link_datatypes(xmlDocPtr schema
)
844 xmlNodePtr root
, next
, child
;
847 assert(xmlGetExternalEntityLoader() == external_entity_loader
);
848 root
= xmlDocGetRootElement(schema
);
852 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
854 if (xmlStrEqual(ns
->href
, DT_nsURI
))
861 next
= xmlFirstElementChild(root
);
862 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
863 if (next
) child
= xmlAddPrevSibling(next
, child
);
864 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
865 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
870 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
872 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
873 xmlSchemaParserCtxtPtr spctx
;
874 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
876 link_datatypes(new_doc
);
878 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
879 * do we need to do something special here? */
880 entry
->type
= CacheEntryType_XSD
;
882 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
884 if ((entry
->schema
= Schema_parse(spctx
)))
886 xmldoc_init(entry
->schema
->doc
, v
);
887 entry
->doc
= entry
->schema
->doc
;
888 xmldoc_add_ref(entry
->doc
);
892 FIXME("failed to parse doc\n");
897 xmlSchemaFreeParserCtxt(spctx
);
901 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
903 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
904 xmlSchemaParserCtxtPtr spctx
;
905 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
907 link_datatypes(xsd_doc
);
909 entry
->type
= CacheEntryType_XDR
;
911 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
913 if ((entry
->schema
= Schema_parse(spctx
)))
915 entry
->doc
= new_doc
;
916 xmldoc_init(entry
->schema
->doc
, version
);
917 xmldoc_init(entry
->doc
, version
);
918 xmldoc_add_ref(entry
->doc
);
919 xmldoc_add_ref(entry
->schema
->doc
);
923 FIXME("failed to parse doc\n");
929 xmlSchemaFreeParserCtxt(spctx
);
934 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
937 IXMLDOMDocument3
* domdoc
= NULL
;
938 xmlDocPtr doc
= NULL
;
939 HRESULT hr
= DOMDocument_create(version
, (void**)&domdoc
);
940 VARIANT_BOOL b
= VARIANT_FALSE
;
941 CacheEntryType type
= CacheEntryType_Invalid
;
945 FIXME("failed to create domdoc\n");
948 assert(domdoc
!= NULL
);
949 assert(V_VT(&url
) == VT_BSTR
);
951 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
954 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
955 if (b
!= VARIANT_TRUE
)
957 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url
)));
958 IXMLDOMDocument3_Release(domdoc
);
962 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
963 type
= cache_type_from_xmlDocPtr(doc
);
967 case CacheEntryType_XSD
:
968 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
970 case CacheEntryType_XDR
:
971 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
975 FIXME("invalid schema\n");
978 IXMLDOMDocument3_Release(domdoc
);
983 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
985 cache_entry_release((cache_entry
*)data
);
988 /* returns index or -1 if not found */
989 static int cache_free_uri(schema_cache
*cache
, const xmlChar
*uri
)
993 for (i
= 0; i
< cache
->count
; i
++)
994 if (xmlStrEqual(cache
->uris
[i
], uri
))
996 heap_free(cache
->uris
[i
]);
1003 static void cache_add_entry(schema_cache
*cache
, const xmlChar
*uri
, cache_entry
*entry
)
1007 /* meaning no entry found with this name */
1008 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
))
1010 if (cache
->count
== cache
->allocated
)
1012 cache
->allocated
*= 2;
1013 cache
->uris
= heap_realloc(cache
->uris
, cache
->allocated
*sizeof(xmlChar
*));
1018 i
= cache_free_uri(cache
, uri
);
1020 cache
->uris
[i
] = heap_strdupxmlChar(uri
);
1021 xmlHashAddEntry(cache
->cache
, uri
, entry
);
1024 static void cache_remove_entry(schema_cache
*cache
, const xmlChar
*uri
)
1026 /* adjust index if entry was really removed */
1027 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
) == 0)
1029 int i
= cache_free_uri(cache
, uri
);
1030 if (i
== -1) return;
1032 if (i
!= --cache
->count
)
1033 memmove(&cache
->uris
[i
], &cache
->uris
[i
+1], (cache
->count
-i
)*sizeof(xmlChar
*));
1037 /* This one adds all namespaces defined in document to a cache, without anything
1038 associated with uri obviously.
1039 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
1040 it uses additional node type to describe namespace definition attribute while
1041 in msxml it's expected to be a normal attribute - as a workaround document is
1042 queried at libxml2 level here. */
1043 HRESULT
cache_from_doc_ns(IXMLDOMSchemaCollection2
*iface
, xmlnode
*node
)
1045 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1046 static const xmlChar query
[] = "//*/namespace::*";
1047 xmlXPathObjectPtr nodeset
;
1048 xmlXPathContextPtr ctxt
;
1050 This
->read_only
= 1;
1052 ctxt
= xmlXPathNewContext(node
->node
->doc
);
1054 nodeset
= xmlXPathEvalExpression(query
, ctxt
);
1055 xmlXPathFreeContext(ctxt
);
1059 int pos
= 0, len
= xmlXPathNodeSetGetLength(nodeset
->nodesetval
);
1061 if (len
== 0) return S_OK
;
1065 xmlNodePtr node
= xmlXPathNodeSetItem(nodeset
->nodesetval
, pos
);
1066 if (node
->type
== XML_NAMESPACE_DECL
)
1068 static const xmlChar defns
[] = "http://www.w3.org/XML/1998/namespace";
1069 xmlNsPtr ns
= (xmlNsPtr
)node
;
1072 /* filter out default uri */
1073 if (xmlStrEqual(ns
->href
, defns
))
1079 entry
= heap_alloc(sizeof(cache_entry
));
1080 entry
->type
= CacheEntryType_NS
;
1082 entry
->schema
= NULL
;
1085 cache_add_entry(This
, ns
->href
, entry
);
1090 xmlXPathFreeObject(nodeset
);
1096 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
1097 REFIID riid
, void** ppvObject
)
1099 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1101 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1103 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
1104 IsEqualIID(riid
, &IID_IDispatch
) ||
1105 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
1106 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
1110 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
1112 return *ppvObject
? S_OK
: E_NOINTERFACE
;
1114 else if(IsEqualGUID( riid
, &IID_ISupportErrorInfo
))
1116 return node_create_supporterrorinfo(schema_cache_se_tids
, ppvObject
);
1120 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1122 return E_NOINTERFACE
;
1125 IXMLDOMSchemaCollection2_AddRef(iface
);
1130 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
1132 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1133 LONG ref
= InterlockedIncrement(&This
->ref
);
1134 TRACE("(%p)->(%d)\n", This
, ref
);
1138 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
1140 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1141 LONG ref
= InterlockedDecrement(&This
->ref
);
1142 TRACE("(%p)->(%d)\n", This
, ref
);
1148 for (i
= 0; i
< This
->count
; i
++)
1149 heap_free(This
->uris
[i
]);
1150 heap_free(This
->uris
);
1151 xmlHashFree(This
->cache
, cache_free
);
1152 release_dispex(&This
->dispex
);
1159 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1162 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1163 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1166 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1167 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1169 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1170 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1171 iTInfo
, lcid
, ppTInfo
);
1174 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1175 REFIID riid
, LPOLESTR
* rgszNames
,
1176 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1178 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1179 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1180 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1183 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1184 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1185 WORD wFlags
, DISPPARAMS
* pDispParams
,
1186 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1189 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1190 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1191 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1194 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1196 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1197 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1198 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1200 if (This
->read_only
) return E_FAIL
;
1206 cache_remove_entry(This
, name
);
1212 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1216 cache_entry_add_ref(entry
);
1224 cache_add_entry(This
, name
, entry
);
1230 xmlDocPtr doc
= NULL
;
1232 CacheEntryType type
;
1233 IXMLDOMNode
* domnode
= NULL
;
1234 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1237 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1241 IXMLDOMNode_Release(domnode
);
1243 return E_INVALIDARG
;
1245 type
= cache_type_from_xmlDocPtr(doc
);
1247 if (type
== CacheEntryType_XSD
)
1249 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1251 else if (type
== CacheEntryType_XDR
)
1253 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1257 WARN("invalid schema!\n");
1261 IXMLDOMNode_Release(domnode
);
1265 cache_entry_add_ref(entry
);
1273 cache_add_entry(This
, name
, entry
);
1280 return E_INVALIDARG
;
1287 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1290 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1294 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1296 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1301 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1302 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1305 /* TODO: this should be read-only */
1306 if (entry
&& entry
->doc
)
1307 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1313 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1315 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1316 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1317 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1319 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1321 cache_remove_entry(This
, name
);
1326 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1328 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1329 TRACE("(%p)->(%p)\n", This
, length
);
1334 *length
= This
->count
;
1338 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1339 LONG index
, BSTR
* uri
)
1341 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1343 TRACE("(%p)->(%i %p)\n", This
, index
, uri
);
1348 if (index
>= This
->count
)
1351 *uri
= bstr_from_xmlChar(This
->uris
[index
]);
1355 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1357 schema_cache
* This
= (schema_cache
*) dest
;
1358 cache_entry
* entry
= (cache_entry
*) data
;
1360 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1362 cache_entry_add_ref(entry
);
1363 cache_add_entry(This
, name
, entry
);
1367 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1368 IXMLDOMSchemaCollection
* collection
)
1370 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1373 TRACE("(%p)->(%p)\n", This
, collection
);
1378 That
= unsafe_impl_from_IXMLDOMSchemaCollection(collection
);
1381 ERR("external collection implementation\n");
1385 /* TODO: detect errors while copying & return E_FAIL */
1386 xmlHashScan(That
->cache
, cache_copy
, This
);
1391 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
, IUnknown
** enumv
)
1393 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1394 TRACE("(%p)->(%p)\n", This
, enumv
);
1395 return create_enumvariant((IUnknown
*)iface
, TRUE
, &schemacache_enumvariant
, (IEnumVARIANT
**)enumv
);
1398 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1400 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1401 FIXME("(%p): stub\n", This
);
1405 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1408 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1409 FIXME("(%p)->(%d): stub\n", This
, value
);
1411 This
->validateOnLoad
= value
;
1412 /* it's ok to disable it, cause we don't validate on load anyway */
1413 if (value
== VARIANT_FALSE
) return S_OK
;
1418 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1419 VARIANT_BOOL
* value
)
1421 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1422 TRACE("(%p)->(%p)\n", This
, value
);
1424 if (!value
) return E_POINTER
;
1425 *value
= This
->validateOnLoad
;
1430 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1431 BSTR namespaceURI
, ISchema
** schema
)
1433 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1434 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1440 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1441 IXMLDOMNode
* node
, ISchemaItem
** item
)
1443 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1444 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1450 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1452 schema_cache_QueryInterface
,
1453 schema_cache_AddRef
,
1454 schema_cache_Release
,
1455 schema_cache_GetTypeInfoCount
,
1456 schema_cache_GetTypeInfo
,
1457 schema_cache_GetIDsOfNames
,
1458 schema_cache_Invoke
,
1461 schema_cache_remove
,
1462 schema_cache_get_length
,
1463 schema_cache_get_namespaceURI
,
1464 schema_cache_addCollection
,
1465 schema_cache_get__newEnum
,
1466 schema_cache_validate
,
1467 schema_cache_put_validateOnLoad
,
1468 schema_cache_get_validateOnLoad
,
1469 schema_cache_getSchema
,
1470 schema_cache_getDeclaration
1473 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1475 xmlSchemaElementPtr decl
= NULL
;
1476 xmlChar
const* nsURI
= get_node_nsURI(node
);
1478 TRACE("(%p, %p)\n", schema
, node
);
1480 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1481 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1483 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1485 FIXME("declaration not found in main schema - need to check schema imports!\n");
1486 /*xmlSchemaImportPtr import;
1488 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1490 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1493 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1499 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1501 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1502 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1503 decl
= decl
->refDecl
;
1504 return (decl
!= NULL
)? decl
->node
: NULL
;
1507 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1509 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1510 xmlSchemaPtr schema
;
1512 TRACE("(%p, %p)\n", This
, tree
);
1517 if (tree
->type
== XML_DOCUMENT_NODE
)
1518 tree
= xmlDocGetRootElement(tree
->doc
);
1520 schema
= get_node_schema(This
, tree
);
1521 /* TODO: if the ns is not in the cache, and it's a URL,
1522 * do we try to load from that? */
1524 return Schema_validate_tree(schema
, tree
);
1526 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1531 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1533 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1534 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1535 XDR_DT dt
= DT_INVALID
;
1537 TRACE("(%p, %p)\n", This
, node
);
1539 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1541 dt
= str_to_dt(node
->name
, -1);
1546 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1548 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1551 dt
= str_to_dt(str
, -1);
1559 static const tid_t schemacache_iface_tids
[] = {
1560 IXMLDOMSchemaCollection2_tid
,
1564 static dispex_static_data_t schemacache_dispex
= {
1566 IXMLDOMSchemaCollection2_tid
,
1568 schemacache_iface_tids
1571 HRESULT
SchemaCache_create(MSXML_VERSION version
, void** obj
)
1573 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1575 return E_OUTOFMEMORY
;
1577 TRACE("(%d %p)\n", version
, obj
);
1579 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1580 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1581 This
->allocated
= 10;
1583 This
->uris
= heap_alloc(This
->allocated
*sizeof(xmlChar
*));
1585 This
->version
= version
;
1586 This
->validateOnLoad
= VARIANT_TRUE
;
1587 This
->read_only
= 0;
1588 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1590 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1596 HRESULT
SchemaCache_create(MSXML_VERSION version
, void** obj
)
1598 MESSAGE("This program tried to use a SchemaCache object, but\n"
1599 "libxml2 support was not present at compile time.\n");