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
27 # include <libxml/xmlschemas.h>
28 # include <libxml/schemasInternals.h>
29 # include <libxml/parserInternals.h>
30 # include <libxml/xpath.h>
33 /* We use a chained hashtable, which can hold any number of schemas
34 * TODO: grow/shrink hashtable depending on load factor
35 * TODO: implement read-only where appropriate
38 /* This is just the number of buckets, should be prime */
39 #define DEFAULT_HASHTABLE_SIZE 17
43 xmlDocPtr
XDR_to_XSD_doc(xmlDocPtr xdr_doc
, xmlChar
const* nsURI
);
45 static const xmlChar XSD_schema
[] = "schema";
46 static const xmlChar XSD_nsURI
[] = "http://www.w3.org/2001/XMLSchema";
47 static const xmlChar XDR_schema
[] = "Schema";
48 static const xmlChar XDR_nsURI
[] = "urn:schemas-microsoft-com:xml-data";
49 static const xmlChar DT_nsURI
[] = "urn:schemas-microsoft-com:datatypes";
51 static xmlChar
* datatypes_src
;
52 static int datatypes_len
;
53 static HGLOBAL datatypes_handle
;
54 static HRSRC datatypes_rsrc
;
55 static xmlSchemaPtr datatypes_schema
;
57 static const WCHAR emptyW
[] = {0};
65 * CacheType_NS is a special type used for read-only collection build with
66 * IXMLDOMDocument2::namespaces()
69 CacheEntryType_Invalid
,
78 IXMLDOMSchemaCollection2 IXMLDOMSchemaCollection2_iface
;
81 MSXML_VERSION version
;
82 xmlHashTablePtr cache
;
87 VARIANT_BOOL validateOnLoad
;
99 /* datatypes lookup stuff
100 * generated with help from gperf */
101 #define DT_MIN_STR_LEN 2
102 #define DT_MAX_STR_LEN 11
103 #define DT_MIN_HASH_VALUE 2
104 #define DT_MAX_HASH_VALUE 115
106 static const xmlChar DT_bin_base64
[] = "bin.base64";
107 static const xmlChar DT_bin_hex
[] = "bin.hex";
108 static const xmlChar DT_boolean
[] = "boolean";
109 static const xmlChar DT_char
[] = "char";
110 static const xmlChar DT_date
[] = "date";
111 static const xmlChar DT_date_tz
[] = "date.tz";
112 static const xmlChar DT_dateTime
[] = "dateTime";
113 static const xmlChar DT_dateTime_tz
[] = "dateTime.tz";
114 static const xmlChar DT_entity
[] = "entity";
115 static const xmlChar DT_entities
[] = "entities";
116 static const xmlChar DT_enumeration
[] = "enumeration";
117 static const xmlChar DT_fixed_14_4
[] = "fixed.14.4";
118 static const xmlChar DT_float
[] = "float";
119 static const xmlChar DT_i1
[] = "i1";
120 static const xmlChar DT_i2
[] = "i2";
121 static const xmlChar DT_i4
[] = "i4";
122 static const xmlChar DT_i8
[] = "i8";
123 static const xmlChar DT_id
[] = "id";
124 static const xmlChar DT_idref
[] = "idref";
125 static const xmlChar DT_idrefs
[] = "idrefs";
126 static const xmlChar DT_int
[] = "int";
127 static const xmlChar DT_nmtoken
[] = "nmtoken";
128 static const xmlChar DT_nmtokens
[] = "nmtokens";
129 static const xmlChar DT_notation
[] = "notation";
130 static const xmlChar DT_number
[] = "number";
131 static const xmlChar DT_r4
[] = "r4";
132 static const xmlChar DT_r8
[] = "r8";
133 static const xmlChar DT_string
[] = "string";
134 static const xmlChar DT_time
[] = "time";
135 static const xmlChar DT_time_tz
[] = "time.tz";
136 static const xmlChar DT_ui1
[] = "ui1";
137 static const xmlChar DT_ui2
[] = "ui2";
138 static const xmlChar DT_ui4
[] = "ui4";
139 static const xmlChar DT_ui8
[] = "ui8";
140 static const xmlChar DT_uri
[] = "uri";
141 static const xmlChar DT_uuid
[] = "uuid";
143 static const OLECHAR wDT_bin_base64
[] = {'b','i','n','.','b','a','s','e','6','4',0};
144 static const OLECHAR wDT_bin_hex
[] = {'b','i','n','.','h','e','x',0};
145 static const OLECHAR wDT_boolean
[] = {'b','o','o','l','e','a','n',0};
146 static const OLECHAR wDT_char
[] = {'c','h','a','r',0};
147 static const OLECHAR wDT_date
[] = {'d','a','t','e',0};
148 static const OLECHAR wDT_date_tz
[] = {'d','a','t','e','.','t','z',0};
149 static const OLECHAR wDT_dateTime
[] = {'d','a','t','e','T','i','m','e',0};
150 static const OLECHAR wDT_dateTime_tz
[] = {'d','a','t','e','T','i','m','e','.','t','z',0};
151 static const OLECHAR wDT_entity
[] = {'e','n','t','i','t','y',0};
152 static const OLECHAR wDT_entities
[] = {'e','n','t','i','t','i','e','s',0};
153 static const OLECHAR wDT_enumeration
[] = {'e','n','u','m','e','r','a','t','i','o','n',0};
154 static const OLECHAR wDT_fixed_14_4
[] = {'f','i','x','e','d','.','1','4','.','4',0};
155 static const OLECHAR wDT_float
[] = {'f','l','o','a','t',0};
156 static const OLECHAR wDT_i1
[] = {'i','1',0};
157 static const OLECHAR wDT_i2
[] = {'i','2',0};
158 static const OLECHAR wDT_i4
[] = {'i','4',0};
159 static const OLECHAR wDT_i8
[] = {'i','8',0};
160 static const OLECHAR wDT_id
[] = {'i','d',0};
161 static const OLECHAR wDT_idref
[] = {'i','d','r','e','f',0};
162 static const OLECHAR wDT_idrefs
[] = {'i','d','r','e','f','s',0};
163 static const OLECHAR wDT_int
[] = {'i','n','t',0};
164 static const OLECHAR wDT_nmtoken
[] = {'n','m','t','o','k','e','n',0};
165 static const OLECHAR wDT_nmtokens
[] = {'n','m','t','o','k','e','n','s',0};
166 static const OLECHAR wDT_notation
[] = {'n','o','t','a','t','i','o','n',0};
167 static const OLECHAR wDT_number
[] = {'n','u','m','b','e','r',0};
168 static const OLECHAR wDT_r4
[] = {'r','4',0};
169 static const OLECHAR wDT_r8
[] = {'r','8',0};
170 static const OLECHAR wDT_string
[] = {'s','t','r','i','n','g',0};
171 static const OLECHAR wDT_time
[] = {'t','i','m','e',0};
172 static const OLECHAR wDT_time_tz
[] = {'t','i','m','e','.','t','z',0};
173 static const OLECHAR wDT_ui1
[] = {'u','i','1',0};
174 static const OLECHAR wDT_ui2
[] = {'u','i','2',0};
175 static const OLECHAR wDT_ui4
[] = {'u','i','4',0};
176 static const OLECHAR wDT_ui8
[] = {'u','i','8',0};
177 static const OLECHAR wDT_uri
[] = {'u','r','i',0};
178 static const OLECHAR wDT_uuid
[] = {'u','u','i','d',0};
180 static const BYTE hash_assoc_values
[] =
182 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
183 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
184 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
185 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
186 116, 116, 116, 116, 116, 116, 10, 116, 116, 55,
187 45, 116, 5, 116, 0, 116, 0, 116, 116, 116,
188 116, 116, 116, 116, 116, 5, 0, 0, 20, 0,
189 0, 10, 0, 0, 116, 0, 0, 0, 15, 5,
190 116, 116, 10, 0, 0, 0, 116, 116, 0, 0,
191 10, 116, 116, 116, 116, 116, 116, 5, 0, 0,
192 20, 0, 0, 10, 0, 0, 116, 0, 0, 0,
193 15, 5, 116, 116, 10, 0, 0, 0, 116, 116,
194 0, 0, 10, 116, 116, 116, 116, 116, 116, 116,
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, 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, 116, 116, 116, 116,
204 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
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
210 static void LIBXML2_LOG_CALLBACK
parser_error(void* ctx
, char const* msg
, ...)
214 LIBXML2_CALLBACK_ERR(Schema_parse
, msg
, ap
);
218 static void LIBXML2_LOG_CALLBACK
parser_warning(void* ctx
, char const* msg
, ...)
222 LIBXML2_CALLBACK_WARN(Schema_parse
, msg
, ap
);
226 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
227 static void parser_serror(void* ctx
, xmlErrorPtr err
)
229 LIBXML2_CALLBACK_SERROR(Schema_parse
, err
);
233 static inline xmlSchemaPtr
Schema_parse(xmlSchemaParserCtxtPtr spctx
)
235 TRACE("(%p)\n", spctx
);
237 xmlSchemaSetParserErrors(spctx
, parser_error
, parser_warning
, NULL
);
238 #ifdef HAVE_XMLSCHEMASSETPARSERSTRUCTUREDERRORS
239 xmlSchemaSetParserStructuredErrors(spctx
, parser_serror
, NULL
);
242 return xmlSchemaParse(spctx
);
245 static void LIBXML2_LOG_CALLBACK
validate_error(void* ctx
, char const* msg
, ...)
249 LIBXML2_CALLBACK_ERR(Schema_validate_tree
, msg
, ap
);
253 static void LIBXML2_LOG_CALLBACK
validate_warning(void* ctx
, char const* msg
, ...)
257 LIBXML2_CALLBACK_WARN(Schema_validate_tree
, msg
, ap
);
261 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
262 static void validate_serror(void* ctx
, xmlErrorPtr err
)
264 LIBXML2_CALLBACK_SERROR(Schema_validate_tree
, err
);
268 static HRESULT
schema_cache_get_item(IUnknown
*iface
, LONG index
, VARIANT
*item
)
270 V_VT(item
) = VT_BSTR
;
271 return IXMLDOMSchemaCollection2_get_namespaceURI((IXMLDOMSchemaCollection2
*)iface
, index
, &V_BSTR(item
));
274 static const struct enumvariant_funcs schemacache_enumvariant
= {
275 schema_cache_get_item
,
279 static inline HRESULT
Schema_validate_tree(xmlSchemaPtr schema
, xmlNodePtr tree
)
281 xmlSchemaValidCtxtPtr svctx
;
284 TRACE("(%p, %p)\n", schema
, tree
);
285 /* TODO: if validateOnLoad property is false,
286 * we probably need to validate the schema here. */
287 svctx
= xmlSchemaNewValidCtxt(schema
);
288 xmlSchemaSetValidErrors(svctx
, validate_error
, validate_warning
, NULL
);
289 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
290 xmlSchemaSetValidStructuredErrors(svctx
, validate_serror
, NULL
);
293 if (tree
->type
== XML_DOCUMENT_NODE
)
294 err
= xmlSchemaValidateDoc(svctx
, (xmlDocPtr
)tree
);
296 err
= xmlSchemaValidateOneElement(svctx
, tree
);
298 xmlSchemaFreeValidCtxt(svctx
);
299 return err
? S_FALSE
: S_OK
;
302 static DWORD
dt_hash(xmlChar
const* str
, int len
/* calculated if -1 */)
304 DWORD hval
= (len
== -1)? xmlStrlen(str
) : len
;
309 hval
+= hash_assoc_values
[str
[10]];
312 hval
+= hash_assoc_values
[str
[9]];
315 hval
+= hash_assoc_values
[str
[8]];
318 hval
+= hash_assoc_values
[str
[7]];
321 hval
+= hash_assoc_values
[str
[6]];
324 hval
+= hash_assoc_values
[str
[5]];
327 hval
+= hash_assoc_values
[str
[4]];
330 hval
+= hash_assoc_values
[str
[3]];
333 hval
+= hash_assoc_values
[str
[2]];
336 hval
+= hash_assoc_values
[str
[1]];
339 hval
+= hash_assoc_values
[str
[0]];
345 static DWORD
dt_hash_bstr(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
347 DWORD hval
= (len
== -1)? lstrlenW(bstr
) : len
;
352 hval
+= (bstr
[10] & 0xFF00)? 116 : hash_assoc_values
[bstr
[10]];
355 hval
+= (bstr
[9] & 0xFF00)? 116 : hash_assoc_values
[bstr
[9]];
358 hval
+= (bstr
[8] & 0xFF00)? 116 : hash_assoc_values
[bstr
[8]];
361 hval
+= (bstr
[7] & 0xFF00)? 116 : hash_assoc_values
[bstr
[7]];
364 hval
+= (bstr
[6] & 0xFF00)? 116 : hash_assoc_values
[bstr
[6]];
367 hval
+= (bstr
[5] & 0xFF00)? 116 : hash_assoc_values
[bstr
[5]];
370 hval
+= (bstr
[4] & 0xFF00)? 116 : hash_assoc_values
[bstr
[4]];
373 hval
+= (bstr
[3] & 0xFF00)? 116 : hash_assoc_values
[bstr
[3]];
376 hval
+= (bstr
[2] & 0xFF00)? 116 : hash_assoc_values
[bstr
[2]];
379 hval
+= (bstr
[1] & 0xFF00)? 116 : hash_assoc_values
[bstr
[1]];
382 hval
+= (bstr
[0] & 0xFF00)? 116 : hash_assoc_values
[bstr
[0]];
388 static const xmlChar
*const DT_string_table
[LAST_DT
] =
428 static const WCHAR
*const DT_wstring_table
[LAST_DT
] =
468 static const XDR_DT DT_lookup_table
[] =
521 -1, -1, -1, -1, -1, -1, -1, -1, -1,
522 -1, -1, -1, -1, -1, -1, -1, -1, -1,
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,
530 XDR_DT
str_to_dt(xmlChar
const* str
, int len
/* calculated if -1 */)
532 DWORD hash
= dt_hash(str
, len
);
533 XDR_DT dt
= DT_INVALID
;
535 if (hash
<= DT_MAX_HASH_VALUE
)
536 dt
= DT_lookup_table
[hash
];
538 if (dt
!= DT_INVALID
&& xmlStrcasecmp(str
, DT_string_table
[dt
]) == 0)
544 XDR_DT
bstr_to_dt(OLECHAR
const* bstr
, int len
/* calculated if -1 */)
546 DWORD hash
= dt_hash_bstr(bstr
, len
);
547 XDR_DT dt
= DT_INVALID
;
549 if (hash
<= DT_MAX_HASH_VALUE
)
550 dt
= DT_lookup_table
[hash
];
552 if (dt
!= DT_INVALID
&& lstrcmpiW(bstr
, DT_wstring_table
[dt
]) == 0)
558 xmlChar
const* dt_to_str(XDR_DT dt
)
560 if (dt
== DT_INVALID
)
563 return DT_string_table
[dt
];
566 OLECHAR
const* dt_to_bstr(XDR_DT dt
)
568 if (dt
== DT_INVALID
)
571 return DT_wstring_table
[dt
];
574 const char* debugstr_dt(XDR_DT dt
)
576 return debugstr_a(dt
!= DT_INVALID
? (const char*)DT_string_table
[dt
] : NULL
);
579 HRESULT
dt_validate(XDR_DT dt
, xmlChar
const* content
)
586 TRACE("(dt:%s, %s)\n", debugstr_dt(dt
), debugstr_a((char const*)content
));
588 if (!datatypes_schema
)
590 xmlSchemaParserCtxtPtr spctx
;
591 assert(datatypes_src
!= NULL
);
592 spctx
= xmlSchemaNewMemParserCtxt((char const*)datatypes_src
, datatypes_len
);
593 datatypes_schema
= Schema_parse(spctx
);
594 xmlSchemaFreeParserCtxt(spctx
);
630 if (!datatypes_schema
)
632 ERR("failed to load schema for urn:schemas-microsoft-com:datatypes, "
633 "you're probably using an old version of libxml2: " LIBXML_DOTTED_VERSION
"\n");
635 /* Hopefully they don't need much in the way of XDR datatypes support... */
639 if (content
&& xmlStrlen(content
))
641 tmp_doc
= xmlNewDoc(NULL
);
642 node
= xmlNewChild((xmlNodePtr
)tmp_doc
, NULL
, dt_to_str(dt
), content
);
643 ns
= xmlNewNs(node
, DT_nsURI
, BAD_CAST
"dt");
645 xmlDocSetRootElement(tmp_doc
, node
);
647 hr
= Schema_validate_tree(datatypes_schema
, (xmlNodePtr
)tmp_doc
);
651 { /* probably the node is being created manually and has no content yet */
656 FIXME("need to handle dt:%s\n", debugstr_dt(dt
));
661 static inline xmlChar
const* get_node_nsURI(xmlNodePtr node
)
663 return (node
->ns
!= NULL
)? node
->ns
->href
: NULL
;
666 static inline cache_entry
* get_entry(schema_cache
* This
, xmlChar
const* nsURI
)
668 return (!nsURI
)? xmlHashLookup(This
->cache
, BAD_CAST
"") :
669 xmlHashLookup(This
->cache
, nsURI
);
672 static inline xmlSchemaPtr
get_node_schema(schema_cache
* This
, xmlNodePtr node
)
674 cache_entry
* entry
= get_entry(This
, get_node_nsURI(node
));
675 return (!entry
)? NULL
: entry
->schema
;
678 static xmlExternalEntityLoader _external_entity_loader
;
680 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char *ID
,
681 xmlParserCtxtPtr ctxt
)
683 xmlParserInputPtr input
;
685 TRACE("(%s %s %p)\n", debugstr_a(URL
), debugstr_a(ID
), ctxt
);
687 assert(MSXML_hInstance
!= NULL
);
688 assert(datatypes_rsrc
!= NULL
);
689 assert(datatypes_handle
!= NULL
);
690 assert(datatypes_src
!= NULL
);
692 /* TODO: if the desired schema is in the cache, load it from there */
693 if (lstrcmpA(URL
, "urn:schemas-microsoft-com:datatypes") == 0)
695 TRACE("loading built-in schema for %s\n", URL
);
696 input
= xmlNewStringInputStream(ctxt
, datatypes_src
);
700 input
= _external_entity_loader(URL
, ID
, ctxt
);
706 void schemasInit(void)
709 if (!(datatypes_rsrc
= FindResourceA(MSXML_hInstance
, "DATATYPES", "XML")))
711 FIXME("failed to find resource for %s\n", DT_nsURI
);
715 if (!(datatypes_handle
= LoadResource(MSXML_hInstance
, datatypes_rsrc
)))
717 FIXME("failed to load resource for %s\n", DT_nsURI
);
720 buf
= LockResource(datatypes_handle
);
721 datatypes_len
= SizeofResource(MSXML_hInstance
, datatypes_rsrc
);
723 /* Resource is loaded as raw data,
724 * need a null-terminated string */
725 while (buf
[datatypes_len
- 1] != '>') datatypes_len
--;
726 datatypes_src
= HeapAlloc(GetProcessHeap(), 0, datatypes_len
+ 1);
727 memcpy(datatypes_src
, buf
, datatypes_len
);
728 datatypes_src
[datatypes_len
] = 0;
730 if (xmlGetExternalEntityLoader() != external_entity_loader
)
732 _external_entity_loader
= xmlGetExternalEntityLoader();
733 xmlSetExternalEntityLoader(external_entity_loader
);
737 void schemasCleanup(void)
739 xmlSchemaFree(datatypes_schema
);
740 HeapFree(GetProcessHeap(), 0, datatypes_src
);
741 xmlSetExternalEntityLoader(_external_entity_loader
);
744 static LONG
cache_entry_add_ref(cache_entry
* entry
)
746 LONG ref
= InterlockedIncrement(&entry
->ref
);
747 TRACE("(%p)->(%d)\n", entry
, ref
);
751 static LONG
cache_entry_release(cache_entry
* entry
)
753 LONG ref
= InterlockedDecrement(&entry
->ref
);
754 TRACE("(%p)->(%d)\n", entry
, ref
);
758 if (entry
->type
== CacheEntryType_XSD
)
760 xmldoc_release(entry
->doc
);
761 entry
->schema
->doc
= NULL
;
762 xmlSchemaFree(entry
->schema
);
764 else if (entry
->type
== CacheEntryType_XDR
)
766 xmldoc_release(entry
->doc
);
767 xmldoc_release(entry
->schema
->doc
);
768 entry
->schema
->doc
= NULL
;
769 xmlSchemaFree(entry
->schema
);
777 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
;
779 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2
* iface
)
781 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
784 static inline schema_cache
* impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
* iface
)
786 return CONTAINING_RECORD(iface
, schema_cache
, IXMLDOMSchemaCollection2_iface
);
789 static inline schema_cache
* unsafe_impl_from_IXMLDOMSchemaCollection(IXMLDOMSchemaCollection
*iface
)
791 return iface
->lpVtbl
== (void*)&XMLDOMSchemaCollection2Vtbl
? impl_from_IXMLDOMSchemaCollection(iface
) : NULL
;
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(xmlGetExternalEntityLoader() == 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 /* returns index or -1 if not found */
963 static int cache_free_uri(schema_cache
*cache
, const xmlChar
*uri
)
967 for (i
= 0; i
< cache
->count
; i
++)
968 if (xmlStrEqual(cache
->uris
[i
], uri
))
970 heap_free(cache
->uris
[i
]);
977 static void cache_add_entry(schema_cache
*cache
, const xmlChar
*uri
, cache_entry
*entry
)
981 /* meaning no entry found with this name */
982 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
))
984 if (cache
->count
== cache
->allocated
)
986 cache
->allocated
*= 2;
987 cache
->uris
= heap_realloc(cache
->uris
, cache
->allocated
*sizeof(xmlChar
*));
992 i
= cache_free_uri(cache
, uri
);
994 cache
->uris
[i
] = heap_strdupxmlChar(uri
);
995 xmlHashAddEntry(cache
->cache
, uri
, entry
);
998 static void cache_remove_entry(schema_cache
*cache
, const xmlChar
*uri
)
1000 /* adjust index if entry was really removed */
1001 if (xmlHashRemoveEntry(cache
->cache
, uri
, cache_free
) == 0)
1003 int i
= cache_free_uri(cache
, uri
);
1004 if (i
== -1) return;
1006 if (i
!= --cache
->count
)
1007 memmove(&cache
->uris
[i
], &cache
->uris
[i
+1], (cache
->count
-i
)*sizeof(xmlChar
*));
1011 /* This one adds all namespaces defined in document to a cache, without anything
1012 associated with uri obviously.
1013 Unfortunately namespace:: axis implementation in libxml2 differs from what we need,
1014 it uses additional node type to describe namespace definition attribute while
1015 in msxml it's expected to be a normal attribute - as a workaround document is
1016 queried at libxml2 level here. */
1017 HRESULT
cache_from_doc_ns(IXMLDOMSchemaCollection2
*iface
, xmlnode
*node
)
1019 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1020 static const xmlChar query
[] = "//*/namespace::*";
1021 xmlXPathObjectPtr nodeset
;
1022 xmlXPathContextPtr ctxt
;
1024 This
->read_only
= 1;
1026 ctxt
= xmlXPathNewContext(node
->node
->doc
);
1028 nodeset
= xmlXPathEvalExpression(query
, ctxt
);
1029 xmlXPathFreeContext(ctxt
);
1033 int pos
= 0, len
= xmlXPathNodeSetGetLength(nodeset
->nodesetval
);
1035 if (len
== 0) return S_OK
;
1039 xmlNodePtr node
= xmlXPathNodeSetItem(nodeset
->nodesetval
, pos
);
1040 if (node
->type
== XML_NAMESPACE_DECL
)
1042 static const xmlChar defns
[] = "http://www.w3.org/XML/1998/namespace";
1043 xmlNsPtr ns
= (xmlNsPtr
)node
;
1046 /* filter out default uri */
1047 if (xmlStrEqual(ns
->href
, defns
))
1053 entry
= heap_alloc(sizeof(cache_entry
));
1054 entry
->type
= CacheEntryType_NS
;
1056 entry
->schema
= NULL
;
1059 cache_add_entry(This
, ns
->href
, entry
);
1064 xmlXPathFreeObject(nodeset
);
1070 static HRESULT WINAPI
schema_cache_QueryInterface(IXMLDOMSchemaCollection2
* iface
,
1071 REFIID riid
, void** ppvObject
)
1073 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1075 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1077 if ( IsEqualIID(riid
, &IID_IUnknown
) ||
1078 IsEqualIID(riid
, &IID_IDispatch
) ||
1079 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection
) ||
1080 IsEqualIID(riid
, &IID_IXMLDOMSchemaCollection2
) )
1084 else if (dispex_query_interface(&This
->dispex
, riid
, ppvObject
))
1086 return *ppvObject
? S_OK
: E_NOINTERFACE
;
1090 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1092 return E_NOINTERFACE
;
1095 IXMLDOMSchemaCollection2_AddRef(iface
);
1100 static ULONG WINAPI
schema_cache_AddRef(IXMLDOMSchemaCollection2
* iface
)
1102 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1103 LONG ref
= InterlockedIncrement(&This
->ref
);
1104 TRACE("(%p)->(%d)\n", This
, ref
);
1108 static ULONG WINAPI
schema_cache_Release(IXMLDOMSchemaCollection2
* iface
)
1110 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1111 LONG ref
= InterlockedDecrement(&This
->ref
);
1112 TRACE("(%p)->(%d)\n", This
, ref
);
1118 for (i
= 0; i
< This
->count
; i
++)
1119 heap_free(This
->uris
[i
]);
1120 heap_free(This
->uris
);
1121 xmlHashFree(This
->cache
, cache_free
);
1122 release_dispex(&This
->dispex
);
1129 static HRESULT WINAPI
schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2
* iface
,
1132 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1133 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
1136 static HRESULT WINAPI
schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2
* iface
,
1137 UINT iTInfo
, LCID lcid
, ITypeInfo
** ppTInfo
)
1139 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1140 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
,
1141 iTInfo
, lcid
, ppTInfo
);
1144 static HRESULT WINAPI
schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2
* iface
,
1145 REFIID riid
, LPOLESTR
* rgszNames
,
1146 UINT cNames
, LCID lcid
, DISPID
* rgDispId
)
1148 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1149 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
,
1150 riid
, rgszNames
, cNames
, lcid
, rgDispId
);
1153 static HRESULT WINAPI
schema_cache_Invoke(IXMLDOMSchemaCollection2
* iface
,
1154 DISPID dispIdMember
, REFIID riid
, LCID lcid
,
1155 WORD wFlags
, DISPPARAMS
* pDispParams
,
1156 VARIANT
* pVarResult
, EXCEPINFO
* pExcepInfo
,
1159 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1160 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
,
1161 dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
1164 static HRESULT WINAPI
schema_cache_add(IXMLDOMSchemaCollection2
* iface
, BSTR uri
, VARIANT var
)
1166 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1167 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1168 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(uri
), debugstr_variant(&var
));
1170 if (This
->read_only
) return E_FAIL
;
1176 cache_remove_entry(This
, name
);
1182 cache_entry
* entry
= cache_entry_from_url(var
, name
, This
->version
);
1186 cache_entry_add_ref(entry
);
1194 cache_add_entry(This
, name
, entry
);
1200 xmlDocPtr doc
= NULL
;
1202 CacheEntryType type
;
1203 IXMLDOMNode
* domnode
= NULL
;
1204 IDispatch_QueryInterface(V_DISPATCH(&var
), &IID_IXMLDOMNode
, (void**)&domnode
);
1207 doc
= xmlNodePtr_from_domnode(domnode
, XML_DOCUMENT_NODE
)->doc
;
1211 IXMLDOMNode_Release(domnode
);
1213 return E_INVALIDARG
;
1215 type
= cache_type_from_xmlDocPtr(doc
);
1217 if (type
== CacheEntryType_XSD
)
1219 entry
= cache_entry_from_xsd_doc(doc
, name
, This
->version
);
1221 else if (type
== CacheEntryType_XDR
)
1223 entry
= cache_entry_from_xdr_doc(doc
, name
, This
->version
);
1227 WARN("invalid schema!\n");
1231 IXMLDOMNode_Release(domnode
);
1235 cache_entry_add_ref(entry
);
1243 cache_add_entry(This
, name
, entry
);
1250 return E_INVALIDARG
;
1257 static HRESULT WINAPI
schema_cache_get(IXMLDOMSchemaCollection2
* iface
, BSTR uri
,
1260 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1264 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(uri
), node
);
1266 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1271 name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1272 entry
= (cache_entry
*) xmlHashLookup(This
->cache
, name
);
1275 /* TODO: this should be read-only */
1276 if (entry
&& entry
->doc
)
1277 return get_domdoc_from_xmldoc(entry
->doc
, (IXMLDOMDocument3
**)node
);
1283 static HRESULT WINAPI
schema_cache_remove(IXMLDOMSchemaCollection2
* iface
, BSTR uri
)
1285 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1286 xmlChar
* name
= uri
? xmlchar_from_wchar(uri
) : xmlchar_from_wchar(emptyW
);
1287 TRACE("(%p)->(%s)\n", This
, debugstr_w(uri
));
1289 if (This
->version
== MSXML6
) return E_NOTIMPL
;
1291 cache_remove_entry(This
, name
);
1296 static HRESULT WINAPI
schema_cache_get_length(IXMLDOMSchemaCollection2
* iface
, LONG
* length
)
1298 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1299 TRACE("(%p)->(%p)\n", This
, length
);
1304 *length
= This
->count
;
1308 static HRESULT WINAPI
schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2
* iface
,
1309 LONG index
, BSTR
* uri
)
1311 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1313 TRACE("(%p)->(%i %p)\n", This
, index
, uri
);
1318 if (index
>= This
->count
)
1321 *uri
= bstr_from_xmlChar(This
->uris
[index
]);
1325 static void cache_copy(void* data
, void* dest
, xmlChar
* name
)
1327 schema_cache
* This
= (schema_cache
*) dest
;
1328 cache_entry
* entry
= (cache_entry
*) data
;
1330 if (xmlHashLookup(This
->cache
, name
) == NULL
)
1332 cache_entry_add_ref(entry
);
1333 cache_add_entry(This
, name
, entry
);
1337 static HRESULT WINAPI
schema_cache_addCollection(IXMLDOMSchemaCollection2
* iface
,
1338 IXMLDOMSchemaCollection
* collection
)
1340 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1343 TRACE("(%p)->(%p)\n", This
, collection
);
1348 That
= unsafe_impl_from_IXMLDOMSchemaCollection(collection
);
1351 ERR("external collection implementation\n");
1355 /* TODO: detect errors while copying & return E_FAIL */
1356 xmlHashScan(That
->cache
, cache_copy
, This
);
1361 static HRESULT WINAPI
schema_cache_get__newEnum(IXMLDOMSchemaCollection2
* iface
, IUnknown
** enumv
)
1363 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1364 TRACE("(%p)->(%p)\n", This
, enumv
);
1365 return create_enumvariant((IUnknown
*)iface
, TRUE
, &schemacache_enumvariant
, (IEnumVARIANT
**)enumv
);
1368 static HRESULT WINAPI
schema_cache_validate(IXMLDOMSchemaCollection2
* iface
)
1370 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1371 FIXME("(%p): stub\n", This
);
1375 static HRESULT WINAPI
schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1378 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1379 FIXME("(%p)->(%d): stub\n", This
, value
);
1381 This
->validateOnLoad
= value
;
1382 /* it's ok to disable it, cause we don't validate on load anyway */
1383 if (value
== VARIANT_FALSE
) return S_OK
;
1388 static HRESULT WINAPI
schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2
* iface
,
1389 VARIANT_BOOL
* value
)
1391 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1392 TRACE("(%p)->(%p)\n", This
, value
);
1394 if (!value
) return E_POINTER
;
1395 *value
= This
->validateOnLoad
;
1400 static HRESULT WINAPI
schema_cache_getSchema(IXMLDOMSchemaCollection2
* iface
,
1401 BSTR namespaceURI
, ISchema
** schema
)
1403 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1404 FIXME("(%p)->(%s %p): stub\n", This
, debugstr_w(namespaceURI
), schema
);
1410 static HRESULT WINAPI
schema_cache_getDeclaration(IXMLDOMSchemaCollection2
* iface
,
1411 IXMLDOMNode
* node
, ISchemaItem
** item
)
1413 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1414 FIXME("(%p)->(%p %p): stub\n", This
, node
, item
);
1420 static const struct IXMLDOMSchemaCollection2Vtbl XMLDOMSchemaCollection2Vtbl
=
1422 schema_cache_QueryInterface
,
1423 schema_cache_AddRef
,
1424 schema_cache_Release
,
1425 schema_cache_GetTypeInfoCount
,
1426 schema_cache_GetTypeInfo
,
1427 schema_cache_GetIDsOfNames
,
1428 schema_cache_Invoke
,
1431 schema_cache_remove
,
1432 schema_cache_get_length
,
1433 schema_cache_get_namespaceURI
,
1434 schema_cache_addCollection
,
1435 schema_cache_get__newEnum
,
1436 schema_cache_validate
,
1437 schema_cache_put_validateOnLoad
,
1438 schema_cache_get_validateOnLoad
,
1439 schema_cache_getSchema
,
1440 schema_cache_getDeclaration
1443 static xmlSchemaElementPtr
lookup_schema_elemDecl(xmlSchemaPtr schema
, xmlNodePtr node
)
1445 xmlSchemaElementPtr decl
= NULL
;
1446 xmlChar
const* nsURI
= get_node_nsURI(node
);
1448 TRACE("(%p, %p)\n", schema
, node
);
1450 if (xmlStrEqual(nsURI
, schema
->targetNamespace
))
1451 decl
= xmlHashLookup(schema
->elemDecl
, node
->name
);
1453 if (!decl
&& xmlHashSize(schema
->schemasImports
) > 1)
1455 FIXME("declaration not found in main schema - need to check schema imports!\n");
1456 /*xmlSchemaImportPtr import;
1458 import = xmlHashLookup(schema->schemasImports, XML_SCHEMAS_NO_NAMESPACE);
1460 import = xmlHashLookup(schema->schemasImports, node->ns->href);
1463 decl = xmlHashLookup(import->schema->elemDecl, node->name);*/
1469 static inline xmlNodePtr
lookup_schema_element(xmlSchemaPtr schema
, xmlNodePtr node
)
1471 xmlSchemaElementPtr decl
= lookup_schema_elemDecl(schema
, node
);
1472 while (decl
!= NULL
&& decl
->refDecl
!= NULL
)
1473 decl
= decl
->refDecl
;
1474 return (decl
!= NULL
)? decl
->node
: NULL
;
1477 HRESULT
SchemaCache_validate_tree(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr tree
)
1479 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1480 xmlSchemaPtr schema
;
1482 TRACE("(%p, %p)\n", This
, tree
);
1487 if (tree
->type
== XML_DOCUMENT_NODE
)
1488 tree
= xmlDocGetRootElement(tree
->doc
);
1490 schema
= get_node_schema(This
, tree
);
1491 /* TODO: if the ns is not in the cache, and it's a URL,
1492 * do we try to load from that? */
1494 return Schema_validate_tree(schema
, tree
);
1496 WARN("no schema found for xmlns=%s\n", get_node_nsURI(tree
));
1501 XDR_DT
SchemaCache_get_node_dt(IXMLDOMSchemaCollection2
* iface
, xmlNodePtr node
)
1503 schema_cache
* This
= impl_from_IXMLDOMSchemaCollection2(iface
);
1504 xmlSchemaPtr schema
= get_node_schema(This
, node
);
1505 XDR_DT dt
= DT_INVALID
;
1507 TRACE("(%p, %p)\n", This
, node
);
1509 if (node
->ns
&& xmlStrEqual(node
->ns
->href
, DT_nsURI
))
1511 dt
= str_to_dt(node
->name
, -1);
1516 xmlNodePtr schema_node
= lookup_schema_element(schema
, node
);
1518 str
= xmlGetNsProp(schema_node
, BAD_CAST
"dt", DT_nsURI
);
1521 dt
= str_to_dt(str
, -1);
1529 static const tid_t schemacache_iface_tids
[] = {
1530 IXMLDOMSchemaCollection2_tid
,
1534 static dispex_static_data_t schemacache_dispex
= {
1536 IXMLDOMSchemaCollection2_tid
,
1538 schemacache_iface_tids
1541 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1543 schema_cache
* This
= heap_alloc(sizeof(schema_cache
));
1545 return E_OUTOFMEMORY
;
1547 TRACE("(%d %p %p)\n", version
, outer
, obj
);
1549 This
->IXMLDOMSchemaCollection2_iface
.lpVtbl
= &XMLDOMSchemaCollection2Vtbl
;
1550 This
->cache
= xmlHashCreate(DEFAULT_HASHTABLE_SIZE
);
1551 This
->allocated
= 10;
1553 This
->uris
= heap_alloc(This
->allocated
*sizeof(xmlChar
*));
1555 This
->version
= version
;
1556 This
->validateOnLoad
= VARIANT_TRUE
;
1557 This
->read_only
= 0;
1558 init_dispex(&This
->dispex
, (IUnknown
*)&This
->IXMLDOMSchemaCollection2_iface
, &schemacache_dispex
);
1560 *obj
= &This
->IXMLDOMSchemaCollection2_iface
;
1566 HRESULT
SchemaCache_create(MSXML_VERSION version
, IUnknown
* outer
, void** obj
)
1568 MESSAGE("This program tried to use a SchemaCache object, but\n"
1569 "libxml2 support was not present at compile time.\n");