Fix bug #13716 - use XmlSchemaSettings.XmlResolver to resolve schemas.
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaSet.cs
1 //
2 // XmlSchemaSet.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
6 //
7 // (C)2003 Atsushi Enomoto
8 // (C)2004 Novell Inc.
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Collections.Specialized;
35 using System.ComponentModel;
36 using System.IO;
37 using System.Security.Policy;
38 using System.Xml.Schema;
39 using System.Xml.XPath;
40
41 namespace System.Xml.Schema
42 {
43 #if NET_2_0
44         public class XmlSchemaSet
45 #else
46         internal sealed class XmlSchemaSet
47 #endif
48         {
49                 XmlNameTable nameTable;
50                 XmlResolver xmlResolver = new XmlUrlResolver ();
51
52                 ArrayList schemas;
53                 XmlSchemaObjectTable attributes;
54                 XmlSchemaObjectTable elements;
55                 XmlSchemaObjectTable types;
56
57                 XmlSchemaCompilationSettings settings =
58                         new XmlSchemaCompilationSettings ();
59
60                 bool isCompiled;
61
62                 internal Guid CompilationId { get; private set; }
63
64                 public XmlSchemaSet ()
65                         : this (new NameTable ())
66                 {
67                 }
68
69                 public XmlSchemaSet (XmlNameTable nameTable)
70                 {
71                         if (nameTable == null)
72                                 throw new ArgumentNullException ("nameTable");
73
74                         this.nameTable = nameTable;
75                         schemas = new ArrayList ();
76                         CompilationId = Guid.NewGuid ();
77                 }
78
79                 public event ValidationEventHandler ValidationEventHandler;
80
81                 public int Count {
82                         get { return schemas.Count; }
83                 }
84
85                 public XmlSchemaObjectTable GlobalAttributes {
86                         get {
87                                 if (attributes == null)
88                                         attributes = new XmlSchemaObjectTable ();
89                                 return attributes;
90                         }
91                 }
92
93                 public XmlSchemaObjectTable GlobalElements {
94                         get {
95                                 if (elements == null)
96                                         elements = new XmlSchemaObjectTable ();
97                                 return elements;
98                         }
99                 }
100
101                 public XmlSchemaObjectTable GlobalTypes { 
102                         get {
103                                 if (types == null)
104                                         types = new XmlSchemaObjectTable ();
105                                 return types;
106                         }
107                 }
108
109                 public bool IsCompiled { 
110                         get { return isCompiled; }
111                 }
112
113                 public XmlNameTable NameTable { 
114                         get { return nameTable; }
115                 }
116
117                 public XmlSchemaCompilationSettings CompilationSettings {
118                         get { return settings; }
119                         set { settings = value; }
120                 }
121
122                 public XmlResolver XmlResolver {
123                         set { xmlResolver = value; }
124 #if NET_2_0
125                         internal get { return xmlResolver; }
126 #else
127                         get { return xmlResolver; }
128 #endif
129                 }
130
131                 public XmlSchema Add (string targetNamespace, string schemaUri)
132                 {
133                         var uri = xmlResolver.ResolveUri (null, schemaUri);
134                         using (var stream = (Stream) xmlResolver.GetEntity (uri, null, typeof (Stream)))
135                                 using (var r = XmlReader.Create (stream, new XmlReaderSettings () { XmlResolver = xmlResolver, NameTable = nameTable}))
136                                         return Add (targetNamespace, r);
137                 }
138
139                 public XmlSchema Add (string targetNamespace, XmlReader schemaDocument)
140                 {
141                         XmlSchema schema = XmlSchema.Read (schemaDocument, ValidationEventHandler);
142                         if (schema.TargetNamespace == null)
143                                 schema.TargetNamespace = targetNamespace == String.Empty ? null : targetNamespace; // this weirdness is due to bug #571660.
144                         else if (targetNamespace != null && schema.TargetNamespace != targetNamespace)
145                                 throw new XmlSchemaException ("The actual targetNamespace in the schema does not match the parameter.");
146                         Add (schema);
147                         return schema;
148                 }
149
150                 [MonoTODO]
151                 // FIXME: Check the exact behavior when namespaces are in conflict (but it would be preferable to wait for 2.0 RTM)
152                 public void Add (XmlSchemaSet schemas)
153                 {
154                         ArrayList al = new ArrayList ();
155                         foreach (XmlSchema schema in schemas.schemas) {
156                                 if (!this.schemas.Contains (schema))
157                                         al.Add (schema);
158                         }
159                         foreach (XmlSchema schema in al)
160                                 Add (schema);
161                 }
162
163                 public XmlSchema Add (XmlSchema schema)
164                 {
165                         schemas.Add (schema);
166                         ResetCompile ();
167                         return schema;
168                 }
169
170                 // FIXME: It should be the actual compilation engine.
171                 public void Compile ()
172                 {
173                         ClearGlobalComponents ();
174                         ArrayList al = new ArrayList ();
175                         al.AddRange (schemas);
176
177                         var handledUris = new List<CompiledSchemaMemo> ();
178                         foreach (XmlSchema schema in al)
179                                 if (!schema.IsCompiled)
180                                         schema.CompileSubset (ValidationEventHandler, this, xmlResolver, handledUris);
181
182                         // Process substitutionGroup first, as this process
183                         // involves both substituted and substituting elements
184                         // and hence it needs to be done before actual
185                         // validation (by current design of conformance checker).
186                         foreach (XmlSchema schema in al)
187                                 foreach (XmlSchemaElement elem in schema.Elements.Values)
188                                         elem.FillSubstitutionElementInfo ();
189
190
191                         foreach (XmlSchema schema in al)
192                                 schema.Validate (ValidationEventHandler);
193
194                         foreach (XmlSchema schema in al)
195                                 AddGlobalComponents (schema);
196
197                         isCompiled = true;
198                 }
199
200                 private void ClearGlobalComponents ()
201                 {
202                         GlobalElements.Clear ();
203                         GlobalAttributes.Clear ();
204                         GlobalTypes.Clear ();
205                         global_attribute_groups.Clear ();
206                         global_groups.Clear ();
207                         global_notations.Clear ();
208                         global_ids.Clear ();
209                         global_identity_constraints.Clear ();
210                 }
211                 
212                 XmlSchemaObjectTable global_attribute_groups = new XmlSchemaObjectTable ();
213                 XmlSchemaObjectTable global_groups = new XmlSchemaObjectTable ();
214                 XmlSchemaObjectTable global_notations = new XmlSchemaObjectTable ();
215                 XmlSchemaObjectTable global_identity_constraints = new XmlSchemaObjectTable ();
216                 Hashtable global_ids = new Hashtable ();
217
218                 private void AddGlobalComponents (XmlSchema schema)
219                 {
220                         foreach (XmlSchemaElement el in schema.Elements.Values)
221                                 GlobalElements.Add (el.QualifiedName, el);
222                         foreach (XmlSchemaAttribute a in schema.Attributes.Values)
223                                 GlobalAttributes.Add (a.QualifiedName, a);
224                         foreach (XmlSchemaType t in schema.SchemaTypes.Values)
225                                 GlobalTypes.Add (t.QualifiedName, t);
226                         foreach (XmlSchemaAttributeGroup g in schema.AttributeGroups.Values)
227                                 global_attribute_groups.Add (g.QualifiedName, g);
228                         foreach (XmlSchemaGroup g in schema.Groups.Values)
229                                 global_groups.Add (g.QualifiedName, g);
230                         foreach (DictionaryEntry pair in schema.IDCollection)
231                                 global_ids.Add (pair.Key, pair.Value);
232                         foreach (XmlSchemaIdentityConstraint ic in schema.NamedIdentities.Values)
233                                 global_identity_constraints.Add (ic.QualifiedName, ic);
234                 }
235
236                 public bool Contains (string targetNamespace)
237                 {
238                         targetNamespace = GetSafeNs (targetNamespace);
239                         foreach (XmlSchema schema in schemas)
240                                 if (GetSafeNs (schema.TargetNamespace) == targetNamespace)
241                                         return true;
242                         return false;
243                 }
244
245                 public bool Contains (XmlSchema schema)
246                 {
247                         foreach (XmlSchema s in schemas)
248                                 if (s == schema)
249                                         return true;
250                         return false;
251                 }
252
253                 public void CopyTo (XmlSchema [] schemas, int index)
254                 {
255                         this.schemas.CopyTo (schemas, index);
256                 }
257
258                 internal void CopyTo (Array array, int index)
259                 {
260                         schemas.CopyTo (array, index);
261                 }
262
263                 string GetSafeNs (string ns)
264                 {
265                         return ns == null ? "" : ns;
266                 }
267
268                 [MonoTODO]
269                 // FIXME: Check exact behavior
270                 public XmlSchema Remove (XmlSchema schema)
271                 {
272                         if (schema == null)
273                                 throw new ArgumentNullException ("schema");
274                         ArrayList al = new ArrayList ();
275                         al.AddRange (schemas);
276                         if (!al.Contains (schema))
277                                 return null;
278                         // FIXME: I have no idea why Remove() might throw
279                         // XmlSchemaException, except for the case it compiles.
280                         if (!schema.IsCompiled)
281                                 schema.CompileSubset (ValidationEventHandler, this, xmlResolver);
282                         schemas.Remove (schema);
283                         ResetCompile ();
284                         return schema;
285                 }
286
287                 void ResetCompile ()
288                 {
289                         isCompiled = false;
290                         ClearGlobalComponents ();
291                 }
292
293                 public bool RemoveRecursive (XmlSchema schemaToRemove)
294                 {
295                         if (schemaToRemove == null)
296                                 throw new ArgumentNullException ("schema");
297                         ArrayList al = new ArrayList ();
298                         al.AddRange (schemas);
299                         if (!al.Contains (schemaToRemove))
300                                 return false;
301                         al.Remove (schemaToRemove);
302                         schemas.Remove (schemaToRemove);
303
304                         if (!IsCompiled)
305                                 return true;
306
307                         ClearGlobalComponents ();
308                         foreach (XmlSchema s in al) {
309                                 if (s.IsCompiled)
310                                         AddGlobalComponents (schemaToRemove);
311                         }
312                         return true;
313                 }
314
315                 public XmlSchema Reprocess (XmlSchema schema)
316                 {
317                         if (schema == null)
318                                 throw new ArgumentNullException ("schema");
319                         ArrayList al = new ArrayList ();
320                         al.AddRange (schemas);
321                         if (!al.Contains (schema))
322                                 throw new ArgumentException ("Target schema is not contained in the schema set.");
323                         ClearGlobalComponents ();
324                         foreach (XmlSchema s in al) {
325                                 if (schema == s)
326                                         schema.CompileSubset (ValidationEventHandler, this, xmlResolver);
327                                 if (s.IsCompiled)
328                                         AddGlobalComponents (schema);
329                         }
330                         return schema.IsCompiled ? schema : null;
331                 }
332
333                 public ICollection Schemas ()
334                 {
335                         return schemas;
336                 }
337
338                 public ICollection Schemas (string targetNamespace)
339                 {
340                         targetNamespace = GetSafeNs (targetNamespace);
341                         ArrayList al = new ArrayList ();
342                         foreach (XmlSchema schema in schemas)
343                                 if (GetSafeNs (schema.TargetNamespace) == targetNamespace)
344                                         al.Add (schema);
345                         return al;
346                 }
347
348                 internal bool MissedSubComponents (string targetNamespace)
349                 {
350                         foreach (XmlSchema s in Schemas (targetNamespace))
351                                 if (s.missedSubComponents)
352                                         return true;
353                         return false;
354                 }
355         }
356 }