2 * Schema cache implementation
4 * Copyright 2007 Huw Davies
5 * Copyright 2010 Adam Martinson for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 # include <libxml/xmlerror.h>
30 # include <libxml/tree.h>
31 # include <libxml/xmlschemas.h>
32 # include <libxml/schemasInternals.h>
33 # include <libxml/hash.h>
34 # include <libxml/parser.h>
35 # include <libxml/parserInternals.h>
36 # include <libxml/xmlIO.h>
37 # include <libxml/xmlversion.h>
46 #include "wine/debug.h"
48 #include "msxml_private.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(msxml
);
52 /* We use a chained hashtable, which can hold any number of schemas
53 * TODO: grow/shrink hashtable depending on load factor
54 * TODO: implement read-only where appropriate
57 /* This is just the number of buckets, should be prime */
58 #define DEFAULT_HASHTABLE_SIZE 17
62 xmlDocPtr
XDR_to_XSD_doc(xmlDocPtr xdr_doc
, xmlChar
const* nsURI
);
64 static const xmlChar XSD_schema
[] = "schema";
65 static const xmlChar XSD_nsURI
[] = "http://www.w3.org/2001/XMLSchema";
66 static const xmlChar XDR_schema
[] = "Schema";
67 static const xmlChar XDR_nsURI
[] = "urn:schemas-microsoft-com:xml-data";
68 static const xmlChar DT_nsURI
[] = "urn:schemas-microsoft-com:datatypes";
70 static xmlChar
const* datatypes_src
;
71 static int datatypes_len
;
72 static HGLOBAL datatypes_handle
;
73 static HRSRC datatypes_rsrc
;
74 static xmlSchemaPtr datatypes_schema
;
76 static const WCHAR emptyW
[] = {0};
84 typedef enum _SCHEMA_TYPE
{
93 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface
;
96 MSXML_VERSION version
;
97 xmlHashTablePtr cache
;
99 VARIANT_BOOL validateOnLoad
;
102 typedef struct _cache_entry
110 typedef struct _cache_index_data
116 /* datatypes lookup stuff
117 * generated with help from gperf */
118 #define DT_MIN_STR_LEN 2
119 #define DT_MAX_STR_LEN 11
120 #define DT_MIN_HASH_VALUE 2
121 #define DT_MAX_HASH_VALUE 115
123 static const xmlChar DT_bin_base64
[] = "bin.base64";
124 static const xmlChar DT_bin_hex
[] = "bin.hex";
125 static const xmlChar DT_boolean
[] = "boolean";
126 static const xmlChar DT_char
[] = "char";
127 static const xmlChar DT_date
[] = "date";
128 static const xmlChar DT_date_tz
[] = "date.tz";
129 static const xmlChar DT_dateTime
[] = "dateTime";
130 static const xmlChar DT_dateTime_tz
[] = "dateTime.tz";
131 static const xmlChar DT_entity
[] = "entity";
132 static const xmlChar DT_entities
[] = "entities";
133 static const xmlChar DT_enumeration
[] = "enumeration";
134 static const xmlChar DT_fixed_14_4
[] = "fixed.14.4";
135 static const xmlChar DT_float
[] = "float";
136 static const xmlChar DT_i1
[] = "i1";
137 static const xmlChar DT_i2
[] = "i2";
138 static const xmlChar DT_i4
[] = "i4";
139 static const xmlChar DT_i8
[] = "i8";
140 static const xmlChar DT_id
[] = "id";
141 static const xmlChar DT_idref
[] = "idref";
142 static const xmlChar DT_idrefs
[] = "idrefs";
143 static const xmlChar DT_int
[] = "int";
144 static const xmlChar DT_nmtoken
[] = "nmtoken";
145 static const xmlChar DT_nmtokens
[] = "nmtokens";
146 static const xmlChar DT_notation
[] = "notation";
147 static const xmlChar DT_number
[] = "number";
148 static const xmlChar DT_r4
[] = "r4";
149 static const xmlChar DT_r8
[] = "r8";
150 static const xmlChar DT_string
[] = "string";
151 static const xmlChar DT_time
[] = "time";
152 static const xmlChar DT_time_tz
[] = "time.tz";
153 static const xmlChar DT_ui1
[] = "ui1";
154 static const xmlChar DT_ui2
[] = "ui2";
155 static const xmlChar DT_ui4
[] = "ui4";
156 static const xmlChar DT_ui8
[] = "ui8";
157 static const xmlChar DT_uri
[] = "uri";
158 static const xmlChar DT_uuid
[] = "uuid";
160 static const OLECHAR wDT_bin_base64
[] = {'b','i','n','.','b','a','s','e','6','4',0};
161 static const OLECHAR wDT_bin_hex
[] = {'b','i','n','.','h','e','x',0};
162 static const OLECHAR wDT_boolean
[] = {'b','o','o','l','e','a','n',0};
163 static const OLECHAR wDT_char
[] = {'c','h','a','r',0};
164 static const OLECHAR wDT_date
[] = {'d','a','t','e',0};
165 static const OLECHAR wDT_date_tz
[] = {'d','a','t','e','.','t','z',0};
166 static const OLECHAR wDT_dateTime
[] = {'d','a','t','e','T','i','m','e',0};
167 static const OLECHAR wDT_dateTime_tz
[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
168 static const OLECHAR wDT_entity
[] = {'e','n','t','i','t','y',0};
169 static const OLECHAR wDT_entities
[] = {'e','n','t','i','t','i','e','s',0};
170 static const OLECHAR wDT_enumeration
[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
171 static const OLECHAR wDT_fixed_14_4
[] = {'f','i','x','e','d','.','1','4','.','4',0};
172 static const OLECHAR wDT_float
[] = {'f','l','o','a','t',0};
173 static const OLECHAR wDT_i1
[] = {'i','1',0};
174 static const OLECHAR wDT_i2
[] = {'i','2',0};
175 static const OLECHAR wDT_i4
[] = {'i','4',0};
176 static const OLECHAR wDT_i8
[] = {'i','8',0};
177 static const OLECHAR wDT_id
[] = {'i','d',0};
178 static const OLECHAR wDT_idref
[] = {'i','d','r','e','f',0};
179 static const OLECHAR wDT_idrefs
[] = {'i','d','r','e','f','s',0};
180 static const OLECHAR wDT_int
[] = {'i','n','t',0};
181 static const OLECHAR wDT_nmtoken
[] = {'n','m','t','o','k','e','n',0};
182 static const OLECHAR wDT_nmtokens
[] = {'n','m','t','o','k','e','n','s',0};
183 static const OLECHAR wDT_notation
[] = {'n','o','t','a','t','i','o','n',0};
184 static const OLECHAR wDT_number
[] = {'n','u','m','b','e','r',0};
185 static const OLECHAR wDT_r4
[] = {'r','4',0};
186 static const OLECHAR wDT_r8
[] = {'r','8',0};
187 static const OLECHAR wDT_string
[] = {'s','t','r','i','n','g',0};
188 static const OLECHAR wDT_time
[] = {'t','i','m','e',0};
189 static const OLECHAR wDT_time_tz
[] = {'t','i','m','e','.','t','z',0};
190 static const OLECHAR wDT_ui1
[] = {'u','i','1',0};
191 static const OLECHAR wDT_ui2
[] = {'u','i','2',0};
192 static const OLECHAR wDT_ui4
[] = {'u','i','4',0};
193 static const OLECHAR wDT_ui8
[] = {'u','i','8',0};
194 static const OLECHAR wDT_uri
[] = {'u','r','i',0};
195 static const OLECHAR wDT_uuid
[] = {'u','u','i','d',0};
197 static const BYTE hash_assoc_values
[] =
199 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
200 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
201 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
202 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
203 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
204 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
205 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
206 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
207 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
208 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
209 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
210 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
211 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
213 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
214 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
215 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
216 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
217 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
218 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
219 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
220 116, 116, 116, 116, 116, 116, 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
227 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
231 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
235 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
239 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
243 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
244 static void parser_serror(void* ctx
, xmlErrorPtr err
)
246 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
250 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
252 TRACE("(%p)\n", spctx
);
254 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
255 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
256 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
259 return xmlSchemaParse(spctx
);
262 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
266 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
270 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
274 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
278 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
279 static void validate_serror(void* ctx
, xmlErrorPtr err
)
281 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
285 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
287 xmlSchemaValidCtxtPtr svctx
;
290 TRACE("(%p, %p)\n", schema
, tree
);
291 /* TODO: if validateOnLoad property is false,
292 * we probably need to validate the schema here. */
293 svctx
= xmlSchemaNewValidCtxt(schema
);
294 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
295 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
296 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
299 if (tree
->type
== XML_DOCUMENT_NODE
)
300 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
302 err
= xmlSchemaValidateOneElement(svctx
, tree
);
304 xmlSchemaFreeValidCtxt(svctx
);
305 return err
? S_FALSE
: S_OK
;
308 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
310 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
315 hval
+= hash_assoc_values
[str
[10]];
318 hval
+= hash_assoc_values
[str
[9]];
321 hval
+= hash_assoc_values
[str
[8]];
324 hval
+= hash_assoc_values
[str
[7]];
327 hval
+= hash_assoc_values
[str
[6]];
330 hval
+= hash_assoc_values
[str
[5]];
333 hval
+= hash_assoc_values
[str
[4]];
336 hval
+= hash_assoc_values
[str
[3]];
339 hval
+= hash_assoc_values
[str
[2]];
342 hval
+= hash_assoc_values
[str
[1]];
345 hval
+= hash_assoc_values
[str
[0]];
351 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
353 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
358 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
361 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
364 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
367 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
370 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
373 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
376 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
379 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
382 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
385 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
388 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
394 static const xmlChar
*const DT_string_table
[LAST_DT
] =
434 static const WCHAR
*const DT_wstring_table
[LAST_DT
] =
474 static const XDR_DT DT_lookup_table
[] =
527 -1, -1, -1, -1, -1, -1, -1, -1, -1,
528 -1, -1, -1, -1, -1, -1, -1, -1, -1,
529 -1, -1, -1, -1, -1, -1, -1, -1, -1,
530 -1, -1, -1, -1, -1, -1, -1, -1, -1,
531 -1, -1, -1, -1, -1, -1, -1, -1, -1,
532 -1, -1, -1, -1, -1, -1, -1, -1,
536 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
538 DWORD hash
= dt_hash(str
, len
);
539 XDR_DT dt
= DT_INVALID
;
541 if (hash
<= DT_MAX_HASH_VALUE
)
542 dt
= DT_lookup_table
[hash
];
544 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
550 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
552 DWORD hash
= dt_hash_bstr(bstr
, len
);
553 XDR_DT dt
= DT_INVALID
;
555 if (hash
<= DT_MAX_HASH_VALUE
)
556 dt
= DT_lookup_table
[hash
];
558 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
564 xmlChar
const* dt_to_str(XDR_DT dt
)
566 if (dt
== DT_INVALID
)
569 return DT_string_table
[dt
];
572 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
574 if (dt
== DT_INVALID
)
577 return DT_wstring_table
[dt
];
580 const char* debugstr_dt(XDR_DT dt
)
582 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
585 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
592 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), debugstr_a((char const*)content
));
594 if (!datatypes_schema
)
596 xmlSchemaParserCtxtPtr spctx
;
597 assert(datatypes_src
!= NULL
);
598 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
599 datatypes_schema
= Schema_parse(spctx
);
600 xmlSchemaFreeParserCtxt(spctx
);
636 if (!datatypes_schema
)
638 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
639 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
641 /* Hopefully they don't need much in the way of XDR datatypes support... */
645 if (content
&& xmlStrlen(content
))
647 tmp_doc
= xmlNewDoc(NULL
);
648 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
649 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
651 xmlDocSetRootElement(tmp_doc
, node
);
653 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
657 { /* probably the node is being created manually and has no content yet */
662 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
667 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
669 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
672 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
674 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
675 xmlHashLookup(This
->cache
, nsURI
);
678 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
680 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
681 return (!entry
)? NULL
: entry
->schema
;
684 static xmlExternalEntityLoader _external_entity_loader
;
686 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
687 xmlParserCtxtPtr ctxt
)
689 xmlParserInputPtr input
;
691 TRACE("(%s %s %p)\n", debugstr_a(URL
), debugstr_a(ID
), ctxt
);
693 assert(MSXML_hInstance
!= NULL
);
694 assert(datatypes_rsrc
!= NULL
);
695 assert(datatypes_handle
!= NULL
);
696 assert(datatypes_src
!= NULL
);
698 /* TODO: if the desired schema is in the cache, load it from there */
699 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
701 TRACE("loading built-in schema for %s\n", URL
);
702 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
706 input
= _external_entity_loader(URL
, ID
, ctxt
);
712 void schemasInit(void)
716 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
718 FIXME("failed to find resource for %s\n", DT_nsURI
);
722 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
724 FIXME("failed to load resource for %s\n", DT_nsURI
);
727 buf
= LockResource(datatypes_handle
);
728 len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
) - 1;
730 /* Resource is loaded as raw data,
731 * need a null-terminated string */
732 while (buf
[len
] != '>')
734 datatypes_src
= BAD_CAST buf
;
735 datatypes_len
= len
+ 1;
737 if (xmlGetExternalEntityLoader() != external_entity_loader
)
739 _external_entity_loader
= xmlGetExternalEntityLoader();
740 xmlSetExternalEntityLoader(external_entity_loader
);
744 void schemasCleanup(void)
746 xmlSchemaFree(datatypes_schema
);
747 xmlSetExternalEntityLoader(_external_entity_loader
);
750 static LONG
cache_entry_add_ref(cache_entry
* entry
)
752 LONG ref
= InterlockedIncrement(&entry
->ref
);
753 TRACE("(%p)->(%d)\n", entry
, ref
);
757 static LONG
cache_entry_release(cache_entry
* entry
)
759 LONG ref
= InterlockedDecrement(&entry
->ref
);
760 TRACE("(%p)->(%d)\n", entry
, ref
);
764 if (entry
->type
== SCHEMA_TYPE_XSD
)
766 xmldoc_release(entry
->doc
);
767 entry
->schema
->doc
= NULL
;
768 xmlSchemaFree(entry
->schema
);
771 else /* SCHEMA_TYPE_XDR */
773 xmldoc_release(entry
->doc
);
774 xmldoc_release(entry
->schema
->doc
);
775 entry
->schema
->doc
= NULL
;
776 xmlSchemaFree(entry
->schema
);
783 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
785 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
788 static inline SCHEMA_TYPE
schema_type_from_xmlDocPtr(xmlDocPtr schema
)
790 xmlNodePtr root
= NULL
;
792 root
= xmlDocGetRootElement(schema
);
793 if (root
&& root
->ns
)
796 if (xmlStrEqual(root
->name
, XDR_schema
) &&
797 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
799 return SCHEMA_TYPE_XDR
;
801 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
802 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
804 return SCHEMA_TYPE_XSD
;
807 return SCHEMA_TYPE_INVALID
;
810 static BOOL
link_datatypes(xmlDocPtr schema
)
812 xmlNodePtr root
, next
, child
;
815 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader
);
816 root
= xmlDocGetRootElement(schema
);
820 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
822 if (xmlStrEqual(ns
->href
, DT_nsURI
))
829 next
= xmlFirstElementChild(root
);
830 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
831 if (next
) child
= xmlAddPrevSibling(next
, child
);
832 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
833 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
838 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
840 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
841 xmlSchemaParserCtxtPtr spctx
;
842 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
844 link_datatypes(new_doc
);
846 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
847 * do we need to do something special here? */
848 entry
->type
= SCHEMA_TYPE_XSD
;
850 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
852 if ((entry
->schema
= Schema_parse(spctx
)))
854 xmldoc_init(entry
->schema
->doc
, v
);
855 entry
->doc
= entry
->schema
->doc
;
856 xmldoc_add_ref(entry
->doc
);
860 FIXME("failed to parse doc\n");
865 xmlSchemaFreeParserCtxt(spctx
);
869 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
871 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
872 xmlSchemaParserCtxtPtr spctx
;
873 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
875 link_datatypes(xsd_doc
);
877 entry
->type
= SCHEMA_TYPE_XDR
;
879 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
881 if ((entry
->schema
= Schema_parse(spctx
)))
883 entry
->doc
= new_doc
;
884 xmldoc_init(entry
->schema
->doc
, version
);
885 xmldoc_init(entry
->doc
, version
);
886 xmldoc_add_ref(entry
->doc
);
887 xmldoc_add_ref(entry
->schema
->doc
);
891 FIXME("failed to parse doc\n");
897 xmlSchemaFreeParserCtxt(spctx
);
902 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
905 IXMLDOMDocument3
* domdoc
= NULL
;
906 xmlDocPtr doc
= NULL
;
907 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
908 VARIANT_BOOL b
= VARIANT_FALSE
;
909 SCHEMA_TYPE type
= SCHEMA_TYPE_INVALID
;
913 FIXME("failed to create domdoc\n");
916 assert(domdoc
!= NULL
);
917 assert(V_VT(&url
) == VT_BSTR
);
919 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
922 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
923 if (b
!= VARIANT_TRUE
)
925 FIXME("Failed to load doc at %s\n", debugstr_w(V_BSTR(&url
)));
926 IXMLDOMDocument3_Release(domdoc
);
930 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
931 type
= schema_type_from_xmlDocPtr(doc
);
935 case SCHEMA_TYPE_XSD
:
936 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
938 case SCHEMA_TYPE_XDR
:
939 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
941 case SCHEMA_TYPE_INVALID
:
943 FIXME("invalid schema\n");
946 IXMLDOMDocument3_Release(domdoc
);
951 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
952 REFIID riid
, void** ppvObject
)
954 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
956 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
958 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
959 IsEqualIID(riid
, &IID_IDispatch
) ||
960 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
961 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
965 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
967 return *ppvObject
? S_OK
: E_NOINTERFACE
;
971 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
973 return E_NOINTERFACE
;
976 IXMLDOMSchemaCollection2_AddRef(iface
);
981 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
983 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
984 LONG ref
= InterlockedIncrement(&This
->ref
);
985 TRACE("(%p)->(%d)\n", This
, ref
);
989 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
991 cache_entry_release((cache_entry
*)data
);
994 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
996 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
997 LONG ref
= InterlockedDecrement(&This
->ref
);
998 TRACE("(%p)->(%d)\n", This
, ref
);
1002 xmlHashFree(This
->cache
, cache_free
);
1003 release_dispex(&This
->dispex
);
1010 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1013 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1014 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1017 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1018 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1020 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1021 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1022 iTInfo
, lcid
, ppTInfo
);
1025 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1026 REFIID riid
, LPOLESTR
* rgszNames
,
1027 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1029 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1030 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1031 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1034 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1035 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1036 WORD wFlags
, DISPPARAMS
* pDispParams
,
1037 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1040 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1041 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1042 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1045 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1047 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1048 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1049 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1055 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1061 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1065 cache_entry_add_ref(entry
);
1073 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1074 xmlHashAddEntry(This
->cache
, name
, entry
);
1080 xmlDocPtr doc
= NULL
;
1083 IXMLDOMNode
* domnode
= NULL
;
1084 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1087 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1091 IXMLDOMNode_Release(domnode
);
1093 return E_INVALIDARG
;
1095 type
= schema_type_from_xmlDocPtr(doc
);
1097 if (type
== SCHEMA_TYPE_XSD
)
1099 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1101 else if (type
== SCHEMA_TYPE_XDR
)
1103 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1107 WARN("invalid schema!\n");
1111 IXMLDOMNode_Release(domnode
);
1115 cache_entry_add_ref(entry
);
1123 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1124 xmlHashAddEntry(This
->cache
, name
, entry
);
1131 return E_INVALIDARG
;
1138 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1141 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1145 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1147 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1152 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1153 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1156 /* TODO: this should be read-only */
1158 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1164 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1166 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1167 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1168 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1170 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1172 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1177 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1179 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1180 TRACE("(%p)->(%p)\n", This
, length
);
1184 *length
= xmlHashSize(This
->cache
);
1188 static void cache_index(void* data
/* ignored */, void* index
, xmlChar
* name
)
1190 cache_index_data
* index_data
= (cache_index_data
*)index
;
1192 if (index_data
->index
-- == 0)
1193 *index_data
->out
= bstr_from_xmlChar(name
);
1196 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1197 LONG index
, BSTR
* len
)
1199 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1200 cache_index_data data
= {index
,len
};
1201 TRACE("(%p)->(%i %p)\n", This
, index
, len
);
1207 if (index
>= xmlHashSize(This
->cache
))
1210 xmlHashScan(This
->cache
, cache_index
, &data
);
1214 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1216 schema_cache
* This
= (schema_cache
*) dest
;
1217 cache_entry
* entry
= (cache_entry
*) data
;
1219 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1221 cache_entry_add_ref(entry
);
1222 xmlHashAddEntry(This
->cache
, name
, entry
);
1226 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1227 IXMLDOMSchemaCollection
* otherCollection
)
1229 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1230 schema_cache
* That
= impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2
*)otherCollection
);
1231 TRACE("(%p)->(%p)\n", This
, That
);
1233 if (!otherCollection
)
1236 /* TODO: detect errors while copying & return E_FAIL */
1237 xmlHashScan(That
->cache
, cache_copy
, This
);
1242 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
1245 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1246 FIXME("(%p)->(%p): stub\n", This
, ppUnk
);
1252 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1254 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1255 FIXME("(%p): stub\n", This
);
1259 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1262 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1263 FIXME("(%p)->(%d): stub\n", This
, value
);
1265 This
->validateOnLoad
= value
;
1266 /* it's ok to disable it, cause we don't validate on load anyway */
1267 if (value
== VARIANT_FALSE
) return S_OK
;
1272 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1273 VARIANT_BOOL
* value
)
1275 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1276 TRACE("(%p)->(%p)\n", This
, value
);
1278 if (!value
) return E_POINTER
;
1279 *value
= This
->validateOnLoad
;
1284 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1285 BSTR namespaceURI
, ISchema
** schema
)
1287 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1288 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1294 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1295 IXMLDOMNode
* node
, ISchemaItem
** item
)
1297 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1298 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1304 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1306 schema_cache_QueryInterface
,
1307 schema_cache_AddRef
,
1308 schema_cache_Release
,
1309 schema_cache_GetTypeInfoCount
,
1310 schema_cache_GetTypeInfo
,
1311 schema_cache_GetIDsOfNames
,
1312 schema_cache_Invoke
,
1315 schema_cache_remove
,
1316 schema_cache_get_length
,
1317 schema_cache_get_namespaceURI
,
1318 schema_cache_addCollection
,
1319 schema_cache_get__newEnum
,
1320 schema_cache_validate
,
1321 schema_cache_put_validateOnLoad
,
1322 schema_cache_get_validateOnLoad
,
1323 schema_cache_getSchema
,
1324 schema_cache_getDeclaration
1327 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1329 xmlSchemaElementPtr decl
= NULL
;
1330 xmlChar
const* nsURI
= get_node_nsURI(node
);
1332 TRACE("(%p, %p)\n", schema
, node
);
1334 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1335 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1337 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1339 FIXME("declaration not found in main schema - need to check schema imports!\n");
1340 /*xmlSchemaImportPtr import;
1342 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1344 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1347 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1353 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1355 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1356 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1357 decl
= decl
->refDecl
;
1358 return (decl
!= NULL
)? decl
->node
: NULL
;
1361 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1363 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1364 xmlSchemaPtr schema
;
1366 TRACE("(%p, %p)\n", This
, tree
);
1371 if (tree
->type
== XML_DOCUMENT_NODE
)
1372 tree
= xmlDocGetRootElement(tree
->doc
);
1374 schema
= get_node_schema(This
, tree
);
1375 /* TODO: if the ns is not in the cache, and it's a URL,
1376 * do we try to load from that? */
1378 return Schema_validate_tree(schema
, tree
);
1380 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1385 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1387 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1388 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1389 XDR_DT dt
= DT_INVALID
;
1391 TRACE("(%p, %p)\n", This
, node
);
1393 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1395 dt
= str_to_dt(node
->name
, -1);
1400 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1402 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1405 dt
= str_to_dt(str
, -1);
1413 static const tid_t schemacache_iface_tids
[] = {
1414 IXMLDOMSchemaCollection2_tid
,
1418 static dispex_static_data_t schemacache_dispex
= {
1420 IXMLDOMSchemaCollection2_tid
,
1422 schemacache_iface_tids
1425 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1427 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1429 return E_OUTOFMEMORY
;
1431 TRACE("(%d %p %p)\n", version
, outer
, obj
);
1433 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1434 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1436 This
->version
= version
;
1437 This
->validateOnLoad
= VARIANT_TRUE
;
1438 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1440 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1446 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1448 MESSAGE("This program tried to use a SchemaCache object, but\n"
1449 "libxml2 support was not present at compile time.\n");