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
const* 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
;
104 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 inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
293 xmlSchemaValidCtxtPtr svctx
;
296 TRACE("(%p, %p)\n", schema
, tree
);
297 /* TODO: if validateOnLoad property is false,
298 * we probably need to validate the schema here. */
299 svctx
= xmlSchemaNewValidCtxt(schema
);
300 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
301 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
302 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
305 if (tree
->type
== XML_DOCUMENT_NODE
)
306 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
308 err
= xmlSchemaValidateOneElement(svctx
, tree
);
310 xmlSchemaFreeValidCtxt(svctx
);
311 return err
? S_FALSE
: S_OK
;
314 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
316 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
321 hval
+= hash_assoc_values
[str
[10]];
324 hval
+= hash_assoc_values
[str
[9]];
327 hval
+= hash_assoc_values
[str
[8]];
330 hval
+= hash_assoc_values
[str
[7]];
333 hval
+= hash_assoc_values
[str
[6]];
336 hval
+= hash_assoc_values
[str
[5]];
339 hval
+= hash_assoc_values
[str
[4]];
342 hval
+= hash_assoc_values
[str
[3]];
345 hval
+= hash_assoc_values
[str
[2]];
348 hval
+= hash_assoc_values
[str
[1]];
351 hval
+= hash_assoc_values
[str
[0]];
357 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
359 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
364 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
367 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
370 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
373 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
376 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
379 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
382 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
385 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
388 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
391 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
394 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
400 static const xmlChar
*const DT_string_table
[LAST_DT
] =
440 static const WCHAR
*const DT_wstring_table
[LAST_DT
] =
480 static const XDR_DT DT_lookup_table
[] =
533 -1, -1, -1, -1, -1, -1, -1, -1, -1,
534 -1, -1, -1, -1, -1, -1, -1, -1, -1,
535 -1, -1, -1, -1, -1, -1, -1, -1, -1,
536 -1, -1, -1, -1, -1, -1, -1, -1, -1,
537 -1, -1, -1, -1, -1, -1, -1, -1, -1,
538 -1, -1, -1, -1, -1, -1, -1, -1,
542 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
544 DWORD hash
= dt_hash(str
, len
);
545 XDR_DT dt
= DT_INVALID
;
547 if (hash
<= DT_MAX_HASH_VALUE
)
548 dt
= DT_lookup_table
[hash
];
550 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
556 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
558 DWORD hash
= dt_hash_bstr(bstr
, 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
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
570 xmlChar
const* dt_to_str(XDR_DT dt
)
572 if (dt
== DT_INVALID
)
575 return DT_string_table
[dt
];
578 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
580 if (dt
== DT_INVALID
)
583 return DT_wstring_table
[dt
];
586 const char* debugstr_dt(XDR_DT dt
)
588 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
591 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
598 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), debugstr_a((char const*)content
));
600 if (!datatypes_schema
)
602 xmlSchemaParserCtxtPtr spctx
;
603 assert(datatypes_src
!= NULL
);
604 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
605 datatypes_schema
= Schema_parse(spctx
);
606 xmlSchemaFreeParserCtxt(spctx
);
642 if (!datatypes_schema
)
644 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
645 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
647 /* Hopefully they don't need much in the way of XDR datatypes support... */
651 if (content
&& xmlStrlen(content
))
653 tmp_doc
= xmlNewDoc(NULL
);
654 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
655 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
657 xmlDocSetRootElement(tmp_doc
, node
);
659 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
663 { /* probably the node is being created manually and has no content yet */
668 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
673 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
675 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
678 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
680 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
681 xmlHashLookup(This
->cache
, nsURI
);
684 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
686 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
687 return (!entry
)? NULL
: entry
->schema
;
690 static xmlExternalEntityLoader _external_entity_loader
;
692 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
693 xmlParserCtxtPtr ctxt
)
695 xmlParserInputPtr input
;
697 TRACE("(%s %s %p)\n", debugstr_a(URL
), debugstr_a(ID
), ctxt
);
699 assert(MSXML_hInstance
!= NULL
);
700 assert(datatypes_rsrc
!= NULL
);
701 assert(datatypes_handle
!= NULL
);
702 assert(datatypes_src
!= NULL
);
704 /* TODO: if the desired schema is in the cache, load it from there */
705 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
707 TRACE("loading built-in schema for %s\n", URL
);
708 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
712 input
= _external_entity_loader(URL
, ID
, ctxt
);
718 void schemasInit(void)
722 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
724 FIXME("failed to find resource for %s\n", DT_nsURI
);
728 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
730 FIXME("failed to load resource for %s\n", DT_nsURI
);
733 buf
= LockResource(datatypes_handle
);
734 len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
) - 1;
736 /* Resource is loaded as raw data,
737 * need a null-terminated string */
738 while (buf
[len
] != '>')
740 datatypes_src
= BAD_CAST buf
;
741 datatypes_len
= len
+ 1;
743 if (xmlGetExternalEntityLoader() != external_entity_loader
)
745 _external_entity_loader
= xmlGetExternalEntityLoader();
746 xmlSetExternalEntityLoader(external_entity_loader
);
750 void schemasCleanup(void)
752 xmlSchemaFree(datatypes_schema
);
753 xmlSetExternalEntityLoader(_external_entity_loader
);
756 static LONG
cache_entry_add_ref(cache_entry
* entry
)
758 LONG ref
= InterlockedIncrement(&entry
->ref
);
759 TRACE("(%p)->(%d)\n", entry
, ref
);
763 static LONG
cache_entry_release(cache_entry
* entry
)
765 LONG ref
= InterlockedDecrement(&entry
->ref
);
766 TRACE("(%p)->(%d)\n", entry
, ref
);
770 if (entry
->type
== CacheEntryType_XSD
)
772 xmldoc_release(entry
->doc
);
773 entry
->schema
->doc
= NULL
;
774 xmlSchemaFree(entry
->schema
);
776 else if (entry
->type
== CacheEntryType_XDR
)
778 xmldoc_release(entry
->doc
);
779 xmldoc_release(entry
->schema
->doc
);
780 entry
->schema
->doc
= NULL
;
781 xmlSchemaFree(entry
->schema
);
789 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
;
791 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
793 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
796 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
* iface
)
798 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
801 static inline schema_cache
* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
*iface
)
803 return iface
->lpVtbl
== (void*)&XMLDOMSchemaCollection2Vtbl
? impl_from_IXMLDOMSchemaCollection(iface
) : NULL
;
806 static inline CacheEntryType
cache_type_from_xmlDocPtr(xmlDocPtr schema
)
808 xmlNodePtr root
= NULL
;
810 root
= xmlDocGetRootElement(schema
);
811 if (root
&& root
->ns
)
814 if (xmlStrEqual(root
->name
, XDR_schema
) &&
815 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
817 return CacheEntryType_XDR
;
819 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
820 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
822 return CacheEntryType_XSD
;
825 return CacheEntryType_Invalid
;
828 static BOOL
link_datatypes(xmlDocPtr schema
)
830 xmlNodePtr root
, next
, child
;
833 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader
);
834 root
= xmlDocGetRootElement(schema
);
838 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
840 if (xmlStrEqual(ns
->href
, DT_nsURI
))
847 next
= xmlFirstElementChild(root
);
848 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
849 if (next
) child
= xmlAddPrevSibling(next
, child
);
850 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
851 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
856 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
858 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
859 xmlSchemaParserCtxtPtr spctx
;
860 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
862 link_datatypes(new_doc
);
864 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
865 * do we need to do something special here? */
866 entry
->type
= CacheEntryType_XSD
;
868 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
870 if ((entry
->schema
= Schema_parse(spctx
)))
872 xmldoc_init(entry
->schema
->doc
, v
);
873 entry
->doc
= entry
->schema
->doc
;
874 xmldoc_add_ref(entry
->doc
);
878 FIXME("failed to parse doc\n");
883 xmlSchemaFreeParserCtxt(spctx
);
887 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
889 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
890 xmlSchemaParserCtxtPtr spctx
;
891 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
893 link_datatypes(xsd_doc
);
895 entry
->type
= CacheEntryType_XDR
;
897 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
899 if ((entry
->schema
= Schema_parse(spctx
)))
901 entry
->doc
= new_doc
;
902 xmldoc_init(entry
->schema
->doc
, version
);
903 xmldoc_init(entry
->doc
, version
);
904 xmldoc_add_ref(entry
->doc
);
905 xmldoc_add_ref(entry
->schema
->doc
);
909 FIXME("failed to parse doc\n");
915 xmlSchemaFreeParserCtxt(spctx
);
920 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
923 IXMLDOMDocument3
* domdoc
= NULL
;
924 xmlDocPtr doc
= NULL
;
925 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
926 VARIANT_BOOL b
= VARIANT_FALSE
;
927 CacheEntryType type
= CacheEntryType_Invalid
;
931 FIXME("failed to create domdoc\n");
934 assert(domdoc
!= NULL
);
935 assert(V_VT(&url
) == VT_BSTR
);
937 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
940 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
941 if (b
!= VARIANT_TRUE
)
943 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url
)));
944 IXMLDOMDocument3_Release(domdoc
);
948 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
949 type
= cache_type_from_xmlDocPtr(doc
);
953 case CacheEntryType_XSD
:
954 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
956 case CacheEntryType_XDR
:
957 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
961 FIXME("invalid schema\n");
964 IXMLDOMDocument3_Release(domdoc
);
969 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
971 cache_entry_release((cache_entry
*)data
);
974 /* This one adds all namespaces defined in document to a cache, without anything
975 associated with uri obviously.
976 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
977 it uses additional node type to describe namespace definition attribute while
978 in msxml it's expected to be a normal attribute - as a workaround document is
979 queried at libxml2 level here. */
980 HRESULT
cache_from_doc_ns(IXMLDOMSchemaCollection2
*iface
, xmlnode
*node
)
982 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
983 static const xmlChar query
[] = "//*/namespace::*";
984 xmlXPathObjectPtr nodeset
;
985 xmlXPathContextPtr ctxt
;
989 ctxt
= xmlXPathNewContext(node
->node
->doc
);
991 nodeset
= xmlXPathEvalExpression(query
, ctxt
);
992 xmlXPathFreeContext(ctxt
);
996 int pos
= 0, len
= xmlXPathNodeSetGetLength(nodeset
->nodesetval
);
998 if (len
== 0) return S_OK
;
1002 xmlNodePtr node
= xmlXPathNodeSetItem(nodeset
->nodesetval
, pos
);
1003 if (node
->type
== XML_NAMESPACE_DECL
)
1005 static const xmlChar defns
[] = "http://www.w3.org/XML/1998/namespace";
1006 xmlNsPtr ns
= (xmlNsPtr
)node
;
1009 /* filter out default uri */
1010 if (xmlStrEqual(ns
->href
, defns
))
1016 entry
= heap_alloc(sizeof(cache_entry
));
1017 entry
->type
= CacheEntryType_NS
;
1019 entry
->schema
= NULL
;
1022 xmlHashRemoveEntry(This
->cache
, ns
->href
, cache_free
);
1023 xmlHashAddEntry(This
->cache
, ns
->href
, entry
);
1028 xmlXPathFreeObject(nodeset
);
1034 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
1035 REFIID riid
, void** ppvObject
)
1037 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1039 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1041 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
1042 IsEqualIID(riid
, &IID_IDispatch
) ||
1043 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
1044 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
1048 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
1050 return *ppvObject
? S_OK
: E_NOINTERFACE
;
1054 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1056 return E_NOINTERFACE
;
1059 IXMLDOMSchemaCollection2_AddRef(iface
);
1064 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
1066 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1067 LONG ref
= InterlockedIncrement(&This
->ref
);
1068 TRACE("(%p)->(%d)\n", This
, ref
);
1072 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
1074 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1075 LONG ref
= InterlockedDecrement(&This
->ref
);
1076 TRACE("(%p)->(%d)\n", This
, ref
);
1080 xmlHashFree(This
->cache
, cache_free
);
1081 release_dispex(&This
->dispex
);
1088 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1091 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1092 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1095 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1096 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1098 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1099 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1100 iTInfo
, lcid
, ppTInfo
);
1103 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1104 REFIID riid
, LPOLESTR
* rgszNames
,
1105 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1107 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1108 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1109 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1112 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1113 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1114 WORD wFlags
, DISPPARAMS
* pDispParams
,
1115 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1118 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1119 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1120 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1123 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1125 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1126 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1127 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1129 if (This
->read_only
) return E_FAIL
;
1135 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1141 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1145 cache_entry_add_ref(entry
);
1153 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1154 xmlHashAddEntry(This
->cache
, name
, entry
);
1160 xmlDocPtr doc
= NULL
;
1162 CacheEntryType type
;
1163 IXMLDOMNode
* domnode
= NULL
;
1164 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1167 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1171 IXMLDOMNode_Release(domnode
);
1173 return E_INVALIDARG
;
1175 type
= cache_type_from_xmlDocPtr(doc
);
1177 if (type
== CacheEntryType_XSD
)
1179 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1181 else if (type
== CacheEntryType_XDR
)
1183 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1187 WARN("invalid schema!\n");
1191 IXMLDOMNode_Release(domnode
);
1195 cache_entry_add_ref(entry
);
1203 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1204 xmlHashAddEntry(This
->cache
, name
, entry
);
1211 return E_INVALIDARG
;
1218 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1221 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1225 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1227 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1232 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1233 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1236 /* TODO: this should be read-only */
1237 if (entry
&& entry
->doc
)
1238 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1244 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1246 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1247 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1248 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1250 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1252 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1257 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1259 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1260 TRACE("(%p)->(%p)\n", This
, length
);
1264 *length
= xmlHashSize(This
->cache
);
1268 static void cache_index(void* data
/* ignored */, void* index
, xmlChar
* name
)
1270 cache_index_data
* index_data
= (cache_index_data
*)index
;
1272 if (index_data
->index
-- == 0)
1273 *index_data
->out
= bstr_from_xmlChar(name
);
1276 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1277 LONG index
, BSTR
* len
)
1279 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1280 cache_index_data data
= {index
, len
};
1281 TRACE("(%p)->(%i %p)\n", This
, index
, len
);
1286 if (index
>= xmlHashSize(This
->cache
))
1290 xmlHashScan(This
->cache
, cache_index
, &data
);
1294 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1296 schema_cache
* This
= (schema_cache
*) dest
;
1297 cache_entry
* entry
= (cache_entry
*) data
;
1299 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1301 cache_entry_add_ref(entry
);
1302 xmlHashAddEntry(This
->cache
, name
, entry
);
1306 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1307 IXMLDOMSchemaCollection
* collection
)
1309 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1312 TRACE("(%p)->(%p)\n", This
, collection
);
1317 That
= unsafe_impl_from_IXMLDOMSchemaCollection(collection
);
1320 ERR("external collection implementation\n");
1324 /* TODO: detect errors while copying & return E_FAIL */
1325 xmlHashScan(That
->cache
, cache_copy
, This
);
1330 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
1333 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1334 FIXME("(%p)->(%p): stub\n", This
, ppUnk
);
1340 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1342 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1343 FIXME("(%p): stub\n", This
);
1347 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1350 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1351 FIXME("(%p)->(%d): stub\n", This
, value
);
1353 This
->validateOnLoad
= value
;
1354 /* it's ok to disable it, cause we don't validate on load anyway */
1355 if (value
== VARIANT_FALSE
) return S_OK
;
1360 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1361 VARIANT_BOOL
* value
)
1363 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1364 TRACE("(%p)->(%p)\n", This
, value
);
1366 if (!value
) return E_POINTER
;
1367 *value
= This
->validateOnLoad
;
1372 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1373 BSTR namespaceURI
, ISchema
** schema
)
1375 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1376 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1382 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1383 IXMLDOMNode
* node
, ISchemaItem
** item
)
1385 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1386 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1392 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1394 schema_cache_QueryInterface
,
1395 schema_cache_AddRef
,
1396 schema_cache_Release
,
1397 schema_cache_GetTypeInfoCount
,
1398 schema_cache_GetTypeInfo
,
1399 schema_cache_GetIDsOfNames
,
1400 schema_cache_Invoke
,
1403 schema_cache_remove
,
1404 schema_cache_get_length
,
1405 schema_cache_get_namespaceURI
,
1406 schema_cache_addCollection
,
1407 schema_cache_get__newEnum
,
1408 schema_cache_validate
,
1409 schema_cache_put_validateOnLoad
,
1410 schema_cache_get_validateOnLoad
,
1411 schema_cache_getSchema
,
1412 schema_cache_getDeclaration
1415 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1417 xmlSchemaElementPtr decl
= NULL
;
1418 xmlChar
const* nsURI
= get_node_nsURI(node
);
1420 TRACE("(%p, %p)\n", schema
, node
);
1422 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1423 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1425 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1427 FIXME("declaration not found in main schema - need to check schema imports!\n");
1428 /*xmlSchemaImportPtr import;
1430 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1432 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1435 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1441 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1443 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1444 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1445 decl
= decl
->refDecl
;
1446 return (decl
!= NULL
)? decl
->node
: NULL
;
1449 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1451 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1452 xmlSchemaPtr schema
;
1454 TRACE("(%p, %p)\n", This
, tree
);
1459 if (tree
->type
== XML_DOCUMENT_NODE
)
1460 tree
= xmlDocGetRootElement(tree
->doc
);
1462 schema
= get_node_schema(This
, tree
);
1463 /* TODO: if the ns is not in the cache, and it's a URL,
1464 * do we try to load from that? */
1466 return Schema_validate_tree(schema
, tree
);
1468 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1473 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1475 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1476 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1477 XDR_DT dt
= DT_INVALID
;
1479 TRACE("(%p, %p)\n", This
, node
);
1481 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1483 dt
= str_to_dt(node
->name
, -1);
1488 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1490 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1493 dt
= str_to_dt(str
, -1);
1501 static const tid_t schemacache_iface_tids
[] = {
1502 IXMLDOMSchemaCollection2_tid
,
1506 static dispex_static_data_t schemacache_dispex
= {
1508 IXMLDOMSchemaCollection2_tid
,
1510 schemacache_iface_tids
1513 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1515 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1517 return E_OUTOFMEMORY
;
1519 TRACE("(%d %p %p)\n", version
, outer
, obj
);
1521 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1522 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1524 This
->version
= version
;
1525 This
->validateOnLoad
= VARIANT_TRUE
;
1526 This
->read_only
= 0;
1527 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1529 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1535 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1537 MESSAGE("This program tried to use a SchemaCache object, but\n"
1538 "libxml2 support was not present at compile time.\n");