Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Schema / XmlSchemaCollection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlSchemaCollection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright> 
5 // <owner current="true" primary="true">Microsoft</owner>                                                               
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Schema {
9
10     using System;
11     using System.Threading;
12     using System.Collections;
13     using System.Xml.Schema;
14     using System.Runtime.Versioning;
15
16
17     /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection"]/*' />
18     /// <devdoc>
19     ///    <para>The XmlSchemaCollection contains a set of namespace URI's.
20     ///       Each namespace also have an associated private data cache
21     ///       corresponding to the XML-Data Schema or W3C XML Schema.
22     ///       The XmlSchemaCollection will able to load XSD and XDR schemas,
23     ///       and compile them into an internal "cooked schema representation".
24     ///       The Validate method then uses this internal representation for
25     ///       efficient runtime validation of any given subtree.</para>
26     /// </devdoc>
27     [Obsolete("Use System.Xml.Schema.XmlSchemaSet for schema compilation and validation. http://go.microsoft.com/fwlink/?linkid=14202")]
28     public sealed class XmlSchemaCollection: ICollection {
29         private Hashtable               collection;
30         private XmlNameTable            nameTable;
31         private SchemaNames             schemaNames;
32         private ReaderWriterLock        wLock;
33         private int                     timeout = Timeout.Infinite;
34         private bool                    isThreadSafe = true;
35         private ValidationEventHandler  validationEventHandler = null;
36         private XmlResolver             xmlResolver = null;
37
38
39         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.XmlSchemaCollection"]/*' />
40         /// <devdoc>
41         ///    <para>Construct a new empty schema collection.</para>
42         /// </devdoc>
43         public XmlSchemaCollection() : this(new NameTable()) {
44         }
45  
46         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.XmlSchemaCollection1"]/*' />
47         /// <devdoc>
48         ///    <para>Construct a new empty schema collection with associated XmlNameTable.
49         ///       The XmlNameTable is used when loading schemas</para>
50         /// </devdoc>
51         public XmlSchemaCollection(XmlNameTable nametable) {
52             if (nametable == null) {
53                 throw new ArgumentNullException("nametable");
54             }
55             nameTable = nametable;
56             collection = Hashtable.Synchronized(new Hashtable());
57             xmlResolver = System.Xml.XmlConfiguration.XmlReaderSection.CreateDefaultResolver();
58             isThreadSafe = true;
59             if (isThreadSafe) {
60                 wLock = new ReaderWriterLock();
61             }
62         }
63
64         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Count"]/*' />
65         /// <devdoc>
66         ///    <para>Returns the number of namespaces defined in this collection
67         ///       (whether or not there is an actual schema associated with those namespaces or not).</para>
68         /// </devdoc>
69         public int Count {
70             get { return collection.Count;}
71         }
72  
73         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.NameTable"]/*' />
74         /// <devdoc>
75         ///    <para>The default XmlNameTable used by the XmlSchemaCollection when loading new schemas.</para>
76         /// </devdoc>
77         public XmlNameTable NameTable {
78             get { return nameTable;}
79         }
80
81         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.ValidationEventHandler"]/*' />
82         public  event ValidationEventHandler ValidationEventHandler {
83             add { validationEventHandler += value; }
84             remove { validationEventHandler -= value; }
85         }
86
87         internal XmlResolver XmlResolver
88         {
89             set {
90                 xmlResolver = value;
91             }
92         }
93
94
95         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add"]/*' />
96         /// <devdoc>
97         ///    <para>Add the schema located by the given URL into the schema collection.
98         ///       If the given schema references other namespaces, the schemas for those other
99         ///       namespaces are NOT automatically loaded.</para>
100         /// </devdoc>
101         [ResourceConsumption(ResourceScope.Machine)]
102         [ResourceExposure(ResourceScope.Machine)]
103         public XmlSchema Add(string ns, string uri) {
104             if (uri == null || uri.Length == 0)
105                 throw new ArgumentNullException("uri");
106             XmlTextReader reader = new XmlTextReader(uri, nameTable);
107             reader.XmlResolver = xmlResolver;
108
109             XmlSchema schema = null;
110             try {
111                 schema = Add(ns, reader, xmlResolver);
112                 while(reader.Read());// wellformness check
113             }
114             finally {
115                 reader.Close();
116             }
117             return schema;
118         }
119
120         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add4"]/*' />
121         public XmlSchema Add(String ns, XmlReader reader) {
122             return Add(ns, reader, xmlResolver);        
123         }
124
125         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add1"]/*' />
126         /// <devdoc>
127         ///    <para>Add the given schema into the schema collection.
128         ///       If the given schema references other namespaces, the schemas for those
129         ///       other namespaces are NOT automatically loaded.</para>
130         /// </devdoc>
131         public XmlSchema Add(String ns, XmlReader reader, XmlResolver resolver) {
132             if (reader == null)
133                 throw new ArgumentNullException("reader");
134             XmlNameTable readerNameTable = reader.NameTable;
135             SchemaInfo schemaInfo = new SchemaInfo(); 
136                 
137             Parser parser = new Parser(SchemaType.None, readerNameTable, GetSchemaNames(readerNameTable), validationEventHandler);
138             parser.XmlResolver = resolver;
139             SchemaType schemaType;
140             try {
141                 schemaType = parser.Parse(reader, ns);
142             }
143             catch (XmlSchemaException e) {
144                 SendValidationEvent(e);
145                 return null;
146             }
147
148             if (schemaType == SchemaType.XSD) {
149                                 schemaInfo.SchemaType = SchemaType.XSD;
150                 return Add(ns, schemaInfo, parser.XmlSchema, true, resolver);
151             }
152             else {
153                 SchemaInfo xdrSchema = parser.XdrSchema;
154                 return Add(ns, parser.XdrSchema, null, true, resolver);
155             }
156         }
157
158         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add2"]/*' />
159         /// <devdoc>
160         ///    <para>[To be supplied.]</para>
161         /// </devdoc>
162         public XmlSchema Add(XmlSchema schema) {
163                         return Add(schema, xmlResolver);
164         }
165
166         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add5"]/*' />
167             public XmlSchema Add(XmlSchema schema, XmlResolver resolver) {
168             if (schema == null)
169                 throw new ArgumentNullException("schema");
170
171             SchemaInfo schemaInfo = new SchemaInfo(); 
172             schemaInfo.SchemaType = SchemaType.XSD;
173             return Add(schema.TargetNamespace, schemaInfo, schema, true, resolver);
174         }
175
176         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Add3"]/*' />
177         /// <devdoc>
178         ///    <para>Adds all the namespaces defined in the given collection
179         ///       (including their associated schemas) to this collection.</para>
180         /// </devdoc>
181         public void Add(XmlSchemaCollection schema) {
182             if (schema == null)
183                 throw new ArgumentNullException("schema");
184             if (this == schema)
185                 return;
186             IDictionaryEnumerator enumerator = schema.collection.GetEnumerator();
187             while (enumerator.MoveNext()) {
188                 XmlSchemaCollectionNode node = (XmlSchemaCollectionNode) enumerator.Value;
189                 Add(node.NamespaceURI, node);
190             }
191         }
192
193
194         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.this"]/*' />
195         /// <devdoc>
196         ///    <para>Looks up the schema by it's associated namespace URI</para>
197         /// </devdoc>
198         public XmlSchema this[string ns] {
199             get {
200                 XmlSchemaCollectionNode node = (XmlSchemaCollectionNode)collection[(ns != null) ? ns: string.Empty];
201                 return (node != null) ? node.Schema : null;
202             }
203         }
204
205         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Contains"]/*' />
206         /// <devdoc>
207         ///    <para>[To be supplied.]</para>
208         /// </devdoc>
209         public bool Contains(XmlSchema schema) {
210             if (schema == null) {
211                 throw new ArgumentNullException("schema");
212             }
213             return this[schema.TargetNamespace] != null;
214         }
215
216         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.Contains1"]/*' />
217         public bool Contains(string ns) {
218             return collection[(ns != null) ? ns : string.Empty] != null;
219         }
220
221         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.IEnumerable.GetEnumerator"]/*' />
222         /// <internalonly/>
223         /// <devdoc>
224         /// Get a IEnumerator of the XmlSchemaCollection.
225         /// </devdoc>
226         IEnumerator IEnumerable.GetEnumerator() {
227             return new XmlSchemaCollectionEnumerator(collection);
228         }
229
230         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.GetEnumerator"]/*' />
231         public XmlSchemaCollectionEnumerator GetEnumerator() {
232             return new XmlSchemaCollectionEnumerator(collection);
233         }
234
235         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.ICollection.CopyTo"]/*' />
236         /// <internalonly/>
237         void ICollection.CopyTo(Array array, int index) {
238             if (array == null)
239                 throw new ArgumentNullException("array");
240             if (index < 0)
241                 throw new ArgumentOutOfRangeException("index");
242             for (XmlSchemaCollectionEnumerator e = this.GetEnumerator(); e.MoveNext();) {
243                 if (index == array.Length && array.IsFixedSize) {
244                     throw new ArgumentOutOfRangeException("index");
245                 }
246                 array.SetValue(e.Current, index++);
247             }
248         }
249
250         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.CopyTo"]/*' />
251         /// <devdoc>
252         ///    <para>[To be supplied.]</para>
253         /// </devdoc>
254         public void CopyTo(XmlSchema[] array, int index) {
255             if (array == null)
256                 throw new ArgumentNullException("array");
257             if (index < 0)
258                 throw new ArgumentOutOfRangeException("index");
259             for (XmlSchemaCollectionEnumerator e = this.GetEnumerator(); e.MoveNext();) {
260                 XmlSchema schema = e.Current;
261                 if (schema != null) {
262                     if (index == array.Length) {
263                         throw new ArgumentOutOfRangeException("index");
264                     }
265                     array[index++] = e.Current;
266                 }
267             }
268         }
269
270         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.ICollection.IsSynchronized"]/*' />
271         /// <internalonly/>
272         bool ICollection.IsSynchronized {
273             get { return true; }
274         }
275
276         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.ICollection.SyncRoot"]/*' />
277         /// <internalonly/>
278         object ICollection.SyncRoot {
279             get { return this; }
280         }
281
282         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollection.ICollection.Count"]/*' />
283         /// <internalonly/>
284         int ICollection.Count {
285             get { return collection.Count; }
286         }
287
288         internal SchemaInfo GetSchemaInfo(string ns) {
289             XmlSchemaCollectionNode node = (XmlSchemaCollectionNode)collection[(ns != null) ? ns : string.Empty];
290             return (node != null) ? node.SchemaInfo : null;
291         }
292
293         internal SchemaNames GetSchemaNames(XmlNameTable nt) {
294             if (nameTable != nt) {
295                 return new SchemaNames(nt);
296             }
297             else {
298                 if (schemaNames == null) {
299                     schemaNames = new SchemaNames( nameTable );
300                 }
301                 return schemaNames;
302             }
303         }
304
305         internal XmlSchema Add(string ns, SchemaInfo schemaInfo, XmlSchema schema, bool compile) {
306                 return Add(ns, schemaInfo, schema, compile, xmlResolver);
307         }
308
309         private XmlSchema Add(string ns, SchemaInfo schemaInfo, XmlSchema schema, bool compile, XmlResolver resolver) {
310             int errorCount = 0;
311             if (schema != null) {
312                 if (schema.ErrorCount == 0 && compile) {
313                                         if (!schema.CompileSchema(this, resolver, schemaInfo, ns, validationEventHandler, nameTable, true)) {
314                                                 errorCount = 1;
315                                         }
316                                         ns = schema.TargetNamespace == null ? string.Empty : schema.TargetNamespace;
317                 }
318                                 errorCount += schema.ErrorCount;
319             } 
320             else {
321                 errorCount += schemaInfo.ErrorCount;
322                 //ns = ns == null? string.Empty : NameTable.Add(ns);
323                 ns = NameTable.Add(ns); //Added without checking for ns == null, since XDR cannot have null namespace
324             }
325             if (errorCount == 0) {
326                 XmlSchemaCollectionNode node = new XmlSchemaCollectionNode();
327                 node.NamespaceURI = ns;
328                 node.SchemaInfo = schemaInfo; 
329                                 node.Schema = schema; 
330                 Add(ns, node);
331                 return schema;
332             }
333             return null;
334         }
335
336         private void Add(string ns, XmlSchemaCollectionNode node) {
337             if (isThreadSafe)
338                 wLock.AcquireWriterLock(timeout);           
339             try {
340                 if (collection[ns] != null)
341                     collection.Remove(ns);
342                 collection.Add(ns, node);    
343             }
344             finally {
345                 if (isThreadSafe)
346                     wLock.ReleaseWriterLock();                      
347             }
348         }
349
350         private void SendValidationEvent(XmlSchemaException e) {
351             if (validationEventHandler != null) {
352                 validationEventHandler(this, new ValidationEventArgs(e));
353             } 
354             else {
355                 throw e;
356             }
357         }
358
359         internal ValidationEventHandler EventHandler {
360             get {
361                 return validationEventHandler;
362             }
363             set {
364                 validationEventHandler = value;
365             }
366         }
367     };
368
369
370     internal sealed class XmlSchemaCollectionNode {
371         private String      namespaceUri;
372         private SchemaInfo  schemaInfo;
373         private XmlSchema   schema;
374
375         internal String NamespaceURI {
376             get { return namespaceUri;}
377             set { namespaceUri = value;}
378         }
379
380         internal SchemaInfo SchemaInfo {       
381             get { return schemaInfo;}
382             set { schemaInfo = value;}
383         }
384
385         internal XmlSchema Schema {
386             get { return schema;}
387             set { schema = value;}
388         }   
389 }
390
391
392     /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator"]/*' />
393     /// <devdoc>
394     ///    <para>[To be supplied.]</para>
395     /// </devdoc>
396     public sealed class XmlSchemaCollectionEnumerator: IEnumerator {
397         private IDictionaryEnumerator     enumerator;
398
399         internal XmlSchemaCollectionEnumerator( Hashtable collection ) {
400             enumerator = collection.GetEnumerator();            
401         }
402
403         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator.IEnumerator.Reset"]/*' />
404         /// <internalonly/>
405         void IEnumerator.Reset() {
406             enumerator.Reset();
407         }
408
409         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator.IEnumerator.MoveNext"]/*' />
410         /// <internalonly/>
411         bool IEnumerator.MoveNext() {
412             return enumerator.MoveNext();
413         }
414
415         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator.MoveNext"]/*' />
416         /// <devdoc>
417         ///    <para>[To be supplied.]</para>
418         /// </devdoc>
419         public bool MoveNext() {
420             return enumerator.MoveNext();
421         }
422
423         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator.IEnumerator.Current"]/*' />
424         /// <internalonly/>
425         object IEnumerator.Current {
426             get { return this.Current; }
427         }
428
429         /// <include file='doc\XmlSchemaCollection.uex' path='docs/doc[@for="XmlSchemaCollectionEnumerator.Current"]/*' />
430         /// <devdoc>
431         ///    <para>[To be supplied.]</para>
432         /// </devdoc>
433         public XmlSchema Current {
434             get {
435                 XmlSchemaCollectionNode n = (XmlSchemaCollectionNode)enumerator.Value;
436                 if (n != null)
437                     return n.Schema;
438                 else
439                     return null;
440             }
441         }
442
443         internal XmlSchemaCollectionNode CurrentNode {
444             get {
445                 XmlSchemaCollectionNode n = (XmlSchemaCollectionNode)enumerator.Value;
446                 return n;
447             }
448         }
449     }
450 }