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 inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
791 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
794 static inline CacheEntryType
cache_type_from_xmlDocPtr(xmlDocPtr schema
)
796 xmlNodePtr root
= NULL
;
798 root
= xmlDocGetRootElement(schema
);
799 if (root
&& root
->ns
)
802 if (xmlStrEqual(root
->name
, XDR_schema
) &&
803 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
805 return CacheEntryType_XDR
;
807 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
808 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
810 return CacheEntryType_XSD
;
813 return CacheEntryType_Invalid
;
816 static BOOL
link_datatypes(xmlDocPtr schema
)
818 xmlNodePtr root
, next
, child
;
821 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader
);
822 root
= xmlDocGetRootElement(schema
);
826 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
828 if (xmlStrEqual(ns
->href
, DT_nsURI
))
835 next
= xmlFirstElementChild(root
);
836 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
837 if (next
) child
= xmlAddPrevSibling(next
, child
);
838 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
839 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
844 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
846 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
847 xmlSchemaParserCtxtPtr spctx
;
848 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
850 link_datatypes(new_doc
);
852 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
853 * do we need to do something special here? */
854 entry
->type
= CacheEntryType_XSD
;
856 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
858 if ((entry
->schema
= Schema_parse(spctx
)))
860 xmldoc_init(entry
->schema
->doc
, v
);
861 entry
->doc
= entry
->schema
->doc
;
862 xmldoc_add_ref(entry
->doc
);
866 FIXME("failed to parse doc\n");
871 xmlSchemaFreeParserCtxt(spctx
);
875 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
877 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
878 xmlSchemaParserCtxtPtr spctx
;
879 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
881 link_datatypes(xsd_doc
);
883 entry
->type
= CacheEntryType_XDR
;
885 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
887 if ((entry
->schema
= Schema_parse(spctx
)))
889 entry
->doc
= new_doc
;
890 xmldoc_init(entry
->schema
->doc
, version
);
891 xmldoc_init(entry
->doc
, version
);
892 xmldoc_add_ref(entry
->doc
);
893 xmldoc_add_ref(entry
->schema
->doc
);
897 FIXME("failed to parse doc\n");
903 xmlSchemaFreeParserCtxt(spctx
);
908 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
911 IXMLDOMDocument3
* domdoc
= NULL
;
912 xmlDocPtr doc
= NULL
;
913 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
914 VARIANT_BOOL b
= VARIANT_FALSE
;
915 CacheEntryType type
= CacheEntryType_Invalid
;
919 FIXME("failed to create domdoc\n");
922 assert(domdoc
!= NULL
);
923 assert(V_VT(&url
) == VT_BSTR
);
925 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
928 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
929 if (b
!= VARIANT_TRUE
)
931 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url
)));
932 IXMLDOMDocument3_Release(domdoc
);
936 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
937 type
= cache_type_from_xmlDocPtr(doc
);
941 case CacheEntryType_XSD
:
942 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
944 case CacheEntryType_XDR
:
945 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
949 FIXME("invalid schema\n");
952 IXMLDOMDocument3_Release(domdoc
);
957 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
959 cache_entry_release((cache_entry
*)data
);
962 /* This one adds all namespaces defined in document to a cache, without anything
963 associated with uri obviously.
964 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
965 it uses additional node type to describe namespace definition attribute while
966 in msxml it's expected to be a normal attribute - as a workaround document is
967 queried at libxml2 level here. */
968 HRESULT
cache_from_doc_ns(IXMLDOMSchemaCollection2
*iface
, xmlnode
*node
)
970 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
971 static const xmlChar query
[] = "//*/namespace::*";
972 xmlXPathObjectPtr nodeset
;
973 xmlXPathContextPtr ctxt
;
977 ctxt
= xmlXPathNewContext(node
->node
->doc
);
979 nodeset
= xmlXPathEvalExpression(query
, ctxt
);
980 xmlXPathFreeContext(ctxt
);
984 int pos
= 0, len
= xmlXPathNodeSetGetLength(nodeset
->nodesetval
);
986 if (len
== 0) return S_OK
;
990 xmlNodePtr node
= xmlXPathNodeSetItem(nodeset
->nodesetval
, pos
);
991 if (node
->type
== XML_NAMESPACE_DECL
)
993 static const xmlChar defns
[] = "http://www.w3.org/XML/1998/namespace";
994 xmlNsPtr ns
= (xmlNsPtr
)node
;
997 /* filter out default uri */
998 if (xmlStrEqual(ns
->href
, defns
))
1004 entry
= heap_alloc(sizeof(cache_entry
));
1005 entry
->type
= CacheEntryType_NS
;
1007 entry
->schema
= NULL
;
1010 xmlHashRemoveEntry(This
->cache
, ns
->href
, cache_free
);
1011 xmlHashAddEntry(This
->cache
, ns
->href
, entry
);
1016 xmlXPathFreeObject(nodeset
);
1022 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
1023 REFIID riid
, void** ppvObject
)
1025 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1027 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1029 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
1030 IsEqualIID(riid
, &IID_IDispatch
) ||
1031 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
1032 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
1036 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
1038 return *ppvObject
? S_OK
: E_NOINTERFACE
;
1042 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1044 return E_NOINTERFACE
;
1047 IXMLDOMSchemaCollection2_AddRef(iface
);
1052 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
1054 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1055 LONG ref
= InterlockedIncrement(&This
->ref
);
1056 TRACE("(%p)->(%d)\n", This
, ref
);
1060 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
1062 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1063 LONG ref
= InterlockedDecrement(&This
->ref
);
1064 TRACE("(%p)->(%d)\n", This
, ref
);
1068 xmlHashFree(This
->cache
, cache_free
);
1069 release_dispex(&This
->dispex
);
1076 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1079 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1080 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1083 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1084 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1086 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1087 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1088 iTInfo
, lcid
, ppTInfo
);
1091 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1092 REFIID riid
, LPOLESTR
* rgszNames
,
1093 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1095 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1096 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1097 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1100 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1101 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1102 WORD wFlags
, DISPPARAMS
* pDispParams
,
1103 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1106 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1107 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1108 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1111 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1113 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1114 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1115 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1117 if (This
->read_only
) return E_FAIL
;
1123 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1129 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1133 cache_entry_add_ref(entry
);
1141 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1142 xmlHashAddEntry(This
->cache
, name
, entry
);
1148 xmlDocPtr doc
= NULL
;
1150 CacheEntryType type
;
1151 IXMLDOMNode
* domnode
= NULL
;
1152 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1155 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1159 IXMLDOMNode_Release(domnode
);
1161 return E_INVALIDARG
;
1163 type
= cache_type_from_xmlDocPtr(doc
);
1165 if (type
== CacheEntryType_XSD
)
1167 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1169 else if (type
== CacheEntryType_XDR
)
1171 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1175 WARN("invalid schema!\n");
1179 IXMLDOMNode_Release(domnode
);
1183 cache_entry_add_ref(entry
);
1191 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1192 xmlHashAddEntry(This
->cache
, name
, entry
);
1199 return E_INVALIDARG
;
1206 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1209 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1213 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1215 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1220 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1221 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1224 /* TODO: this should be read-only */
1225 if (entry
&& entry
->doc
)
1226 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1232 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1234 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1235 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1236 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1238 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1240 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1245 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1247 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1248 TRACE("(%p)->(%p)\n", This
, length
);
1252 *length
= xmlHashSize(This
->cache
);
1256 static void cache_index(void* data
/* ignored */, void* index
, xmlChar
* name
)
1258 cache_index_data
* index_data
= (cache_index_data
*)index
;
1260 if (index_data
->index
-- == 0)
1261 *index_data
->out
= bstr_from_xmlChar(name
);
1264 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1265 LONG index
, BSTR
* len
)
1267 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1268 cache_index_data data
= {index
, len
};
1269 TRACE("(%p)->(%i %p)\n", This
, index
, len
);
1274 if (index
>= xmlHashSize(This
->cache
))
1278 xmlHashScan(This
->cache
, cache_index
, &data
);
1282 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1284 schema_cache
* This
= (schema_cache
*) dest
;
1285 cache_entry
* entry
= (cache_entry
*) data
;
1287 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1289 cache_entry_add_ref(entry
);
1290 xmlHashAddEntry(This
->cache
, name
, entry
);
1294 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1295 IXMLDOMSchemaCollection
* otherCollection
)
1297 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1298 schema_cache
* That
= impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2
*)otherCollection
);
1299 TRACE("(%p)->(%p)\n", This
, That
);
1301 if (!otherCollection
)
1304 /* TODO: detect errors while copying & return E_FAIL */
1305 xmlHashScan(That
->cache
, cache_copy
, This
);
1310 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
1313 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1314 FIXME("(%p)->(%p): stub\n", This
, ppUnk
);
1320 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1322 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1323 FIXME("(%p): stub\n", This
);
1327 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1330 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1331 FIXME("(%p)->(%d): stub\n", This
, value
);
1333 This
->validateOnLoad
= value
;
1334 /* it's ok to disable it, cause we don't validate on load anyway */
1335 if (value
== VARIANT_FALSE
) return S_OK
;
1340 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1341 VARIANT_BOOL
* value
)
1343 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1344 TRACE("(%p)->(%p)\n", This
, value
);
1346 if (!value
) return E_POINTER
;
1347 *value
= This
->validateOnLoad
;
1352 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1353 BSTR namespaceURI
, ISchema
** schema
)
1355 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1356 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1362 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1363 IXMLDOMNode
* node
, ISchemaItem
** item
)
1365 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1366 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1372 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1374 schema_cache_QueryInterface
,
1375 schema_cache_AddRef
,
1376 schema_cache_Release
,
1377 schema_cache_GetTypeInfoCount
,
1378 schema_cache_GetTypeInfo
,
1379 schema_cache_GetIDsOfNames
,
1380 schema_cache_Invoke
,
1383 schema_cache_remove
,
1384 schema_cache_get_length
,
1385 schema_cache_get_namespaceURI
,
1386 schema_cache_addCollection
,
1387 schema_cache_get__newEnum
,
1388 schema_cache_validate
,
1389 schema_cache_put_validateOnLoad
,
1390 schema_cache_get_validateOnLoad
,
1391 schema_cache_getSchema
,
1392 schema_cache_getDeclaration
1395 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1397 xmlSchemaElementPtr decl
= NULL
;
1398 xmlChar
const* nsURI
= get_node_nsURI(node
);
1400 TRACE("(%p, %p)\n", schema
, node
);
1402 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1403 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1405 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1407 FIXME("declaration not found in main schema - need to check schema imports!\n");
1408 /*xmlSchemaImportPtr import;
1410 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1412 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1415 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1421 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1423 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1424 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1425 decl
= decl
->refDecl
;
1426 return (decl
!= NULL
)? decl
->node
: NULL
;
1429 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1431 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1432 xmlSchemaPtr schema
;
1434 TRACE("(%p, %p)\n", This
, tree
);
1439 if (tree
->type
== XML_DOCUMENT_NODE
)
1440 tree
= xmlDocGetRootElement(tree
->doc
);
1442 schema
= get_node_schema(This
, tree
);
1443 /* TODO: if the ns is not in the cache, and it's a URL,
1444 * do we try to load from that? */
1446 return Schema_validate_tree(schema
, tree
);
1448 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1453 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1455 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1456 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1457 XDR_DT dt
= DT_INVALID
;
1459 TRACE("(%p, %p)\n", This
, node
);
1461 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1463 dt
= str_to_dt(node
->name
, -1);
1468 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1470 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1473 dt
= str_to_dt(str
, -1);
1481 static const tid_t schemacache_iface_tids
[] = {
1482 IXMLDOMSchemaCollection2_tid
,
1486 static dispex_static_data_t schemacache_dispex
= {
1488 IXMLDOMSchemaCollection2_tid
,
1490 schemacache_iface_tids
1493 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1495 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1497 return E_OUTOFMEMORY
;
1499 TRACE("(%d %p %p)\n", version
, outer
, obj
);
1501 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1502 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1504 This
->version
= version
;
1505 This
->validateOnLoad
= VARIANT_TRUE
;
1506 This
->read_only
= 0;
1507 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1509 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1515 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1517 MESSAGE("This program tried to use a SchemaCache object, but\n"
1518 "libxml2 support was not present at compile time.\n");