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 WIN32_NO_STATUS
32 //# include <libxml/xmlerror.h>
33 //# include <libxml/tree.h>
34 # include <libxml/xmlschemas.h>
35 # include <libxml/schemasInternals.h>
36 //# include <libxml/hash.h>
37 //# include <libxml/parser.h>
38 # include <libxml/parserInternals.h>
39 //# include <libxml/xmlIO.h>
40 //# include <libxml/xmlversion.h>
41 # include <libxml/xpath.h>
46 //#include "winuser.h"
50 #include <wine/debug.h>
52 #include "msxml_private.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(msxml
);
56 /* We use a chained hashtable, which can hold any number of schemas
57 * TODO: grow/shrink hashtable depending on load factor
58 * TODO: implement read-only where appropriate
61 /* This is just the number of buckets, should be prime */
62 #define DEFAULT_HASHTABLE_SIZE 17
66 xmlDocPtr
XDR_to_XSD_doc(xmlDocPtr xdr_doc
, xmlChar
const* nsURI
);
68 static const xmlChar XSD_schema
[] = "schema";
69 static const xmlChar XSD_nsURI
[] = "http://www.w3.org/2001/XMLSchema";
70 static const xmlChar XDR_schema
[] = "Schema";
71 static const xmlChar XDR_nsURI
[] = "urn:schemas-microsoft-com:xml-data";
72 static const xmlChar DT_nsURI
[] = "urn:schemas-microsoft-com:datatypes";
74 static xmlChar
* datatypes_src
;
75 static int datatypes_len
;
76 static HGLOBAL datatypes_handle
;
77 static HRSRC datatypes_rsrc
;
78 static xmlSchemaPtr datatypes_schema
;
80 static const WCHAR emptyW
[] = {0};
88 * CacheType_NS is a special type used for read-only collection build with
89 * IXMLDOMDocument2::namespaces()
92 CacheEntryType_Invalid
,
101 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface
;
104 MSXML_VERSION version
;
105 xmlHashTablePtr cache
;
110 VARIANT_BOOL validateOnLoad
;
122 /* datatypes lookup stuff
123 * generated with help from gperf */
124 #define DT_MIN_STR_LEN 2
125 #define DT_MAX_STR_LEN 11
126 #define DT_MIN_HASH_VALUE 2
127 #define DT_MAX_HASH_VALUE 115
129 static const xmlChar DT_bin_base64
[] = "bin.base64";
130 static const xmlChar DT_bin_hex
[] = "bin.hex";
131 static const xmlChar DT_boolean
[] = "boolean";
132 static const xmlChar DT_char
[] = "char";
133 static const xmlChar DT_date
[] = "date";
134 static const xmlChar DT_date_tz
[] = "date.tz";
135 static const xmlChar DT_dateTime
[] = "dateTime";
136 static const xmlChar DT_dateTime_tz
[] = "dateTime.tz";
137 static const xmlChar DT_entity
[] = "entity";
138 static const xmlChar DT_entities
[] = "entities";
139 static const xmlChar DT_enumeration
[] = "enumeration";
140 static const xmlChar DT_fixed_14_4
[] = "fixed.14.4";
141 static const xmlChar DT_float
[] = "float";
142 static const xmlChar DT_i1
[] = "i1";
143 static const xmlChar DT_i2
[] = "i2";
144 static const xmlChar DT_i4
[] = "i4";
145 static const xmlChar DT_i8
[] = "i8";
146 static const xmlChar DT_id
[] = "id";
147 static const xmlChar DT_idref
[] = "idref";
148 static const xmlChar DT_idrefs
[] = "idrefs";
149 static const xmlChar DT_int
[] = "int";
150 static const xmlChar DT_nmtoken
[] = "nmtoken";
151 static const xmlChar DT_nmtokens
[] = "nmtokens";
152 static const xmlChar DT_notation
[] = "notation";
153 static const xmlChar DT_number
[] = "number";
154 static const xmlChar DT_r4
[] = "r4";
155 static const xmlChar DT_r8
[] = "r8";
156 static const xmlChar DT_string
[] = "string";
157 static const xmlChar DT_time
[] = "time";
158 static const xmlChar DT_time_tz
[] = "time.tz";
159 static const xmlChar DT_ui1
[] = "ui1";
160 static const xmlChar DT_ui2
[] = "ui2";
161 static const xmlChar DT_ui4
[] = "ui4";
162 static const xmlChar DT_ui8
[] = "ui8";
163 static const xmlChar DT_uri
[] = "uri";
164 static const xmlChar DT_uuid
[] = "uuid";
166 static const OLECHAR wDT_bin_base64
[] = {'b','i','n','.','b','a','s','e','6','4',0};
167 static const OLECHAR wDT_bin_hex
[] = {'b','i','n','.','h','e','x',0};
168 static const OLECHAR wDT_boolean
[] = {'b','o','o','l','e','a','n',0};
169 static const OLECHAR wDT_char
[] = {'c','h','a','r',0};
170 static const OLECHAR wDT_date
[] = {'d','a','t','e',0};
171 static const OLECHAR wDT_date_tz
[] = {'d','a','t','e','.','t','z',0};
172 static const OLECHAR wDT_dateTime
[] = {'d','a','t','e','T','i','m','e',0};
173 static const OLECHAR wDT_dateTime_tz
[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
174 static const OLECHAR wDT_entity
[] = {'e','n','t','i','t','y',0};
175 static const OLECHAR wDT_entities
[] = {'e','n','t','i','t','i','e','s',0};
176 static const OLECHAR wDT_enumeration
[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
177 static const OLECHAR wDT_fixed_14_4
[] = {'f','i','x','e','d','.','1','4','.','4',0};
178 static const OLECHAR wDT_float
[] = {'f','l','o','a','t',0};
179 static const OLECHAR wDT_i1
[] = {'i','1',0};
180 static const OLECHAR wDT_i2
[] = {'i','2',0};
181 static const OLECHAR wDT_i4
[] = {'i','4',0};
182 static const OLECHAR wDT_i8
[] = {'i','8',0};
183 static const OLECHAR wDT_id
[] = {'i','d',0};
184 static const OLECHAR wDT_idref
[] = {'i','d','r','e','f',0};
185 static const OLECHAR wDT_idrefs
[] = {'i','d','r','e','f','s',0};
186 static const OLECHAR wDT_int
[] = {'i','n','t',0};
187 static const OLECHAR wDT_nmtoken
[] = {'n','m','t','o','k','e','n',0};
188 static const OLECHAR wDT_nmtokens
[] = {'n','m','t','o','k','e','n','s',0};
189 static const OLECHAR wDT_notation
[] = {'n','o','t','a','t','i','o','n',0};
190 static const OLECHAR wDT_number
[] = {'n','u','m','b','e','r',0};
191 static const OLECHAR wDT_r4
[] = {'r','4',0};
192 static const OLECHAR wDT_r8
[] = {'r','8',0};
193 static const OLECHAR wDT_string
[] = {'s','t','r','i','n','g',0};
194 static const OLECHAR wDT_time
[] = {'t','i','m','e',0};
195 static const OLECHAR wDT_time_tz
[] = {'t','i','m','e','.','t','z',0};
196 static const OLECHAR wDT_ui1
[] = {'u','i','1',0};
197 static const OLECHAR wDT_ui2
[] = {'u','i','2',0};
198 static const OLECHAR wDT_ui4
[] = {'u','i','4',0};
199 static const OLECHAR wDT_ui8
[] = {'u','i','8',0};
200 static const OLECHAR wDT_uri
[] = {'u','r','i',0};
201 static const OLECHAR wDT_uuid
[] = {'u','u','i','d',0};
203 static const BYTE hash_assoc_values
[] =
205 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
206 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
207 116, 116, 116, 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, 10, 116, 116, 55,
210 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
211 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
212 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
213 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
214 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
215 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
216 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
217 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
218 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
219 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
220 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
221 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
222 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
223 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
224 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
225 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
226 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
227 116, 116, 116, 116, 116, 116, 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
233 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
237 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
241 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
245 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
249 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
250 static void parser_serror(void* ctx
, xmlErrorPtr err
)
252 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
256 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
258 TRACE("(%p)\n", spctx
);
260 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
261 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
262 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
265 return xmlSchemaParse(spctx
);
268 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
272 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
276 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
280 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
284 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
285 static void validate_serror(void* ctx
, xmlErrorPtr err
)
287 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
291 static HRESULT
schema_cache_get_item(IUnknown
*iface
, LONG index
, VARIANT
*item
)
293 V_VT(item
) = VT_BSTR
;
294 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2
*)iface
, index
, &V_BSTR(item
));
297 static const struct enumvariant_funcs schemacache_enumvariant
= {
298 schema_cache_get_item
,
302 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
304 xmlSchemaValidCtxtPtr svctx
;
307 TRACE("(%p, %p)\n", schema
, tree
);
308 /* TODO: if validateOnLoad property is false,
309 * we probably need to validate the schema here. */
310 svctx
= xmlSchemaNewValidCtxt(schema
);
311 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
312 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
313 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
316 if (tree
->type
== XML_DOCUMENT_NODE
)
317 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
319 err
= xmlSchemaValidateOneElement(svctx
, tree
);
321 xmlSchemaFreeValidCtxt(svctx
);
322 return err
? S_FALSE
: S_OK
;
325 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
327 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
332 hval
+= hash_assoc_values
[str
[10]];
335 hval
+= hash_assoc_values
[str
[9]];
338 hval
+= hash_assoc_values
[str
[8]];
341 hval
+= hash_assoc_values
[str
[7]];
344 hval
+= hash_assoc_values
[str
[6]];
347 hval
+= hash_assoc_values
[str
[5]];
350 hval
+= hash_assoc_values
[str
[4]];
353 hval
+= hash_assoc_values
[str
[3]];
356 hval
+= hash_assoc_values
[str
[2]];
359 hval
+= hash_assoc_values
[str
[1]];
362 hval
+= hash_assoc_values
[str
[0]];
368 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
370 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
375 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
378 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
381 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
384 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
387 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
390 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
393 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
396 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
399 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
402 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
405 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
411 static const xmlChar
*const DT_string_table
[LAST_DT
] =
451 static const WCHAR
*const DT_wstring_table
[LAST_DT
] =
491 static const XDR_DT DT_lookup_table
[] =
544 -1, -1, -1, -1, -1, -1, -1, -1, -1,
545 -1, -1, -1, -1, -1, -1, -1, -1, -1,
546 -1, -1, -1, -1, -1, -1, -1, -1, -1,
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,
553 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
555 DWORD hash
= dt_hash(str
, len
);
556 XDR_DT dt
= DT_INVALID
;
558 if (hash
<= DT_MAX_HASH_VALUE
)
559 dt
= DT_lookup_table
[hash
];
561 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
567 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
569 DWORD hash
= dt_hash_bstr(bstr
, len
);
570 XDR_DT dt
= DT_INVALID
;
572 if (hash
<= DT_MAX_HASH_VALUE
)
573 dt
= DT_lookup_table
[hash
];
575 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
581 xmlChar
const* dt_to_str(XDR_DT dt
)
583 if (dt
== DT_INVALID
)
586 return DT_string_table
[dt
];
589 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
591 if (dt
== DT_INVALID
)
594 return DT_wstring_table
[dt
];
597 const char* debugstr_dt(XDR_DT dt
)
599 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
602 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
609 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), debugstr_a((char const*)content
));
611 if (!datatypes_schema
)
613 xmlSchemaParserCtxtPtr spctx
;
614 assert(datatypes_src
!= NULL
);
615 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
616 datatypes_schema
= Schema_parse(spctx
);
617 xmlSchemaFreeParserCtxt(spctx
);
653 if (!datatypes_schema
)
655 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
656 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
658 /* Hopefully they don't need much in the way of XDR datatypes support... */
662 if (content
&& xmlStrlen(content
))
664 tmp_doc
= xmlNewDoc(NULL
);
665 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
666 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
668 xmlDocSetRootElement(tmp_doc
, node
);
670 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
674 { /* probably the node is being created manually and has no content yet */
679 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
684 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
686 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
689 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
691 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
692 xmlHashLookup(This
->cache
, nsURI
);
695 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
697 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
698 return (!entry
)? NULL
: entry
->schema
;
701 static xmlExternalEntityLoader _external_entity_loader
;
703 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
704 xmlParserCtxtPtr ctxt
)
706 xmlParserInputPtr input
;
708 TRACE("(%s %s %p)\n", debugstr_a(URL
), debugstr_a(ID
), ctxt
);
710 assert(MSXML_hInstance
!= NULL
);
711 assert(datatypes_rsrc
!= NULL
);
712 assert(datatypes_handle
!= NULL
);
713 assert(datatypes_src
!= NULL
);
715 /* TODO: if the desired schema is in the cache, load it from there */
716 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
718 TRACE("loading built-in schema for %s\n", URL
);
719 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
723 input
= _external_entity_loader(URL
, ID
, ctxt
);
729 void schemasInit(void)
732 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
734 FIXME("failed to find resource for %s\n", DT_nsURI
);
738 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
740 FIXME("failed to load resource for %s\n", DT_nsURI
);
743 buf
= LockResource(datatypes_handle
);
744 datatypes_len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
);
746 /* Resource is loaded as raw data,
747 * need a null-terminated string */
748 while (buf
[datatypes_len
- 1] != '>') datatypes_len
--;
749 datatypes_src
= HeapAlloc(GetProcessHeap(), 0, datatypes_len
+ 1);
750 memcpy(datatypes_src
, buf
, datatypes_len
);
751 datatypes_src
[datatypes_len
] = 0;
753 if (xmlGetExternalEntityLoader() != external_entity_loader
)
755 _external_entity_loader
= xmlGetExternalEntityLoader();
756 xmlSetExternalEntityLoader(external_entity_loader
);
760 void schemasCleanup(void)
762 xmlSchemaFree(datatypes_schema
);
763 HeapFree(GetProcessHeap(), 0, datatypes_src
);
764 xmlSetExternalEntityLoader(_external_entity_loader
);
767 static LONG
cache_entry_add_ref(cache_entry
* entry
)
769 LONG ref
= InterlockedIncrement(&entry
->ref
);
770 TRACE("(%p)->(%d)\n", entry
, ref
);
774 static LONG
cache_entry_release(cache_entry
* entry
)
776 LONG ref
= InterlockedDecrement(&entry
->ref
);
777 TRACE("(%p)->(%d)\n", entry
, ref
);
781 if (entry
->type
== CacheEntryType_XSD
)
783 xmldoc_release(entry
->doc
);
784 entry
->schema
->doc
= NULL
;
785 xmlSchemaFree(entry
->schema
);
787 else if (entry
->type
== CacheEntryType_XDR
)
789 xmldoc_release(entry
->doc
);
790 xmldoc_release(entry
->schema
->doc
);
791 entry
->schema
->doc
= NULL
;
792 xmlSchemaFree(entry
->schema
);
800 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
;
802 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
804 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
807 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
* iface
)
809 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
812 static inline schema_cache
* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
*iface
)
814 return iface
->lpVtbl
== (void*)&XMLDOMSchemaCollection2Vtbl
? impl_from_IXMLDOMSchemaCollection(iface
) : NULL
;
817 static inline CacheEntryType
cache_type_from_xmlDocPtr(xmlDocPtr schema
)
819 xmlNodePtr root
= NULL
;
821 root
= xmlDocGetRootElement(schema
);
822 if (root
&& root
->ns
)
825 if (xmlStrEqual(root
->name
, XDR_schema
) &&
826 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
828 return CacheEntryType_XDR
;
830 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
831 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
833 return CacheEntryType_XSD
;
836 return CacheEntryType_Invalid
;
839 static BOOL
link_datatypes(xmlDocPtr schema
)
841 xmlNodePtr root
, next
, child
;
844 assert(xmlGetExternalEntityLoader() == external_entity_loader
);
845 root
= xmlDocGetRootElement(schema
);
849 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
851 if (xmlStrEqual(ns
->href
, DT_nsURI
))
858 next
= xmlFirstElementChild(root
);
859 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
860 if (next
) child
= xmlAddPrevSibling(next
, child
);
861 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
862 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
867 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
869 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
870 xmlSchemaParserCtxtPtr spctx
;
871 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
873 link_datatypes(new_doc
);
875 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
876 * do we need to do something special here? */
877 entry
->type
= CacheEntryType_XSD
;
879 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
881 if ((entry
->schema
= Schema_parse(spctx
)))
883 xmldoc_init(entry
->schema
->doc
, v
);
884 entry
->doc
= entry
->schema
->doc
;
885 xmldoc_add_ref(entry
->doc
);
889 FIXME("failed to parse doc\n");
894 xmlSchemaFreeParserCtxt(spctx
);
898 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
900 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
901 xmlSchemaParserCtxtPtr spctx
;
902 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
904 link_datatypes(xsd_doc
);
906 entry
->type
= CacheEntryType_XDR
;
908 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
910 if ((entry
->schema
= Schema_parse(spctx
)))
912 entry
->doc
= new_doc
;
913 xmldoc_init(entry
->schema
->doc
, version
);
914 xmldoc_init(entry
->doc
, version
);
915 xmldoc_add_ref(entry
->doc
);
916 xmldoc_add_ref(entry
->schema
->doc
);
920 FIXME("failed to parse doc\n");
926 xmlSchemaFreeParserCtxt(spctx
);
931 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
934 IXMLDOMDocument3
* domdoc
= NULL
;
935 xmlDocPtr doc
= NULL
;
936 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
937 VARIANT_BOOL b
= VARIANT_FALSE
;
938 CacheEntryType type
= CacheEntryType_Invalid
;
942 FIXME("failed to create domdoc\n");
945 assert(domdoc
!= NULL
);
946 assert(V_VT(&url
) == VT_BSTR
);
948 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
951 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
952 if (b
!= VARIANT_TRUE
)
954 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url
)));
955 IXMLDOMDocument3_Release(domdoc
);
959 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
960 type
= cache_type_from_xmlDocPtr(doc
);
964 case CacheEntryType_XSD
:
965 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
967 case CacheEntryType_XDR
:
968 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
972 FIXME("invalid schema\n");
975 IXMLDOMDocument3_Release(domdoc
);
980 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
982 cache_entry_release((cache_entry
*)data
);
985 /* returns index or -1 if not found */
986 static int cache_free_uri(schema_cache
*cache
, const xmlChar
*uri
)
990 for (i
= 0; i
< cache
->count
; i
++)
991 if (xmlStrEqual(cache
->uris
[i
], uri
))
993 heap_free(cache
->uris
[i
]);
1000 static void cache_add_entry(schema_cache
*cache
, const xmlChar
*uri
, cache_entry
*entry
)
1004 /* meaning no entry found with this name */
1005 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
))
1007 if (cache
->count
== cache
->allocated
)
1009 cache
->allocated
*= 2;
1010 cache
->uris
= heap_realloc(cache
->uris
, cache
->allocated
*sizeof(xmlChar
*));
1015 i
= cache_free_uri(cache
, uri
);
1017 cache
->uris
[i
] = heap_strdupxmlChar(uri
);
1018 xmlHashAddEntry(cache
->cache
, uri
, entry
);
1021 static void cache_remove_entry(schema_cache
*cache
, const xmlChar
*uri
)
1023 /* adjust index if entry was really removed */
1024 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
) == 0)
1026 int i
= cache_free_uri(cache
, uri
);
1027 if (i
== -1) return;
1029 if (i
!= --cache
->count
)
1030 memmove(&cache
->uris
[i
], &cache
->uris
[i
+1], (cache
->count
-i
)*sizeof(xmlChar
*));
1034 /* This one adds all namespaces defined in document to a cache, without anything
1035 associated with uri obviously.
1036 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
1037 it uses additional node type to describe namespace definition attribute while
1038 in msxml it's expected to be a normal attribute - as a workaround document is
1039 queried at libxml2 level here. */
1040 HRESULT
cache_from_doc_ns(IXMLDOMSchemaCollection2
*iface
, xmlnode
*node
)
1042 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1043 static const xmlChar query
[] = "//*/namespace::*";
1044 xmlXPathObjectPtr nodeset
;
1045 xmlXPathContextPtr ctxt
;
1047 This
->read_only
= 1;
1049 ctxt
= xmlXPathNewContext(node
->node
->doc
);
1051 nodeset
= xmlXPathEvalExpression(query
, ctxt
);
1052 xmlXPathFreeContext(ctxt
);
1056 int pos
= 0, len
= xmlXPathNodeSetGetLength(nodeset
->nodesetval
);
1058 if (len
== 0) return S_OK
;
1062 xmlNodePtr node
= xmlXPathNodeSetItem(nodeset
->nodesetval
, pos
);
1063 if (node
->type
== XML_NAMESPACE_DECL
)
1065 static const xmlChar defns
[] = "http://www.w3.org/XML/1998/namespace";
1066 xmlNsPtr ns
= (xmlNsPtr
)node
;
1069 /* filter out default uri */
1070 if (xmlStrEqual(ns
->href
, defns
))
1076 entry
= heap_alloc(sizeof(cache_entry
));
1077 entry
->type
= CacheEntryType_NS
;
1079 entry
->schema
= NULL
;
1082 cache_add_entry(This
, ns
->href
, entry
);
1087 xmlXPathFreeObject(nodeset
);
1093 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
1094 REFIID riid
, void** ppvObject
)
1096 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1098 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1100 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
1101 IsEqualIID(riid
, &IID_IDispatch
) ||
1102 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
1103 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
1107 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
1109 return *ppvObject
? S_OK
: E_NOINTERFACE
;
1113 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1115 return E_NOINTERFACE
;
1118 IXMLDOMSchemaCollection2_AddRef(iface
);
1123 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
1125 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1126 LONG ref
= InterlockedIncrement(&This
->ref
);
1127 TRACE("(%p)->(%d)\n", This
, ref
);
1131 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
1133 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1134 LONG ref
= InterlockedDecrement(&This
->ref
);
1135 TRACE("(%p)->(%d)\n", This
, ref
);
1141 for (i
= 0; i
< This
->count
; i
++)
1142 heap_free(This
->uris
[i
]);
1143 heap_free(This
->uris
);
1144 xmlHashFree(This
->cache
, cache_free
);
1145 release_dispex(&This
->dispex
);
1152 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1155 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1156 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1159 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1160 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1162 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1163 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1164 iTInfo
, lcid
, ppTInfo
);
1167 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1168 REFIID riid
, LPOLESTR
* rgszNames
,
1169 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1171 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1172 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1173 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1176 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1177 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1178 WORD wFlags
, DISPPARAMS
* pDispParams
,
1179 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1182 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1183 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1184 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1187 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1189 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1190 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1191 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1193 if (This
->read_only
) return E_FAIL
;
1199 cache_remove_entry(This
, name
);
1205 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1209 cache_entry_add_ref(entry
);
1217 cache_add_entry(This
, name
, entry
);
1223 xmlDocPtr doc
= NULL
;
1225 CacheEntryType type
;
1226 IXMLDOMNode
* domnode
= NULL
;
1227 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1230 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1234 IXMLDOMNode_Release(domnode
);
1236 return E_INVALIDARG
;
1238 type
= cache_type_from_xmlDocPtr(doc
);
1240 if (type
== CacheEntryType_XSD
)
1242 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1244 else if (type
== CacheEntryType_XDR
)
1246 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1250 WARN("invalid schema!\n");
1254 IXMLDOMNode_Release(domnode
);
1258 cache_entry_add_ref(entry
);
1266 cache_add_entry(This
, name
, entry
);
1273 return E_INVALIDARG
;
1280 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1283 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1287 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1289 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1294 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1295 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1298 /* TODO: this should be read-only */
1299 if (entry
&& entry
->doc
)
1300 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1306 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1308 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1309 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1310 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1312 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1314 cache_remove_entry(This
, name
);
1319 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1321 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1322 TRACE("(%p)->(%p)\n", This
, length
);
1327 *length
= This
->count
;
1331 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1332 LONG index
, BSTR
* uri
)
1334 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1336 TRACE("(%p)->(%i %p)\n", This
, index
, uri
);
1341 if (index
>= This
->count
)
1344 *uri
= bstr_from_xmlChar(This
->uris
[index
]);
1348 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1350 schema_cache
* This
= (schema_cache
*) dest
;
1351 cache_entry
* entry
= (cache_entry
*) data
;
1353 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1355 cache_entry_add_ref(entry
);
1356 cache_add_entry(This
, name
, entry
);
1360 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1361 IXMLDOMSchemaCollection
* collection
)
1363 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1366 TRACE("(%p)->(%p)\n", This
, collection
);
1371 That
= unsafe_impl_from_IXMLDOMSchemaCollection(collection
);
1374 ERR("external collection implementation\n");
1378 /* TODO: detect errors while copying & return E_FAIL */
1379 xmlHashScan(That
->cache
, cache_copy
, This
);
1384 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
, IUnknown
** enumv
)
1386 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1387 TRACE("(%p)->(%p)\n", This
, enumv
);
1388 return create_enumvariant((IUnknown
*)iface
, TRUE
, &schemacache_enumvariant
, (IEnumVARIANT
**)enumv
);
1391 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1393 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1394 FIXME("(%p): stub\n", This
);
1398 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1401 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1402 FIXME("(%p)->(%d): stub\n", This
, value
);
1404 This
->validateOnLoad
= value
;
1405 /* it's ok to disable it, cause we don't validate on load anyway */
1406 if (value
== VARIANT_FALSE
) return S_OK
;
1411 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1412 VARIANT_BOOL
* value
)
1414 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1415 TRACE("(%p)->(%p)\n", This
, value
);
1417 if (!value
) return E_POINTER
;
1418 *value
= This
->validateOnLoad
;
1423 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1424 BSTR namespaceURI
, ISchema
** schema
)
1426 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1427 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1433 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1434 IXMLDOMNode
* node
, ISchemaItem
** item
)
1436 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1437 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1443 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1445 schema_cache_QueryInterface
,
1446 schema_cache_AddRef
,
1447 schema_cache_Release
,
1448 schema_cache_GetTypeInfoCount
,
1449 schema_cache_GetTypeInfo
,
1450 schema_cache_GetIDsOfNames
,
1451 schema_cache_Invoke
,
1454 schema_cache_remove
,
1455 schema_cache_get_length
,
1456 schema_cache_get_namespaceURI
,
1457 schema_cache_addCollection
,
1458 schema_cache_get__newEnum
,
1459 schema_cache_validate
,
1460 schema_cache_put_validateOnLoad
,
1461 schema_cache_get_validateOnLoad
,
1462 schema_cache_getSchema
,
1463 schema_cache_getDeclaration
1466 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1468 xmlSchemaElementPtr decl
= NULL
;
1469 xmlChar
const* nsURI
= get_node_nsURI(node
);
1471 TRACE("(%p, %p)\n", schema
, node
);
1473 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1474 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1476 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1478 FIXME("declaration not found in main schema - need to check schema imports!\n");
1479 /*xmlSchemaImportPtr import;
1481 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1483 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1486 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1492 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1494 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1495 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1496 decl
= decl
->refDecl
;
1497 return (decl
!= NULL
)? decl
->node
: NULL
;
1500 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1502 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1503 xmlSchemaPtr schema
;
1505 TRACE("(%p, %p)\n", This
, tree
);
1510 if (tree
->type
== XML_DOCUMENT_NODE
)
1511 tree
= xmlDocGetRootElement(tree
->doc
);
1513 schema
= get_node_schema(This
, tree
);
1514 /* TODO: if the ns is not in the cache, and it's a URL,
1515 * do we try to load from that? */
1517 return Schema_validate_tree(schema
, tree
);
1519 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1524 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1526 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1527 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1528 XDR_DT dt
= DT_INVALID
;
1530 TRACE("(%p, %p)\n", This
, node
);
1532 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1534 dt
= str_to_dt(node
->name
, -1);
1539 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1541 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1544 dt
= str_to_dt(str
, -1);
1552 static const tid_t schemacache_iface_tids
[] = {
1553 IXMLDOMSchemaCollection2_tid
,
1557 static dispex_static_data_t schemacache_dispex
= {
1559 IXMLDOMSchemaCollection2_tid
,
1561 schemacache_iface_tids
1564 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1566 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1568 return E_OUTOFMEMORY
;
1570 TRACE("(%d %p %p)\n", version
, outer
, obj
);
1572 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1573 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1574 This
->allocated
= 10;
1576 This
->uris
= heap_alloc(This
->allocated
*sizeof(xmlChar
*));
1578 This
->version
= version
;
1579 This
->validateOnLoad
= VARIANT_TRUE
;
1580 This
->read_only
= 0;
1581 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1583 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1589 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1591 MESSAGE("This program tried to use a SchemaCache object, but\n"
1592 "libxml2 support was not present at compile time.\n");