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
{
90 typedef struct _schema_cache
92 const struct IXMLDOMSchemaCollection2Vtbl
* lpVtbl
;
93 MSXML_VERSION version
;
94 xmlHashTablePtr cache
;
98 typedef struct _cache_entry
106 typedef struct _cache_index_data
112 /* datatypes lookup stuff
113 * generated with help from gperf */
114 #define DT_MIN_STR_LEN 2
115 #define DT_MAX_STR_LEN 11
116 #define DT_MIN_HASH_VALUE 2
117 #define DT_MAX_HASH_VALUE 115
119 static const xmlChar DT_bin_base64
[] = "bin.base64";
120 static const xmlChar DT_bin_hex
[] = "bin.hex";
121 static const xmlChar DT_boolean
[] = "boolean";
122 static const xmlChar DT_char
[] = "char";
123 static const xmlChar DT_date
[] = "date";
124 static const xmlChar DT_date_tz
[] = "date.tz";
125 static const xmlChar DT_dateTime
[] = "dateTime";
126 static const xmlChar DT_dateTime_tz
[] = "dateTime.tz";
127 static const xmlChar DT_entity
[] = "entity";
128 static const xmlChar DT_entities
[] = "entities";
129 static const xmlChar DT_enumeration
[] = "enumeration";
130 static const xmlChar DT_fixed_14_4
[] = "fixed.14.4";
131 static const xmlChar DT_float
[] = "float";
132 static const xmlChar DT_i1
[] = "i1";
133 static const xmlChar DT_i2
[] = "i2";
134 static const xmlChar DT_i4
[] = "i4";
135 static const xmlChar DT_i8
[] = "i8";
136 static const xmlChar DT_id
[] = "id";
137 static const xmlChar DT_idref
[] = "idref";
138 static const xmlChar DT_idrefs
[] = "idrefs";
139 static const xmlChar DT_int
[] = "int";
140 static const xmlChar DT_nmtoken
[] = "nmtoken";
141 static const xmlChar DT_nmtokens
[] = "nmtokens";
142 static const xmlChar DT_notation
[] = "notation";
143 static const xmlChar DT_number
[] = "number";
144 static const xmlChar DT_r4
[] = "r4";
145 static const xmlChar DT_r8
[] = "r8";
146 static const xmlChar DT_string
[] = "string";
147 static const xmlChar DT_time
[] = "time";
148 static const xmlChar DT_time_tz
[] = "time.tz";
149 static const xmlChar DT_ui1
[] = "ui1";
150 static const xmlChar DT_ui2
[] = "ui2";
151 static const xmlChar DT_ui4
[] = "ui4";
152 static const xmlChar DT_ui8
[] = "ui8";
153 static const xmlChar DT_uri
[] = "uri";
154 static const xmlChar DT_uuid
[] = "uuid";
156 static const OLECHAR wDT_bin_base64
[] = {'b','i','n','.','b','a','s','e','6','4',0};
157 static const OLECHAR wDT_bin_hex
[] = {'b','i','n','.','h','e','x',0};
158 static const OLECHAR wDT_boolean
[] = {'b','o','o','l','e','a','n',0};
159 static const OLECHAR wDT_char
[] = {'c','h','a','r',0};
160 static const OLECHAR wDT_date
[] = {'d','a','t','e',0};
161 static const OLECHAR wDT_date_tz
[] = {'d','a','t','e','.','t','z',0};
162 static const OLECHAR wDT_dateTime
[] = {'d','a','t','e','T','i','m','e',0};
163 static const OLECHAR wDT_dateTime_tz
[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
164 static const OLECHAR wDT_entity
[] = {'e','n','t','i','t','y',0};
165 static const OLECHAR wDT_entities
[] = {'e','n','t','i','t','i','e','s',0};
166 static const OLECHAR wDT_enumeration
[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
167 static const OLECHAR wDT_fixed_14_4
[] = {'f','i','x','e','d','.','1','4','.','4',0};
168 static const OLECHAR wDT_float
[] = {'f','l','o','a','t',0};
169 static const OLECHAR wDT_i1
[] = {'i','1',0};
170 static const OLECHAR wDT_i2
[] = {'i','2',0};
171 static const OLECHAR wDT_i4
[] = {'i','4',0};
172 static const OLECHAR wDT_i8
[] = {'i','8',0};
173 static const OLECHAR wDT_id
[] = {'i','d',0};
174 static const OLECHAR wDT_idref
[] = {'i','d','r','e','f',0};
175 static const OLECHAR wDT_idrefs
[] = {'i','d','r','e','f','s',0};
176 static const OLECHAR wDT_int
[] = {'i','n','t',0};
177 static const OLECHAR wDT_nmtoken
[] = {'n','m','t','o','k','e','n',0};
178 static const OLECHAR wDT_nmtokens
[] = {'n','m','t','o','k','e','n','s',0};
179 static const OLECHAR wDT_notation
[] = {'n','o','t','a','t','i','o','n',0};
180 static const OLECHAR wDT_number
[] = {'n','u','m','b','e','r',0};
181 static const OLECHAR wDT_r4
[] = {'r','4',0};
182 static const OLECHAR wDT_r8
[] = {'r','8',0};
183 static const OLECHAR wDT_string
[] = {'s','t','r','i','n','g',0};
184 static const OLECHAR wDT_time
[] = {'t','i','m','e',0};
185 static const OLECHAR wDT_time_tz
[] = {'t','i','m','e','.','t','z',0};
186 static const OLECHAR wDT_ui1
[] = {'u','i','1',0};
187 static const OLECHAR wDT_ui2
[] = {'u','i','2',0};
188 static const OLECHAR wDT_ui4
[] = {'u','i','4',0};
189 static const OLECHAR wDT_ui8
[] = {'u','i','8',0};
190 static const OLECHAR wDT_uri
[] = {'u','r','i',0};
191 static const OLECHAR wDT_uuid
[] = {'u','u','i','d',0};
193 static const BYTE hash_assoc_values
[] =
195 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
196 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
197 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
198 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
199 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
200 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
201 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
202 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
203 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
204 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
205 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
206 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
207 0, 0, 10, 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, 116, 116, 116, 116,
210 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
211 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
212 116, 116, 116, 116, 116, 116, 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
223 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
227 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
231 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
235 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
239 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
240 static void parser_serror(void* ctx
, xmlErrorPtr err
)
242 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
246 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
248 TRACE("(%p)\n", spctx
);
250 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
251 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
252 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
255 return xmlSchemaParse(spctx
);
258 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
262 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
266 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
270 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
274 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
275 static void validate_serror(void* ctx
, xmlErrorPtr err
)
277 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
281 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
283 xmlSchemaValidCtxtPtr svctx
;
286 TRACE("(%p, %p)\n", schema
, tree
);
287 /* TODO: if validateOnLoad property is false,
288 * we probably need to validate the schema here. */
289 svctx
= xmlSchemaNewValidCtxt(schema
);
290 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
291 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
292 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
295 if (tree
->type
== XML_DOCUMENT_NODE
)
296 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
298 err
= xmlSchemaValidateOneElement(svctx
, tree
);
300 xmlSchemaFreeValidCtxt(svctx
);
301 return err
? S_FALSE
: S_OK
;
304 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
306 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
311 hval
+= hash_assoc_values
[str
[10]];
314 hval
+= hash_assoc_values
[str
[9]];
317 hval
+= hash_assoc_values
[str
[8]];
320 hval
+= hash_assoc_values
[str
[7]];
323 hval
+= hash_assoc_values
[str
[6]];
326 hval
+= hash_assoc_values
[str
[5]];
329 hval
+= hash_assoc_values
[str
[4]];
332 hval
+= hash_assoc_values
[str
[3]];
335 hval
+= hash_assoc_values
[str
[2]];
338 hval
+= hash_assoc_values
[str
[1]];
341 hval
+= hash_assoc_values
[str
[0]];
347 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
349 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
354 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
357 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
360 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
363 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
366 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
369 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
372 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
375 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
378 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
381 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
384 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
390 static const xmlChar
*const DT_string_table
[DT__N_TYPES
] =
430 static const WCHAR
*const DT_wstring_table
[DT__N_TYPES
] =
470 static const XDR_DT DT_lookup_table
[] =
523 -1, -1, -1, -1, -1, -1, -1, -1, -1,
524 -1, -1, -1, -1, -1, -1, -1, -1, -1,
525 -1, -1, -1, -1, -1, -1, -1, -1, -1,
526 -1, -1, -1, -1, -1, -1, -1, -1, -1,
527 -1, -1, -1, -1, -1, -1, -1, -1, -1,
528 -1, -1, -1, -1, -1, -1, -1, -1,
532 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
534 DWORD hash
= dt_hash(str
, len
);
535 XDR_DT dt
= DT_INVALID
;
537 if (hash
<= DT_MAX_HASH_VALUE
)
538 dt
= DT_lookup_table
[hash
];
540 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
546 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
548 DWORD hash
= dt_hash_bstr(bstr
, len
);
549 XDR_DT dt
= DT_INVALID
;
551 if (hash
<= DT_MAX_HASH_VALUE
)
552 dt
= DT_lookup_table
[hash
];
554 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
560 xmlChar
const* dt_to_str(XDR_DT dt
)
562 if (dt
== DT_INVALID
)
565 return DT_string_table
[dt
];
568 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
570 if (dt
== DT_INVALID
)
573 return DT_wstring_table
[dt
];
576 const char* debugstr_dt(XDR_DT dt
)
578 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
581 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
588 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), wine_dbgstr_a((char const*)content
));
590 if (!datatypes_schema
)
592 xmlSchemaParserCtxtPtr spctx
;
593 assert(datatypes_src
!= NULL
);
594 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
595 datatypes_schema
= Schema_parse(spctx
);
596 xmlSchemaFreeParserCtxt(spctx
);
632 if (!datatypes_schema
)
634 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
635 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
637 /* Hopefully they don't need much in the way of XDR datatypes support... */
641 if (content
&& xmlStrlen(content
))
643 tmp_doc
= xmlNewDoc(NULL
);
644 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
645 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
647 xmlDocSetRootElement(tmp_doc
, node
);
649 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
653 { /* probably the node is being created manually and has no content yet */
658 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
663 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
665 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
668 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
670 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
671 xmlHashLookup(This
->cache
, nsURI
);
674 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
676 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
677 return (!entry
)? NULL
: entry
->schema
;
680 static xmlExternalEntityLoader _external_entity_loader
;
682 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
683 xmlParserCtxtPtr ctxt
)
685 xmlParserInputPtr input
;
687 TRACE("(%s %s %p)\n", wine_dbgstr_a(URL
), wine_dbgstr_a(ID
), ctxt
);
689 assert(MSXML_hInstance
!= NULL
);
690 assert(datatypes_rsrc
!= NULL
);
691 assert(datatypes_handle
!= NULL
);
692 assert(datatypes_src
!= NULL
);
694 /* TODO: if the desired schema is in the cache, load it from there */
695 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
697 TRACE("loading built-in schema for %s\n", URL
);
698 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
702 input
= _external_entity_loader(URL
, ID
, ctxt
);
708 void schemasInit(void)
712 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
714 FIXME("failed to find resource for %s\n", DT_nsURI
);
718 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
720 FIXME("failed to load resource for %s\n", DT_nsURI
);
723 buf
= LockResource(datatypes_handle
);
724 len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
) - 1;
726 /* Resource is loaded as raw data,
727 * need a null-terminated string */
728 while (buf
[len
] != '>')
730 datatypes_src
= BAD_CAST buf
;
731 datatypes_len
= len
+ 1;
733 if (xmlGetExternalEntityLoader() != external_entity_loader
)
735 _external_entity_loader
= xmlGetExternalEntityLoader();
736 xmlSetExternalEntityLoader(external_entity_loader
);
740 void schemasCleanup(void)
742 xmlSchemaFree(datatypes_schema
);
743 xmlSetExternalEntityLoader(_external_entity_loader
);
746 static LONG
cache_entry_add_ref(cache_entry
* entry
)
748 LONG ref
= InterlockedIncrement(&entry
->ref
);
749 TRACE("(%p)->(%d)\n", entry
, ref
);
753 static LONG
cache_entry_release(cache_entry
* entry
)
755 LONG ref
= InterlockedDecrement(&entry
->ref
);
756 TRACE("(%p)->(%d)\n", entry
, ref
);
760 if (entry
->type
== SCHEMA_TYPE_XSD
)
762 xmldoc_release(entry
->doc
);
763 entry
->schema
->doc
= NULL
;
764 xmlSchemaFree(entry
->schema
);
767 else /* SCHEMA_TYPE_XDR */
769 xmldoc_release(entry
->doc
);
770 xmldoc_release(entry
->schema
->doc
);
771 entry
->schema
->doc
= NULL
;
772 xmlSchemaFree(entry
->schema
);
779 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
781 return (schema_cache
*)((char*)iface
- FIELD_OFFSET(schema_cache
, lpVtbl
));
784 static inline SCHEMA_TYPE
schema_type_from_xmlDocPtr(xmlDocPtr schema
)
786 xmlNodePtr root
= NULL
;
788 root
= xmlDocGetRootElement(schema
);
789 if (root
&& root
->ns
)
792 if (xmlStrEqual(root
->name
, XDR_schema
) &&
793 xmlStrEqual(root
->ns
->href
, XDR_nsURI
))
795 return SCHEMA_TYPE_XDR
;
797 else if (xmlStrEqual(root
->name
, XSD_schema
) &&
798 xmlStrEqual(root
->ns
->href
, XSD_nsURI
))
800 return SCHEMA_TYPE_XSD
;
803 return SCHEMA_TYPE_INVALID
;
806 static BOOL
link_datatypes(xmlDocPtr schema
)
808 xmlNodePtr root
, next
, child
;
811 assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader
);
812 root
= xmlDocGetRootElement(schema
);
816 for (ns
= root
->nsDef
; ns
!= NULL
; ns
= ns
->next
)
818 if (xmlStrEqual(ns
->href
, DT_nsURI
))
825 next
= xmlFirstElementChild(root
);
826 child
= xmlNewChild(root
, NULL
, BAD_CAST
"import", NULL
);
827 if (next
) child
= xmlAddPrevSibling(next
, child
);
828 xmlSetProp(child
, BAD_CAST
"namespace", DT_nsURI
);
829 xmlSetProp(child
, BAD_CAST
"schemaLocation", DT_nsURI
);
834 static cache_entry
* cache_entry_from_xsd_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION v
)
836 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
837 xmlSchemaParserCtxtPtr spctx
;
838 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1);
840 link_datatypes(new_doc
);
842 /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
843 * do we need to do something special here? */
844 entry
->type
= SCHEMA_TYPE_XSD
;
846 spctx
= xmlSchemaNewDocParserCtxt(new_doc
);
848 if ((entry
->schema
= Schema_parse(spctx
)))
850 xmldoc_init(entry
->schema
->doc
, v
);
851 entry
->doc
= entry
->schema
->doc
;
852 xmldoc_add_ref(entry
->doc
);
856 FIXME("failed to parse doc\n");
861 xmlSchemaFreeParserCtxt(spctx
);
865 static cache_entry
* cache_entry_from_xdr_doc(xmlDocPtr doc
, xmlChar
const* nsURI
, MSXML_VERSION version
)
867 cache_entry
* entry
= heap_alloc(sizeof(cache_entry
));
868 xmlSchemaParserCtxtPtr spctx
;
869 xmlDocPtr new_doc
= xmlCopyDoc(doc
, 1), xsd_doc
= XDR_to_XSD_doc(doc
, nsURI
);
871 link_datatypes(xsd_doc
);
873 entry
->type
= SCHEMA_TYPE_XDR
;
875 spctx
= xmlSchemaNewDocParserCtxt(xsd_doc
);
877 if ((entry
->schema
= Schema_parse(spctx
)))
879 entry
->doc
= new_doc
;
880 xmldoc_init(entry
->schema
->doc
, version
);
881 xmldoc_init(entry
->doc
, version
);
882 xmldoc_add_ref(entry
->doc
);
883 xmldoc_add_ref(entry
->schema
->doc
);
887 FIXME("failed to parse doc\n");
893 xmlSchemaFreeParserCtxt(spctx
);
898 static cache_entry
* cache_entry_from_url(VARIANT url
, xmlChar
const* nsURI
, MSXML_VERSION version
)
901 IXMLDOMDocument3
* domdoc
= NULL
;
902 xmlDocPtr doc
= NULL
;
903 HRESULT hr
= DOMDocument_create(version
, NULL
, (void**)&domdoc
);
904 VARIANT_BOOL b
= VARIANT_FALSE
;
905 SCHEMA_TYPE type
= SCHEMA_TYPE_INVALID
;
909 FIXME("failed to create domdoc\n");
912 assert(domdoc
!= NULL
);
913 assert(V_VT(&url
) == VT_BSTR
);
915 hr
= IXMLDOMDocument3_load(domdoc
, url
, &b
);
918 ERR("IXMLDOMDocument3_load() returned 0x%08x\n", hr
);
919 if (b
!= VARIANT_TRUE
)
921 FIXME("Failed to load doc at %s\n", wine_dbgstr_w(V_BSTR(&url
)));
922 IXMLDOMDocument3_Release(domdoc
);
926 doc
= xmlNodePtr_from_domnode((IXMLDOMNode
*)domdoc
, XML_DOCUMENT_NODE
)->doc
;
927 type
= schema_type_from_xmlDocPtr(doc
);
931 case SCHEMA_TYPE_XSD
:
932 entry
= cache_entry_from_xsd_doc(doc
, nsURI
, version
);
934 case SCHEMA_TYPE_XDR
:
935 entry
= cache_entry_from_xdr_doc(doc
, nsURI
, version
);
937 case SCHEMA_TYPE_INVALID
:
939 FIXME("invalid schema\n");
942 IXMLDOMDocument3_Release(domdoc
);
947 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
948 REFIID riid
, void** ppvObject
)
950 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
952 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
954 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
955 IsEqualIID(riid
, &IID_IDispatch
) ||
956 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
957 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
963 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
965 return E_NOINTERFACE
;
968 IXMLDOMSchemaCollection2_AddRef(iface
);
973 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
975 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
976 LONG ref
= InterlockedIncrement(&This
->ref
);
977 TRACE("(%p)->(%d)\n", This
, ref
);
981 static void cache_free(void* data
, xmlChar
* name
/* ignored */)
983 cache_entry_release((cache_entry
*)data
);
986 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
988 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
989 LONG ref
= InterlockedDecrement(&This
->ref
);
990 TRACE("(%p)->(%d)\n", This
, ref
);
994 xmlHashFree(This
->cache
, cache_free
);
1001 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1004 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1006 TRACE("(%p)->(%p)\n", This
, pctinfo
);
1013 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1014 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1016 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1019 TRACE("(%p)->(%u %u %p)\n", This
, iTInfo
, lcid
, ppTInfo
);
1021 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, ppTInfo
);
1026 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1027 REFIID riid
, LPOLESTR
* rgszNames
,
1028 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1030 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1031 ITypeInfo
* typeinfo
;
1034 TRACE("(%p)->(%s %p %u %u %p)\n", This
, debugstr_guid(riid
), rgszNames
, cNames
,
1037 if(!rgszNames
|| cNames
== 0 || !rgDispId
)
1038 return E_INVALIDARG
;
1040 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, &typeinfo
);
1043 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, rgszNames
, cNames
, rgDispId
);
1044 ITypeInfo_Release(typeinfo
);
1050 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1051 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1052 WORD wFlags
, DISPPARAMS
* pDispParams
,
1053 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1056 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1057 ITypeInfo
* typeinfo
;
1060 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This
, dispIdMember
, debugstr_guid(riid
),
1061 lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1063 hr
= get_typeinfo(IXMLDOMSchemaCollection_tid
, &typeinfo
);
1066 hr
= ITypeInfo_Invoke(typeinfo
, &(This
->lpVtbl
), dispIdMember
, wFlags
, pDispParams
,
1067 pVarResult
, pExcepInfo
, puArgErr
);
1068 ITypeInfo_Release(typeinfo
);
1074 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1076 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1077 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1078 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1084 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1090 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1094 cache_entry_add_ref(entry
);
1102 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1103 xmlHashAddEntry(This
->cache
, name
, entry
);
1109 xmlDocPtr doc
= NULL
;
1112 IXMLDOMNode
* domnode
= NULL
;
1113 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1116 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1120 IXMLDOMNode_Release(domnode
);
1122 return E_INVALIDARG
;
1124 type
= schema_type_from_xmlDocPtr(doc
);
1126 if (type
== SCHEMA_TYPE_XSD
)
1128 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1130 else if (type
== SCHEMA_TYPE_XDR
)
1132 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1136 WARN("invalid schema!\n");
1140 IXMLDOMNode_Release(domnode
);
1144 cache_entry_add_ref(entry
);
1152 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1153 xmlHashAddEntry(This
->cache
, name
, entry
);
1160 return E_INVALIDARG
;
1167 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1170 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1173 TRACE("(%p)->(%s %p)\n", This
, wine_dbgstr_w(uri
), node
);
1178 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1179 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1182 /* TODO: this should be read-only */
1184 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1190 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1192 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1193 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1194 TRACE("(%p)->(%s)\n", This
, wine_dbgstr_w(uri
));
1196 xmlHashRemoveEntry(This
->cache
, name
, cache_free
);
1201 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1203 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1204 TRACE("(%p)->(%p)\n", This
, length
);
1208 *length
= xmlHashSize(This
->cache
);
1212 static void cache_index(void* data
/* ignored */, void* index
, xmlChar
* name
)
1214 cache_index_data
* index_data
= (cache_index_data
*)index
;
1216 if (index_data
->index
-- == 0)
1217 *index_data
->out
= bstr_from_xmlChar(name
);
1220 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1221 LONG index
, BSTR
* len
)
1223 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1224 cache_index_data data
= {index
,len
};
1225 TRACE("(%p)->(%i %p)\n", This
, index
, len
);
1231 if (index
>= xmlHashSize(This
->cache
))
1234 xmlHashScan(This
->cache
, cache_index
, &data
);
1238 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1240 schema_cache
* This
= (schema_cache
*) dest
;
1241 cache_entry
* entry
= (cache_entry
*) data
;
1243 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1245 cache_entry_add_ref(entry
);
1246 xmlHashAddEntry(This
->cache
, name
, entry
);
1250 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1251 IXMLDOMSchemaCollection
* otherCollection
)
1253 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1254 schema_cache
* That
= impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2
*)otherCollection
);
1255 TRACE("(%p)->(%p)\n", This
, That
);
1257 if (!otherCollection
)
1260 /* TODO: detect errors while copying & return E_FAIL */
1261 xmlHashScan(That
->cache
, cache_copy
, This
);
1266 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
,
1269 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1270 FIXME("(%p)->(%p): stub\n", This
, ppUnk
);
1276 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1278 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1279 FIXME("(%p): stub\n", This
);
1283 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1286 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1287 FIXME("(%p)->(%d): stub\n", This
, value
);
1291 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1292 VARIANT_BOOL
* value
)
1294 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1295 FIXME("(%p)->(%p): stub\n", This
, value
);
1299 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1300 BSTR namespaceURI
, ISchema
** schema
)
1302 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1303 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1309 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1310 IXMLDOMNode
* node
, ISchemaItem
** item
)
1312 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1313 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1319 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl
=
1321 schema_cache_QueryInterface
,
1322 schema_cache_AddRef
,
1323 schema_cache_Release
,
1324 schema_cache_GetTypeInfoCount
,
1325 schema_cache_GetTypeInfo
,
1326 schema_cache_GetIDsOfNames
,
1327 schema_cache_Invoke
,
1330 schema_cache_remove
,
1331 schema_cache_get_length
,
1332 schema_cache_get_namespaceURI
,
1333 schema_cache_addCollection
,
1334 schema_cache_get__newEnum
,
1335 schema_cache_validate
,
1336 schema_cache_put_validateOnLoad
,
1337 schema_cache_get_validateOnLoad
,
1338 schema_cache_getSchema
,
1339 schema_cache_getDeclaration
1342 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1344 xmlSchemaElementPtr decl
= NULL
;
1345 xmlChar
const* nsURI
= get_node_nsURI(node
);
1347 TRACE("(%p, %p)\n", schema
, node
);
1349 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1350 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1352 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1354 FIXME("declaration not found in main schema - need to check schema imports!\n");
1355 /*xmlSchemaImportPtr import;
1357 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1359 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1362 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1368 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1370 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1371 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1372 decl
= decl
->refDecl
;
1373 return (decl
!= NULL
)? decl
->node
: NULL
;
1376 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1378 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1379 xmlSchemaPtr schema
;
1381 TRACE("(%p, %p)\n", This
, tree
);
1386 if (tree
->type
== XML_DOCUMENT_NODE
)
1387 tree
= xmlDocGetRootElement(tree
->doc
);
1389 schema
= get_node_schema(This
, tree
);
1390 /* TODO: if the ns is not in the cache, and it's a URL,
1391 * do we try to load from that? */
1393 return Schema_validate_tree(schema
, tree
);
1395 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1400 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1402 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1403 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1404 XDR_DT dt
= DT_INVALID
;
1406 TRACE("(%p, %p)\n", This
, node
);
1408 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1410 dt
= str_to_dt(node
->name
, -1);
1415 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1417 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1420 dt
= str_to_dt(str
, -1);
1428 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* pUnkOuter
, void** ppObj
)
1430 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1432 return E_OUTOFMEMORY
;
1434 This
->lpVtbl
= &schema_cache_vtbl
;
1435 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1437 This
->version
= version
;
1439 *ppObj
= &This
->lpVtbl
;
1445 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* pUnkOuter
, void** ppObj
)
1447 MESSAGE("This program tried to use a SchemaCache object, but\n"
1448 "libxml2 support was not present at compile time.\n");