1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Collections;
7 using System.Threading;
8 using System.Collections.Generic;
9 using System.Runtime.Versioning;
10 
11 namespace System.Xml.Schema
12 {
13     /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet"]/*' />
14     /// <devdoc>
15     ///    <para>The XmlSchemaSet contains a set of namespace URI's.
16     ///       Each namespace also have an associated private data cache
17     ///       corresponding to the XML-Data Schema or W3C XML Schema.
18     ///       The XmlSchemaSet will able to load only XSD schemas,
19     ///       and compile them into an internal "cooked schema representation".
20     ///       The Validate method then uses this internal representation for
21     ///       efficient runtime validation of any given subtree.</para>
22     /// </devdoc>
23     public class XmlSchemaSet
24     {
25         private XmlNameTable _nameTable;
26         private SchemaNames _schemaNames;
27         private SortedList _schemas;              // List of source schemas
28 
29         //Event handling
30         private ValidationEventHandler _internalEventHandler;
31         private ValidationEventHandler _eventHandler;
32 
33         private bool _isCompiled = false;
34 
35         //Dictionary<Uri, XmlSchema> schemaLocations;
36         //Dictionary<ChameleonKey, XmlSchema> chameleonSchemas;
37         private Hashtable _schemaLocations;
38         private Hashtable _chameleonSchemas;
39 
40         private Hashtable _targetNamespaces;
41         private bool _compileAll;
42 
43         //Cached Compiled Info
44         private SchemaInfo _cachedCompiledInfo;
45 
46         //Reader settings to parse schema
47         private XmlReaderSettings _readerSettings;
48         private XmlSchema _schemaForSchema;  //Only one schema for schema per set
49 
50         //Schema compilation settings
51         private XmlSchemaCompilationSettings _compilationSettings;
52 
53         internal XmlSchemaObjectTable elements;
54         internal XmlSchemaObjectTable attributes;
55         internal XmlSchemaObjectTable schemaTypes;
56         internal XmlSchemaObjectTable substitutionGroups;
57         private XmlSchemaObjectTable _typeExtensions;
58 
59         //Thread safety
60         private Object _internalSyncObject;
61         internal Object InternalSyncObject
62         {
63             get
64             {
65                 if (_internalSyncObject == null)
66                 {
67                     Object o = new Object();
68                     Interlocked.CompareExchange<Object>(ref _internalSyncObject, o, null);
69                 }
70                 return _internalSyncObject;
71             }
72         }
73 
74         //Constructors
75 
76         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlSchemaSet"]/*' />
77         /// <devdoc>
78         ///    <para>Construct a new empty schema schemas.</para>
79         /// </devdoc>
XmlSchemaSet()80         public XmlSchemaSet() : this(new NameTable())
81         {
82         }
83 
84         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlSchemaSet1"]/*' />
85         /// <devdoc>
86         ///    <para>Construct a new empty schema schemas with associated XmlNameTable.
87         ///       The XmlNameTable is used when loading schemas</para>
88         /// </devdoc>
XmlSchemaSet(XmlNameTable nameTable)89         public XmlSchemaSet(XmlNameTable nameTable)
90         {
91             if (nameTable == null)
92             {
93                 throw new ArgumentNullException(nameof(nameTable));
94             }
95             _nameTable = nameTable;
96             _schemas = new SortedList();
97 
98             /*schemaLocations = new Dictionary<Uri, XmlSchema>();
99             chameleonSchemas = new Dictionary<ChameleonKey, XmlSchema>();*/
100             _schemaLocations = new Hashtable();
101             _chameleonSchemas = new Hashtable();
102             _targetNamespaces = new Hashtable();
103             _internalEventHandler = new ValidationEventHandler(InternalValidationCallback);
104             _eventHandler = _internalEventHandler;
105 
106             _readerSettings = new XmlReaderSettings();
107 
108             // we don't have to check XmlReaderSettings.EnableLegacyXmlSettings() here because the following
109             // code will return same result either we are running on v4.5 or later
110             if (_readerSettings.GetXmlResolver() == null)
111             {
112                 // The created resolver will be used in the schema validation only
113                 _readerSettings.XmlResolver = new XmlUrlResolver();
114                 _readerSettings.IsXmlResolverSet = false;
115             }
116 
117             _readerSettings.NameTable = nameTable;
118             _readerSettings.DtdProcessing = DtdProcessing.Prohibit;
119 
120             _compilationSettings = new XmlSchemaCompilationSettings();
121             _cachedCompiledInfo = new SchemaInfo();
122             _compileAll = true;
123         }
124 
125 
126         //Public Properties
127         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.NameTable"]/*' />
128         /// <devdoc>
129         ///    <para>The default XmlNameTable used by the XmlSchemaSet when loading new schemas.</para>
130         /// </devdoc>
131         public XmlNameTable NameTable
132         {
133             get { return _nameTable; }
134         }
135 
136         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.ValidationEventHandler"]/*' />
137         public event ValidationEventHandler ValidationEventHandler
138         {
139             add
140             {
141                 _eventHandler -= _internalEventHandler;
142                 _eventHandler += value;
143                 if (_eventHandler == null)
144                 {
145                     _eventHandler = _internalEventHandler;
146                 }
147             }
148             remove
149             {
150                 _eventHandler -= value;
151                 if (_eventHandler == null)
152                 {
153                     _eventHandler = _internalEventHandler;
154                 }
155             }
156         }
157 
158         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.IsCompiled"]/*' />
159         /// <devdoc>
160         ///    <para>IsCompiled is true when the schema set is in compiled state</para>
161         /// </devdoc>
162         public bool IsCompiled
163         {
164             get
165             {
166                 return _isCompiled;
167             }
168         }
169 
170         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.XmlResolver"]/*' />
171         /// <devdoc>
172         ///    <para></para>
173         /// </devdoc>
174         public XmlResolver XmlResolver
175         {
176             set
177             {
178                 _readerSettings.XmlResolver = value;
179             }
180         }
181 
182         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.CompilationSettings"]/*' />
183         /// <devdoc>
184         ///    <para></para>
185         /// </devdoc>
186         public XmlSchemaCompilationSettings CompilationSettings
187         {
188             get
189             {
190                 return _compilationSettings;
191             }
192             set
193             {
194                 _compilationSettings = value;
195             }
196         }
197 
198         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Count"]/*' />
199         /// <devdoc>
200         ///    <para>Returns the count of schemas in the set</para>
201         /// </devdoc>
202         public int Count
203         {
204             get
205             {
206                 return _schemas.Count;
207             }
208         }
209         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalElements"]/*' />
210         /// <devdoc>
211         ///    <para></para>
212         /// </devdoc>
213         public XmlSchemaObjectTable GlobalElements
214         {
215             get
216             {
217                 if (elements == null)
218                 {
219                     elements = new XmlSchemaObjectTable();
220                 }
221                 return elements;
222             }
223         }
224 
225         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalAttributes"]/*' />
226         /// <devdoc>
227         ///    <para></para>
228         /// </devdoc>
229         public XmlSchemaObjectTable GlobalAttributes
230         {
231             get
232             {
233                 if (attributes == null)
234                 {
235                     attributes = new XmlSchemaObjectTable();
236                 }
237                 return attributes;
238             }
239         }
240 
241         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.GlobalTypes"]/*' />
242         /// <devdoc>
243         ///    <para></para>
244         /// </devdoc>
245         public XmlSchemaObjectTable GlobalTypes
246         {
247             get
248             {
249                 if (schemaTypes == null)
250                 {
251                     schemaTypes = new XmlSchemaObjectTable();
252                 }
253                 return schemaTypes;
254             }
255         }
256 
257         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.SubstitutionGroups"]/*' />
258         /// <devdoc>
259         ///    <para></para>
260         /// </devdoc>
261         internal XmlSchemaObjectTable SubstitutionGroups
262         {
263             get
264             {
265                 if (substitutionGroups == null)
266                 {
267                     substitutionGroups = new XmlSchemaObjectTable();
268                 }
269                 return substitutionGroups;
270             }
271         }
272 
273         /// <summary>
274         /// Table of all types extensions
275         /// </summary>
276         internal Hashtable SchemaLocations
277         {
278             get
279             {
280                 return _schemaLocations;
281             }
282         }
283 
284         /// <summary>
285         /// Table of all types extensions
286         /// </summary>
287         internal XmlSchemaObjectTable TypeExtensions
288         {
289             get
290             {
291                 if (_typeExtensions == null)
292                 {
293                     _typeExtensions = new XmlSchemaObjectTable();
294                 }
295                 return _typeExtensions;
296             }
297         }
298         //Public Methods
299 
300         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add1"]/*' />
301         /// <devdoc>
302         ///    <para>Add the schema located by the given URL into the schema schemas.
303         ///       If the given schema references other namespaces, the schemas for those other
304         ///       namespaces are NOT automatically loaded.</para>
305         /// </devdoc>
Add(String targetNamespace, String schemaUri)306         public XmlSchema Add(String targetNamespace, String schemaUri)
307         {
308             if (schemaUri == null || schemaUri.Length == 0)
309             {
310                 throw new ArgumentNullException(nameof(schemaUri));
311             }
312             if (targetNamespace != null)
313             {
314                 targetNamespace = XmlComplianceUtil.CDataNormalize(targetNamespace);
315             }
316             XmlSchema schema = null;
317             lock (InternalSyncObject)
318             {
319                 //Check if schema from url has already been added
320                 XmlResolver tempResolver = _readerSettings.GetXmlResolver();
321                 if (tempResolver == null)
322                 {
323                     tempResolver = new XmlUrlResolver();
324                 }
325                 Uri tempSchemaUri = tempResolver.ResolveUri(null, schemaUri);
326                 if (IsSchemaLoaded(tempSchemaUri, targetNamespace, out schema))
327                 {
328                     return schema;
329                 }
330                 else
331                 {
332                     //Url already not processed; Load SOM from url
333                     XmlReader reader = XmlReader.Create(schemaUri, _readerSettings);
334                     try
335                     {
336                         schema = Add(targetNamespace, ParseSchema(targetNamespace, reader));
337                         while (reader.Read()) ;// wellformness check;
338                     }
339                     finally
340                     {
341                         reader.Close();
342                     }
343                 }
344             }
345             return schema;
346         }
347 
348 
349         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add4"]/*' />
350         /// <devdoc>
351         ///    <para>Add the given schema into the schema schemas.
352         ///       If the given schema references other namespaces, the schemas for those
353         ///       other namespaces are NOT automatically loaded.</para>
354         /// </devdoc>
Add(String targetNamespace, XmlReader schemaDocument)355         public XmlSchema Add(String targetNamespace, XmlReader schemaDocument)
356         {
357             if (schemaDocument == null)
358             {
359                 throw new ArgumentNullException(nameof(schemaDocument));
360             }
361             if (targetNamespace != null)
362             {
363                 targetNamespace = XmlComplianceUtil.CDataNormalize(targetNamespace);
364             }
365             lock (InternalSyncObject)
366             {
367                 XmlSchema schema = null;
368                 Uri schemaUri = new Uri(schemaDocument.BaseURI, UriKind.RelativeOrAbsolute);
369                 if (IsSchemaLoaded(schemaUri, targetNamespace, out schema))
370                 {
371                     return schema;
372                 }
373                 else
374                 {
375                     DtdProcessing dtdProcessing = _readerSettings.DtdProcessing;
376                     SetDtdProcessing(schemaDocument);
377                     schema = Add(targetNamespace, ParseSchema(targetNamespace, schemaDocument));
378                     _readerSettings.DtdProcessing = dtdProcessing; //reset dtdProcessing setting
379                     return schema;
380                 }
381             }
382         }
383 
384 
385         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add5"]/*' />
386         /// <devdoc>
387         ///    <para>Adds all the namespaces defined in the given schemas
388         ///       (including their associated schemas) to this schemas.</para>
389         /// </devdoc>
Add(XmlSchemaSet schemas)390         public void Add(XmlSchemaSet schemas)
391         {
392             if (schemas == null)
393             {
394                 throw new ArgumentNullException(nameof(schemas));
395             }
396             if (this == schemas)
397             {
398                 return;
399             }
400             bool thisLockObtained = false;
401             bool schemasLockObtained = false;
402             try
403             {
404                 SpinWait spinner = new SpinWait();
405                 while (true)
406                 {
407                     Monitor.TryEnter(InternalSyncObject, ref thisLockObtained);
408                     if (thisLockObtained)
409                     {
410                         Monitor.TryEnter(schemas.InternalSyncObject, ref schemasLockObtained);
411                         if (schemasLockObtained)
412                         {
413                             break;
414                         }
415                         else
416                         {
417                             Monitor.Exit(InternalSyncObject); //Give up this lock and try both again
418                             thisLockObtained = false;
419                             spinner.SpinOnce(); //Let the thread that holds the lock run
420                             continue;
421                         }
422                     }
423                 }
424 
425                 XmlSchema currentSchema;
426                 if (schemas.IsCompiled)
427                 {
428                     CopyFromCompiledSet(schemas);
429                 }
430                 else
431                 {
432                     bool remove = false;
433                     string tns = null;
434                     foreach (XmlSchema schema in schemas.SortedSchemas.Values)
435                     {
436                         tns = schema.TargetNamespace;
437                         if (tns == null)
438                         {
439                             tns = string.Empty;
440                         }
441                         if (_schemas.ContainsKey(schema.SchemaId) || FindSchemaByNSAndUrl(schema.BaseUri, tns, null) != null)
442                         { //Do not already existing url
443                             continue;
444                         }
445                         currentSchema = Add(schema.TargetNamespace, schema);
446                         if (currentSchema == null)
447                         {
448                             remove = true;
449                             break;
450                         }
451                     }
452                     //Remove all from the set if even one schema in the passed in set is not preprocessed.
453                     if (remove)
454                     {
455                         foreach (XmlSchema schema in schemas.SortedSchemas.Values)
456                         { //Remove all previously added schemas from the set
457                             _schemas.Remove(schema.SchemaId); //Might remove schema that was already there and was not added thru this operation
458                             _schemaLocations.Remove(schema.BaseUri);
459                         }
460                     }
461                 }
462             }
463             finally
464             { //release locks on sets
465                 if (thisLockObtained)
466                 {
467                     Monitor.Exit(InternalSyncObject);
468                 }
469                 if (schemasLockObtained)
470                 {
471                     Monitor.Exit(schemas.InternalSyncObject);
472                 }
473             }
474         }
475 
476         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Add6"]/*' />
Add(XmlSchema schema)477         public XmlSchema Add(XmlSchema schema)
478         {
479             if (schema == null)
480             {
481                 throw new ArgumentNullException(nameof(schema));
482             }
483             lock (InternalSyncObject)
484             {
485                 if (_schemas.ContainsKey(schema.SchemaId))
486                 {
487                     return schema;
488                 }
489                 return Add(schema.TargetNamespace, schema);
490             }
491         }
492 
493         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Remove"]/*' />
Remove(XmlSchema schema)494         public XmlSchema Remove(XmlSchema schema)
495         {
496             return Remove(schema, true);
497         }
498 
499         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.RemoveRecursive"]/*' />
RemoveRecursive(XmlSchema schemaToRemove)500         public bool RemoveRecursive(XmlSchema schemaToRemove)
501         {
502             if (schemaToRemove == null)
503             {
504                 throw new ArgumentNullException(nameof(schemaToRemove));
505             }
506             if (!_schemas.ContainsKey(schemaToRemove.SchemaId))
507             {
508                 return false;
509             }
510 
511             lock (InternalSyncObject)
512             { //Need to lock here so that remove cannot be called while the set is being compiled
513                 if (_schemas.ContainsKey(schemaToRemove.SchemaId))
514                 { //Need to check again
515                     //Build disallowedNamespaces list
516                     Hashtable disallowedNamespaces = new Hashtable();
517                     disallowedNamespaces.Add(GetTargetNamespace(schemaToRemove), schemaToRemove);
518                     string importedNS;
519                     for (int i = 0; i < schemaToRemove.ImportedNamespaces.Count; i++)
520                     {
521                         importedNS = (string)schemaToRemove.ImportedNamespaces[i];
522                         if (disallowedNamespaces[importedNS] == null)
523                         {
524                             disallowedNamespaces.Add(importedNS, importedNS);
525                         }
526                     }
527 
528                     //Removal list is all schemas imported by this schema directly or indirectly
529                     //Need to check if other schemas in the set import schemaToRemove / any of its imports
530                     ArrayList needToCheckSchemaList = new ArrayList();
531                     XmlSchema mainSchema;
532                     for (int i = 0; i < _schemas.Count; i++)
533                     {
534                         mainSchema = (XmlSchema)_schemas.GetByIndex(i);
535                         if (mainSchema == schemaToRemove ||
536                             schemaToRemove.ImportedSchemas.Contains(mainSchema))
537                         {
538                             continue;
539                         }
540                         needToCheckSchemaList.Add(mainSchema);
541                     }
542 
543                     mainSchema = null;
544                     for (int i = 0; i < needToCheckSchemaList.Count; i++)
545                     { //Perf: Not using nested foreach here
546                         mainSchema = (XmlSchema)needToCheckSchemaList[i];
547 
548                         if (mainSchema.ImportedNamespaces.Count > 0)
549                         {
550                             foreach (string tns in disallowedNamespaces.Keys)
551                             {
552                                 if (mainSchema.ImportedNamespaces.Contains(tns))
553                                 {
554                                     SendValidationEvent(new XmlSchemaException(SR.Sch_SchemaNotRemoved, string.Empty), XmlSeverityType.Warning);
555                                     return false;
556                                 }
557                             }
558                         }
559                     }
560 
561                     Remove(schemaToRemove, true);
562                     for (int i = 0; i < schemaToRemove.ImportedSchemas.Count; ++i)
563                     {
564                         XmlSchema impSchema = (XmlSchema)schemaToRemove.ImportedSchemas[i];
565                         Remove(impSchema, true);
566                     }
567                     return true;
568                 }
569             }
570             return false;
571         }
572 
573         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Contains1"]/*' />
Contains(String targetNamespace)574         public bool Contains(String targetNamespace)
575         {
576             if (targetNamespace == null)
577             {
578                 targetNamespace = string.Empty;
579             }
580             return _targetNamespaces[targetNamespace] != null;
581         }
582 
583         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Contains2"]/*' />
Contains(XmlSchema schema)584         public bool Contains(XmlSchema schema)
585         {
586             if (schema == null)
587             {
588                 throw new ArgumentNullException(nameof(schema));
589             }
590             return _schemas.ContainsValue(schema);
591         }
592 
593         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Compile"]/*' />
594         /// <devdoc>
595         ///    <para>[To be supplied.]</para>
596         /// </devdoc>
Compile()597         public void Compile()
598         {
599             if (_isCompiled)
600             {
601                 return;
602             }
603             if (_schemas.Count == 0)
604             {
605                 ClearTables(); //Clear any previously present compiled state left by calling just Remove() on the set
606                 _cachedCompiledInfo = new SchemaInfo();
607                 _isCompiled = true;
608                 _compileAll = false;
609                 return;
610             }
611             lock (InternalSyncObject)
612             {
613                 if (!_isCompiled)
614                 { //Locking before checking isCompiled to avoid problems with double locking
615                     Compiler compiler = new Compiler(_nameTable, _eventHandler, _schemaForSchema, _compilationSettings);
616                     SchemaInfo newCompiledInfo = new SchemaInfo();
617                     int schemaIndex = 0;
618                     if (!_compileAll)
619                     { //if we are not compiling everything again, Move the pre-compiled schemas to the compiler's tables
620                         compiler.ImportAllCompiledSchemas(this);
621                     }
622                     try
623                     { //First thing to do in the try block is to acquire locks since finally will try to release them.
624                         //If we don't acquire the locks first, and an exception occurs in the code before the locking code, then Threading.SynchronizationLockException will be thrown
625                         //when attempting to release it in the finally block
626                         XmlSchema currentSchema;
627                         XmlSchema xmlNSSchema = Preprocessor.GetBuildInSchema();
628                         for (schemaIndex = 0; schemaIndex < _schemas.Count; schemaIndex++)
629                         {
630                             currentSchema = (XmlSchema)_schemas.GetByIndex(schemaIndex);
631 
632                             //Lock schema to be compiled
633                             Monitor.Enter(currentSchema);
634                             if (!currentSchema.IsPreprocessed)
635                             {
636                                 SendValidationEvent(new XmlSchemaException(SR.Sch_SchemaNotPreprocessed, string.Empty), XmlSeverityType.Error);
637                                 _isCompiled = false;
638                                 return;
639                             }
640                             if (currentSchema.IsCompiledBySet)
641                             {
642                                 if (!_compileAll)
643                                 {
644                                     continue;
645                                 }
646                                 else if ((object)currentSchema == (object)xmlNSSchema)
647                                 { // prepare for xml namespace schema without cleanup
648                                     compiler.Prepare(currentSchema, false);
649                                     continue;
650                                 }
651                             }
652                             compiler.Prepare(currentSchema, true);
653                         }
654 
655                         _isCompiled = compiler.Execute(this, newCompiledInfo);
656                         if (_isCompiled)
657                         {
658                             if (!_compileAll)
659                             {
660                                 newCompiledInfo.Add(_cachedCompiledInfo, _eventHandler); //Add all the items from the old to the new compiled object
661                             }
662                             _compileAll = false;
663                             _cachedCompiledInfo = newCompiledInfo; //Replace the compiled info in the set after successful compilation
664                         }
665                     }
666                     finally
667                     {
668                         //Release locks on all schemas
669                         XmlSchema currentSchema;
670                         if (schemaIndex == _schemas.Count)
671                         {
672                             schemaIndex--;
673                         }
674                         for (int i = schemaIndex; i >= 0; i--)
675                         {
676                             currentSchema = (XmlSchema)_schemas.GetByIndex(i);
677                             if (currentSchema == Preprocessor.GetBuildInSchema())
678                             { //dont re-set compiled flags for xml namespace schema
679                                 Monitor.Exit(currentSchema);
680                                 continue;
681                             }
682                             currentSchema.IsCompiledBySet = _isCompiled;
683                             Monitor.Exit(currentSchema);
684                         }
685                     }
686                 }
687             }
688             return;
689         }
690 
691         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Reprocess"]/*' />
692         /// <devdoc>
693         ///    <para>[To be supplied.]</para>
694         /// </devdoc>
Reprocess(XmlSchema schema)695         public XmlSchema Reprocess(XmlSchema schema)
696         {
697             // Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Remove, Add and AddSchemaToSet
698             // methods. If you change anything here *make sure* to update Remove/Add/AddSchemaToSet method(s) accordingly.
699             // The only difference is that we don't touch .schemas collection here to not break a code like this:
700             // foreach(XmlSchema s in schemaset.schemas) { schemaset.Reprocess(s); }
701             // This is by purpose.
702             if (schema == null)
703             {
704                 throw new ArgumentNullException(nameof(schema));
705             }
706             if (!_schemas.ContainsKey(schema.SchemaId))
707             {
708                 throw new ArgumentException(SR.Sch_SchemaDoesNotExist, nameof(schema));
709             }
710             XmlSchema originalSchema = schema;
711             lock (InternalSyncObject)
712             { //Lock set so that set cannot be compiled in another thread
713                 // This code is copied from method:
714                 // Remove(XmlSchema schema, bool forceCompile)
715                 // If you changed anything here go and change the same in Remove(XmlSchema schema, bool forceCompile) method
716                 #region Copied from Remove(XmlSchema schema, bool forceCompile)
717 
718                 RemoveSchemaFromGlobalTables(schema);
719                 RemoveSchemaFromCaches(schema);
720                 if (schema.BaseUri != null)
721                 {
722                     _schemaLocations.Remove(schema.BaseUri);
723                 }
724                 string tns = GetTargetNamespace(schema);
725                 if (Schemas(tns).Count == 0)
726                 { //This is the only schema for that namespace
727                     _targetNamespaces.Remove(tns);
728                 }
729                 _isCompiled = false;
730                 _compileAll = true; //Force compilation of the whole set; This is when the set is not completely thread-safe
731 
732                 #endregion //Copied from Remove(XmlSchema schema, bool forceCompile)
733 
734 
735                 // This code is copied from method:
736                 // Add(string targetNamespace, XmlSchema schema)
737                 // If you changed anything here go and change the same in Add(string targetNamespace, XmlSchema schema) method
738                 #region Copied from Add(string targetNamespace, XmlSchema schema)
739 
740                 if (schema.ErrorCount != 0)
741                 { //Schema with parsing errors cannot be loaded
742                     return originalSchema;
743                 }
744                 if (PreprocessSchema(ref schema, schema.TargetNamespace))
745                 { //No perf opt for already compiled schemas
746                     // This code is copied from method:
747                     // AddSchemaToSet(XmlSchema schema)
748                     // If you changed anything here go and change the same in AddSchemaToSet(XmlSchema schema) method
749                     #region Copied from AddSchemaToSet(XmlSchema schema)
750 
751                     //Add to targetNamespaces table
752                     if (_targetNamespaces[tns] == null)
753                     {
754                         _targetNamespaces.Add(tns, tns);
755                     }
756                     if (_schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null)
757                     { //it has xs:anyType
758                         _schemaForSchema = schema;
759                     }
760                     for (int i = 0; i < schema.ImportedSchemas.Count; ++i)
761                     {    //Once preprocessed external schemas property is set
762                         XmlSchema s = (XmlSchema)schema.ImportedSchemas[i];
763                         if (!_schemas.ContainsKey(s.SchemaId))
764                         {
765                             _schemas.Add(s.SchemaId, s);
766                         }
767                         tns = GetTargetNamespace(s);
768                         if (_targetNamespaces[tns] == null)
769                         {
770                             _targetNamespaces.Add(tns, tns);
771                         }
772                         if (_schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null)
773                         { //it has xs:anyType
774                             _schemaForSchema = schema;
775                         }
776                     }
777                     #endregion //Copied from AddSchemaToSet(XmlSchema schema)
778                     return schema;
779                 }
780                 #endregion // Copied from Add(string targetNamespace, XmlSchema schema)
781 
782                 return originalSchema;
783             }
784         }
785 
786         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.CopyTo"]/*' />
787         /// <devdoc>
788         ///    <para>[To be supplied.]</para>
789         /// </devdoc>
CopyTo(XmlSchema[] schemas, int index)790         public void CopyTo(XmlSchema[] schemas, int index)
791         {
792             if (schemas == null)
793                 throw new ArgumentNullException(nameof(schemas));
794             if (index < 0 || index > schemas.Length - 1)
795                 throw new ArgumentOutOfRangeException(nameof(index));
796             _schemas.Values.CopyTo(schemas, index);
797         }
798 
799         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Schemas1"]/*' />
800         /// <devdoc>
801         ///    <para>[To be supplied.]</para>
802         /// </devdoc>
Schemas()803         public ICollection Schemas()
804         {
805             return _schemas.Values;
806         }
807 
808         /// <include file='doc\XmlSchemaSet.uex' path='docs/doc[@for="XmlSchemaSet.Schemas1"]/*' />
809         /// <devdoc>
810         ///    <para>[To be supplied.]</para>
811         /// </devdoc>
Schemas(String targetNamespace)812         public ICollection Schemas(String targetNamespace)
813         {
814             ArrayList tnsSchemas = new ArrayList();
815             XmlSchema currentSchema;
816             if (targetNamespace == null)
817             {
818                 targetNamespace = string.Empty;
819             }
820             for (int i = 0; i < _schemas.Count; i++)
821             {
822                 currentSchema = (XmlSchema)_schemas.GetByIndex(i);
823                 if (GetTargetNamespace(currentSchema) == targetNamespace)
824                 {
825                     tnsSchemas.Add(currentSchema);
826                 }
827             }
828             return tnsSchemas;
829         }
830 
831         //Internal Methods
832 
Add(string targetNamespace, XmlSchema schema)833         private XmlSchema Add(string targetNamespace, XmlSchema schema)
834         {
835             // Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
836             // method. If you change anything here *make sure* to update Reprocess method accordingly.
837 
838             if (schema == null || schema.ErrorCount != 0)
839             { //Schema with parsing errors cannot be loaded
840                 return null;
841             }
842 
843             // This code is copied to method:
844             // Reprocess(XmlSchema schema)
845             // If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
846             if (PreprocessSchema(ref schema, targetNamespace))
847             { //No perf opt for already compiled schemas
848                 AddSchemaToSet(schema);
849                 _isCompiled = false;
850                 return schema;
851             }
852             return null;
853         }
854 
855 #if TRUST_COMPILE_STATE
AddCompiledSchema(XmlSchema schema)856         private void AddCompiledSchema(XmlSchema schema) {
857             if (schema.IsCompiledBySet ) { //trust compiled state always if it is not a chameleon schema
858                 VerifyTables();
859                 SchemaInfo newCompiledInfo = new SchemaInfo();
860                 XmlSchemaObjectTable substitutionGroupsTable = null;
861                 if (!AddToCompiledInfo(schema, newCompiledInfo, ref substitutionGroupsTable)) { //Error while adding main schema
862                     return null;
863                 }
864                 foreach (XmlSchema impSchema in schema.ImportedSchemas) {
865                     if (!AddToCompiledInfo(impSchema, newCompiledInfo, ref substitutionGroupsTable)) { //Error while adding imports
866                         return null;
867                     }
868                 }
869                 newCompiledInfo.Add(cachedCompiledInfo, eventHandler); //Add existing compiled info
870                 cachedCompiledInfo = newCompiledInfo;
871                 if (substitutionGroupsTable != null) {
872                     ProcessNewSubstitutionGroups(substitutionGroupsTable, true);
873                 }
874                 if (schemas.Count == 0) { //If its the first compiled schema being added, then set doesnt need to be compiled
875                     isCompiled = true;
876                     compileAll = false;
877                 }
878                 AddSchemaToSet(schema);
879                 return schema;
880             }
881         }
882 
AddToCompiledInfo(XmlSchema schema, SchemaInfo newCompiledInfo, ref XmlSchemaObjectTable substTable)883         private bool AddToCompiledInfo(XmlSchema schema, SchemaInfo newCompiledInfo, ref XmlSchemaObjectTable substTable) {
884             //Add schema's compiled tables to the set
885             if (schema.BaseUri != null && schemaLocations[schema.BaseUri] == null) { //Update schemaLocations table
886                 schemaLocations.Add(schema.BaseUri, schema);
887             }
888 
889             foreach (XmlSchemaElement element in schema.Elements.Values) {
890                 if(!AddToTable(elements, element.QualifiedName, element)) {
891                     RemoveSchemaFromGlobalTables(schema);
892                     return false;
893                 }
894                 XmlQualifiedName head = element.SubstitutionGroup;
895                 if (!head.IsEmpty) {
896                     if (substTable == null) {
897                         substTable = new XmlSchemaObjectTable();
898                     }
899                     XmlSchemaSubstitutionGroup substitutionGroup = (XmlSchemaSubstitutionGroup)substTable[head];
900                     if (substitutionGroup == null) {
901                         substitutionGroup = new XmlSchemaSubstitutionGroup();
902                         substitutionGroup.Examplar = head;
903                         substTable.Add(head, substitutionGroup);
904                     }
905                     ArrayList members = substitutionGroup.Members;
906                     if (!members.Contains(element)) { //Members might contain element if the same schema is included and imported through different paths. Imp, hence will be added to set directly
907                         members.Add(element);
908                     }
909                 }
910             }
911             foreach (XmlSchemaAttribute attribute in schema.Attributes.Values) {
912                 if (!AddToTable(attributes, attribute.QualifiedName, attribute)) {
913                     RemoveSchemaFromGlobalTables(schema);
914                     return false;
915                 }
916             }
917             foreach (XmlSchemaType schemaType in schema.SchemaTypes.Values) {
918                 if (!AddToTable(schemaTypes, schemaType.QualifiedName, schemaType)) {
919                     RemoveSchemaFromGlobalTables(schema);
920                     return false;
921                 }
922             }
923             schema.AddCompiledInfo(newCompiledInfo);
924 
925             return true;
926         }
927 #endif
928 
929         //For use by the validator when loading schemaLocations in the instance
Add(String targetNamespace, XmlReader reader, Hashtable validatedNamespaces)930         internal void Add(String targetNamespace, XmlReader reader, Hashtable validatedNamespaces)
931         {
932             if (reader == null)
933             {
934                 throw new ArgumentNullException(nameof(reader));
935             }
936             if (targetNamespace == null)
937             {
938                 targetNamespace = string.Empty;
939             }
940             if (validatedNamespaces[targetNamespace] != null)
941             {
942                 if (FindSchemaByNSAndUrl(new Uri(reader.BaseURI, UriKind.RelativeOrAbsolute), targetNamespace, null) != null)
943                 {
944                     return;
945                 }
946                 else
947                 {
948                     throw new XmlSchemaException(SR.Sch_ComponentAlreadySeenForNS, targetNamespace);
949                 }
950             }
951 
952             //Not locking set as this will not be accessible outside the validator
953             XmlSchema schema;
954             if (IsSchemaLoaded(new Uri(reader.BaseURI, UriKind.RelativeOrAbsolute), targetNamespace, out schema))
955             {
956                 return;
957             }
958             else
959             { //top-level schema not present for same url
960                 schema = ParseSchema(targetNamespace, reader);
961 
962                 //Store the previous locations
963                 DictionaryEntry[] oldLocations = new DictionaryEntry[_schemaLocations.Count];
964                 _schemaLocations.CopyTo(oldLocations, 0);
965 
966                 //Add to set
967                 Add(targetNamespace, schema);
968                 if (schema.ImportedSchemas.Count > 0)
969                 { //Check imports
970                     string tns;
971                     for (int i = 0; i < schema.ImportedSchemas.Count; ++i)
972                     {
973                         XmlSchema impSchema = (XmlSchema)schema.ImportedSchemas[i];
974                         tns = impSchema.TargetNamespace;
975                         if (tns == null)
976                         {
977                             tns = string.Empty;
978                         }
979                         if (validatedNamespaces[tns] != null && (FindSchemaByNSAndUrl(impSchema.BaseUri, tns, oldLocations) == null))
980                         {
981                             RemoveRecursive(schema);
982                             throw new XmlSchemaException(SR.Sch_ComponentAlreadySeenForNS, tns);
983                         }
984                     }
985                 }
986             }
987         }
988 
FindSchemaByNSAndUrl(Uri schemaUri, string ns, DictionaryEntry[] locationsTable)989         internal XmlSchema FindSchemaByNSAndUrl(Uri schemaUri, string ns, DictionaryEntry[] locationsTable)
990         {
991             if (schemaUri == null || schemaUri.OriginalString.Length == 0)
992             {
993                 return null;
994             }
995             XmlSchema schema = null;
996             if (locationsTable == null)
997             {
998                 schema = (XmlSchema)_schemaLocations[schemaUri];
999             }
1000             else
1001             {
1002                 for (int i = 0; i < locationsTable.Length; i++)
1003                 {
1004                     if (schemaUri.Equals(locationsTable[i].Key))
1005                     {
1006                         schema = (XmlSchema)locationsTable[i].Value;
1007                         break;
1008                     }
1009                 }
1010             }
1011             if (schema != null)
1012             {
1013                 Debug.Assert(ns != null);
1014                 string tns = schema.TargetNamespace == null ? string.Empty : schema.TargetNamespace;
1015                 if (tns == ns)
1016                 {
1017                     return schema;
1018                 }
1019                 else if (tns == string.Empty)
1020                 { //There could be a chameleon for same ns
1021                     // It is OK to pass in the schema we have found so far, since it must have the schemaUri we're looking for
1022                     // (we found it that way above) and it must be the original chameleon schema (the one without target ns)
1023                     // as we don't add the chameleon copies into the locations tables above.
1024                     Debug.Assert(schema.BaseUri.Equals(schemaUri));
1025                     ChameleonKey cKey = new ChameleonKey(ns, schema);
1026                     schema = (XmlSchema)_chameleonSchemas[cKey]; //Need not clone if a schema for that namespace already exists
1027                 }
1028                 else
1029                 {
1030                     schema = null;
1031                 }
1032             }
1033             return schema;
1034         }
1035 
SetDtdProcessing(XmlReader reader)1036         private void SetDtdProcessing(XmlReader reader)
1037         {
1038             if (reader.Settings != null)
1039             {
1040                 _readerSettings.DtdProcessing = reader.Settings.DtdProcessing;
1041             }
1042             else
1043             {
1044                 XmlTextReader v1Reader = reader as XmlTextReader;
1045                 if (v1Reader != null)
1046                 {
1047                     _readerSettings.DtdProcessing = v1Reader.DtdProcessing;
1048                 }
1049             }
1050         }
1051 
AddSchemaToSet(XmlSchema schema)1052         private void AddSchemaToSet(XmlSchema schema)
1053         {
1054             // Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
1055             // method. If you change anything here *make sure* to update Reprocess method accordingly.
1056 
1057             _schemas.Add(schema.SchemaId, schema);
1058             //Add to targetNamespaces table
1059 
1060             // This code is copied to method:
1061             // Reprocess(XmlSchema schema)
1062             // If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
1063             #region This code is copied to Reprocess(XmlSchema schema) method
1064 
1065             string tns = GetTargetNamespace(schema);
1066             if (_targetNamespaces[tns] == null)
1067             {
1068                 _targetNamespaces.Add(tns, tns);
1069             }
1070             if (_schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null)
1071             { //it has xs:anyType
1072                 _schemaForSchema = schema;
1073             }
1074             for (int i = 0; i < schema.ImportedSchemas.Count; ++i)
1075             {    //Once preprocessed external schemas property is set
1076                 XmlSchema s = (XmlSchema)schema.ImportedSchemas[i];
1077                 if (!_schemas.ContainsKey(s.SchemaId))
1078                 {
1079                     _schemas.Add(s.SchemaId, s);
1080                 }
1081                 tns = GetTargetNamespace(s);
1082                 if (_targetNamespaces[tns] == null)
1083                 {
1084                     _targetNamespaces.Add(tns, tns);
1085                 }
1086                 if (_schemaForSchema == null && tns == XmlReservedNs.NsXs && schema.SchemaTypes[DatatypeImplementation.QnAnyType] != null)
1087                 { //it has xs:anyType
1088                     _schemaForSchema = schema;
1089                 }
1090             }
1091 
1092             #endregion // This code is copied to Reprocess(XmlSchema schema) method
1093         }
1094 
ProcessNewSubstitutionGroups(XmlSchemaObjectTable substitutionGroupsTable, bool resolve)1095         private void ProcessNewSubstitutionGroups(XmlSchemaObjectTable substitutionGroupsTable, bool resolve)
1096         {
1097             foreach (XmlSchemaSubstitutionGroup substGroup in substitutionGroupsTable.Values)
1098             {
1099                 if (resolve)
1100                 { //Resolve substitutionGroups within this schema
1101                     ResolveSubstitutionGroup(substGroup, substitutionGroupsTable);
1102                 }
1103 
1104                 //Add or Merge new substitutionGroups with those that already exist in the set
1105                 XmlQualifiedName head = substGroup.Examplar;
1106                 XmlSchemaSubstitutionGroup oldSubstGroup = (XmlSchemaSubstitutionGroup)substitutionGroups[head];
1107                 if (oldSubstGroup != null)
1108                 {
1109                     for (int i = 0; i < substGroup.Members.Count; ++i)
1110                     {
1111                         if (!oldSubstGroup.Members.Contains(substGroup.Members[i]))
1112                         {
1113                             oldSubstGroup.Members.Add(substGroup.Members[i]);
1114                         }
1115                     }
1116                 }
1117                 else
1118                 {
1119                     AddToTable(substitutionGroups, head, substGroup);
1120                 }
1121             }
1122         }
1123 
ResolveSubstitutionGroup(XmlSchemaSubstitutionGroup substitutionGroup, XmlSchemaObjectTable substTable)1124         private void ResolveSubstitutionGroup(XmlSchemaSubstitutionGroup substitutionGroup, XmlSchemaObjectTable substTable)
1125         {
1126             List<XmlSchemaElement> newMembers = null;
1127             XmlSchemaElement headElement = (XmlSchemaElement)elements[substitutionGroup.Examplar];
1128             if (substitutionGroup.Members.Contains(headElement))
1129             {// already checked
1130                 return;
1131             }
1132             for (int i = 0; i < substitutionGroup.Members.Count; ++i)
1133             {
1134                 XmlSchemaElement element = (XmlSchemaElement)substitutionGroup.Members[i];
1135 
1136                 //Chain to other head's that are members of this head's substGroup
1137                 XmlSchemaSubstitutionGroup g = (XmlSchemaSubstitutionGroup)substTable[element.QualifiedName];
1138                 if (g != null)
1139                 {
1140                     ResolveSubstitutionGroup(g, substTable);
1141                     for (int j = 0; j < g.Members.Count; ++j)
1142                     {
1143                         XmlSchemaElement element1 = (XmlSchemaElement)g.Members[j];
1144                         if (element1 != element)
1145                         { //Exclude the head
1146                             if (newMembers == null)
1147                             {
1148                                 newMembers = new List<XmlSchemaElement>();
1149                             }
1150                             newMembers.Add(element1);
1151                         }
1152                     }
1153                 }
1154             }
1155             if (newMembers != null)
1156             {
1157                 for (int i = 0; i < newMembers.Count; ++i)
1158                 {
1159                     substitutionGroup.Members.Add(newMembers[i]);
1160                 }
1161             }
1162             substitutionGroup.Members.Add(headElement);
1163         }
1164 
Remove(XmlSchema schema, bool forceCompile)1165         internal XmlSchema Remove(XmlSchema schema, bool forceCompile)
1166         {
1167             // Due to bug 644477 - this method is tightly coupled (THE CODE IS BASICALLY COPIED) to Reprocess
1168             // method. If you change anything here *make sure* to update Reprocess method accordingly.
1169             if (schema == null)
1170             {
1171                 throw new ArgumentNullException(nameof(schema));
1172             }
1173             lock (InternalSyncObject)
1174             { //Need to lock here so that remove cannot be called while the set is being compiled
1175                 if (_schemas.ContainsKey(schema.SchemaId))
1176                 {
1177                     // This code is copied to method:
1178                     // Reprocess(XmlSchema schema)
1179                     // If you changed anything here go and change the same in Reprocess(XmlSchema schema) method
1180                     #region This code is copied to Reprocess(XmlSchema schema) method
1181 
1182                     if (forceCompile)
1183                     {
1184                         RemoveSchemaFromGlobalTables(schema);
1185                         RemoveSchemaFromCaches(schema);
1186                     }
1187                     _schemas.Remove(schema.SchemaId);
1188                     if (schema.BaseUri != null)
1189                     {
1190                         _schemaLocations.Remove(schema.BaseUri);
1191                     }
1192                     string tns = GetTargetNamespace(schema);
1193                     if (Schemas(tns).Count == 0)
1194                     { //This is the only schema for that namespace
1195                         _targetNamespaces.Remove(tns);
1196                     }
1197                     if (forceCompile)
1198                     {
1199                         _isCompiled = false;
1200                         _compileAll = true; //Force compilation of the whole set; This is when the set is not completely thread-safe
1201                     }
1202                     return schema;
1203 
1204                     #endregion // This code is copied to Reprocess(XmlSchema schema) method
1205                 }
1206             }
1207             return null;
1208         }
1209 
ClearTables()1210         private void ClearTables()
1211         {
1212             GlobalElements.Clear();
1213             GlobalAttributes.Clear();
1214             GlobalTypes.Clear();
1215             SubstitutionGroups.Clear();
1216             TypeExtensions.Clear();
1217         }
1218 
PreprocessSchema(ref XmlSchema schema, string targetNamespace)1219         internal bool PreprocessSchema(ref XmlSchema schema, string targetNamespace)
1220         {
1221             Preprocessor prep = new Preprocessor(_nameTable, GetSchemaNames(_nameTable), _eventHandler, _compilationSettings);
1222             prep.XmlResolver = _readerSettings.GetXmlResolver_CheckConfig();
1223             prep.ReaderSettings = _readerSettings;
1224             prep.SchemaLocations = _schemaLocations;
1225             prep.ChameleonSchemas = _chameleonSchemas;
1226             bool hasErrors = prep.Execute(schema, targetNamespace, true);
1227             schema = prep.RootSchema; //For any root level chameleon cloned
1228             return hasErrors;
1229         }
1230 
ParseSchema(string targetNamespace, XmlReader reader)1231         internal XmlSchema ParseSchema(string targetNamespace, XmlReader reader)
1232         {
1233             XmlNameTable readerNameTable = reader.NameTable;
1234             SchemaNames schemaNames = GetSchemaNames(readerNameTable);
1235             Parser parser = new Parser(SchemaType.XSD, readerNameTable, schemaNames, _eventHandler);
1236             parser.XmlResolver = _readerSettings.GetXmlResolver_CheckConfig();
1237             SchemaType schemaType;
1238             try
1239             {
1240                 schemaType = parser.Parse(reader, targetNamespace);
1241             }
1242             catch (XmlSchemaException e)
1243             {
1244                 SendValidationEvent(e, XmlSeverityType.Error);
1245                 return null;
1246             }
1247             return parser.XmlSchema;
1248         }
1249 
CopyFromCompiledSet(XmlSchemaSet otherSet)1250         internal void CopyFromCompiledSet(XmlSchemaSet otherSet)
1251         {
1252             XmlSchema currentSchema;
1253             SortedList copyFromList = otherSet.SortedSchemas;
1254             bool setIsCompiled = _schemas.Count == 0 ? true : false;
1255             ArrayList existingSchemas = new ArrayList();
1256 
1257             SchemaInfo newCompiledInfo = new SchemaInfo();
1258             Uri baseUri;
1259             for (int i = 0; i < copyFromList.Count; i++)
1260             {
1261                 currentSchema = (XmlSchema)copyFromList.GetByIndex(i);
1262                 baseUri = currentSchema.BaseUri;
1263                 if (_schemas.ContainsKey(currentSchema.SchemaId) || (baseUri != null && baseUri.OriginalString.Length != 0 && _schemaLocations[baseUri] != null))
1264                 {
1265                     existingSchemas.Add(currentSchema);
1266                     continue;
1267                 }
1268                 _schemas.Add(currentSchema.SchemaId, currentSchema);
1269                 if (baseUri != null && baseUri.OriginalString.Length != 0)
1270                 {
1271                     _schemaLocations.Add(baseUri, currentSchema);
1272                 }
1273                 string tns = GetTargetNamespace(currentSchema);
1274                 if (_targetNamespaces[tns] == null)
1275                 {
1276                     _targetNamespaces.Add(tns, tns);
1277                 }
1278             }
1279 
1280             VerifyTables();
1281             foreach (XmlSchemaElement element in otherSet.GlobalElements.Values)
1282             {
1283                 if (!AddToTable(elements, element.QualifiedName, element))
1284                 {
1285                     goto RemoveAll;
1286                 }
1287             }
1288             foreach (XmlSchemaAttribute attribute in otherSet.GlobalAttributes.Values)
1289             {
1290                 if (!AddToTable(attributes, attribute.QualifiedName, attribute))
1291                 {
1292                     goto RemoveAll;
1293                 }
1294             }
1295             foreach (XmlSchemaType schemaType in otherSet.GlobalTypes.Values)
1296             {
1297                 if (!AddToTable(schemaTypes, schemaType.QualifiedName, schemaType))
1298                 {
1299                     goto RemoveAll;
1300                 }
1301             }
1302             ProcessNewSubstitutionGroups(otherSet.SubstitutionGroups, false);
1303 
1304             newCompiledInfo.Add(_cachedCompiledInfo, _eventHandler); //Add all the items from the old to the new compiled object
1305             newCompiledInfo.Add(otherSet.CompiledInfo, _eventHandler);
1306             _cachedCompiledInfo = newCompiledInfo; //Replace the compiled info in the set after successful compilation
1307             if (setIsCompiled)
1308             {
1309                 _isCompiled = true;
1310                 _compileAll = false;
1311             }
1312             return;
1313 
1314         RemoveAll:
1315             foreach (XmlSchema schemaToRemove in copyFromList.Values)
1316             {
1317                 if (!existingSchemas.Contains(schemaToRemove))
1318                 {
1319                     Remove(schemaToRemove, false);
1320                 }
1321             }
1322             foreach (XmlSchemaElement elementToRemove in otherSet.GlobalElements.Values)
1323             {
1324                 if (!existingSchemas.Contains((XmlSchema)elementToRemove.Parent))
1325                 {
1326                     elements.Remove(elementToRemove.QualifiedName);
1327                 }
1328             }
1329             foreach (XmlSchemaAttribute attributeToRemove in otherSet.GlobalAttributes.Values)
1330             {
1331                 if (!existingSchemas.Contains((XmlSchema)attributeToRemove.Parent))
1332                 {
1333                     attributes.Remove(attributeToRemove.QualifiedName);
1334                 }
1335             }
1336             foreach (XmlSchemaType schemaTypeToRemove in otherSet.GlobalTypes.Values)
1337             {
1338                 if (!existingSchemas.Contains((XmlSchema)schemaTypeToRemove.Parent))
1339                 {
1340                     schemaTypes.Remove(schemaTypeToRemove.QualifiedName);
1341                 }
1342             }
1343         }
1344 
1345         internal SchemaInfo CompiledInfo
1346         {
1347             get
1348             {
1349                 return _cachedCompiledInfo;
1350             }
1351         }
1352 
1353         internal XmlReaderSettings ReaderSettings
1354         {
1355             get
1356             {
1357                 return _readerSettings;
1358             }
1359         }
1360 
GetResolver()1361         internal XmlResolver GetResolver()
1362         {
1363             return _readerSettings.GetXmlResolver_CheckConfig();
1364         }
1365 
GetEventHandler()1366         internal ValidationEventHandler GetEventHandler()
1367         {
1368             return _eventHandler;
1369         }
1370 
GetSchemaNames(XmlNameTable nt)1371         internal SchemaNames GetSchemaNames(XmlNameTable nt)
1372         {
1373             if (_nameTable != nt)
1374             {
1375                 return new SchemaNames(nt);
1376             }
1377             else
1378             {
1379                 if (_schemaNames == null)
1380                 {
1381                     _schemaNames = new SchemaNames(_nameTable);
1382                 }
1383                 return _schemaNames;
1384             }
1385         }
1386 
IsSchemaLoaded(Uri schemaUri, string targetNamespace, out XmlSchema schema)1387         internal bool IsSchemaLoaded(Uri schemaUri, string targetNamespace, out XmlSchema schema)
1388         {
1389             schema = null;
1390             if (targetNamespace == null)
1391             {
1392                 targetNamespace = string.Empty;
1393             }
1394             if (GetSchemaByUri(schemaUri, out schema))
1395             {
1396                 if (_schemas.ContainsKey(schema.SchemaId) && (targetNamespace.Length == 0 || targetNamespace == schema.TargetNamespace))
1397                 { //schema is present in set
1398                     //Schema found
1399                 }
1400                 else if (schema.TargetNamespace == null)
1401                 { //If schema not in set or namespace doesnt match, then it might be a chameleon
1402                     XmlSchema chameleonSchema = FindSchemaByNSAndUrl(schemaUri, targetNamespace, null);
1403                     if (chameleonSchema != null && _schemas.ContainsKey(chameleonSchema.SchemaId))
1404                     {
1405                         schema = chameleonSchema;
1406                     }
1407                     else
1408                     {
1409                         schema = Add(targetNamespace, schema);
1410                     }
1411                 }
1412                 else if (targetNamespace.Length != 0 && targetNamespace != schema.TargetNamespace)
1413                 {
1414                     SendValidationEvent(new XmlSchemaException(SR.Sch_MismatchTargetNamespaceEx, new string[] { targetNamespace, schema.TargetNamespace }), XmlSeverityType.Error);
1415                     schema = null;
1416                 }
1417                 else
1418                 {
1419                     //If here, schema not present in set but in loc and might be added in loc through an earlier include
1420                     //S.TNS != null && ( tns == null or tns == s.TNS)
1421                     AddSchemaToSet(schema);
1422                 }
1423                 return true; //Schema Found
1424             }
1425             return false;
1426         }
1427 
GetSchemaByUri(Uri schemaUri, out XmlSchema schema)1428         internal bool GetSchemaByUri(Uri schemaUri, out XmlSchema schema)
1429         {
1430             schema = null;
1431             if (schemaUri == null || schemaUri.OriginalString.Length == 0)
1432             {
1433                 return false;
1434             }
1435             schema = (XmlSchema)_schemaLocations[schemaUri];
1436             if (schema != null)
1437             {
1438                 return true;
1439             }
1440             return false;
1441         }
1442 
GetTargetNamespace(XmlSchema schema)1443         internal string GetTargetNamespace(XmlSchema schema)
1444         {
1445             return schema.TargetNamespace == null ? string.Empty : schema.TargetNamespace;
1446         }
1447 
1448 
1449         internal SortedList SortedSchemas
1450         {
1451             get
1452             {
1453                 return _schemas;
1454             }
1455         }
1456 
1457         //Private Methods
RemoveSchemaFromCaches(XmlSchema schema)1458         private void RemoveSchemaFromCaches(XmlSchema schema)
1459         {
1460             //Remove From ChameleonSchemas and schemaLocations cache
1461             List<XmlSchema> reprocessList = new List<XmlSchema>();
1462             schema.GetExternalSchemasList(reprocessList, schema);
1463             for (int i = 0; i < reprocessList.Count; ++i)
1464             { //Remove schema from schemaLocations & chameleonSchemas tables
1465                 if (reprocessList[i].BaseUri != null && reprocessList[i].BaseUri.OriginalString.Length != 0)
1466                 {
1467                     _schemaLocations.Remove(reprocessList[i].BaseUri);
1468                 }
1469                 //Remove from chameleon table
1470                 ICollection chameleonKeys = _chameleonSchemas.Keys;
1471                 ArrayList removalList = new ArrayList();
1472                 foreach (ChameleonKey cKey in chameleonKeys)
1473                 {
1474                     if (cKey.chameleonLocation.Equals(reprocessList[i].BaseUri))
1475                     {
1476                         // The key will have the originalSchema set to null if the location was not empty
1477                         //   otherwise we need to care about it as there may be more chameleon schemas without
1478                         //   a base URI and we want to remove only those which were created as a clone of the one
1479                         //   we're removing.
1480                         if (cKey.originalSchema == null || Ref.ReferenceEquals(cKey.originalSchema, reprocessList[i]))
1481                         {
1482                             removalList.Add(cKey);
1483                         }
1484                     }
1485                 }
1486                 for (int j = 0; j < removalList.Count; ++j)
1487                 {
1488                     _chameleonSchemas.Remove(removalList[j]);
1489                 }
1490             }
1491         }
1492 
RemoveSchemaFromGlobalTables(XmlSchema schema)1493         private void RemoveSchemaFromGlobalTables(XmlSchema schema)
1494         {
1495             if (_schemas.Count == 0)
1496             {
1497                 return;
1498             }
1499             VerifyTables();
1500             foreach (XmlSchemaElement elementToRemove in schema.Elements.Values)
1501             {
1502                 XmlSchemaElement elem = (XmlSchemaElement)elements[elementToRemove.QualifiedName];
1503                 if (elem == elementToRemove)
1504                 {
1505                     elements.Remove(elementToRemove.QualifiedName);
1506                 }
1507             }
1508             foreach (XmlSchemaAttribute attributeToRemove in schema.Attributes.Values)
1509             {
1510                 XmlSchemaAttribute attr = (XmlSchemaAttribute)attributes[attributeToRemove.QualifiedName];
1511                 if (attr == attributeToRemove)
1512                 {
1513                     attributes.Remove(attributeToRemove.QualifiedName);
1514                 }
1515             }
1516             foreach (XmlSchemaType schemaTypeToRemove in schema.SchemaTypes.Values)
1517             {
1518                 XmlSchemaType schemaType = (XmlSchemaType)schemaTypes[schemaTypeToRemove.QualifiedName];
1519                 if (schemaType == schemaTypeToRemove)
1520                 {
1521                     schemaTypes.Remove(schemaTypeToRemove.QualifiedName);
1522                 }
1523             }
1524         }
AddToTable(XmlSchemaObjectTable table, XmlQualifiedName qname, XmlSchemaObject item)1525         private bool AddToTable(XmlSchemaObjectTable table, XmlQualifiedName qname, XmlSchemaObject item)
1526         {
1527             if (qname.Name.Length == 0)
1528             {
1529                 return true;
1530             }
1531             XmlSchemaObject existingObject = (XmlSchemaObject)table[qname];
1532             if (existingObject != null)
1533             {
1534                 if (existingObject == item || existingObject.SourceUri == item.SourceUri)
1535                 {
1536                     return true;
1537                 }
1538                 string code = string.Empty;
1539                 if (item is XmlSchemaComplexType)
1540                 {
1541                     code = SR.Sch_DupComplexType;
1542                 }
1543                 else if (item is XmlSchemaSimpleType)
1544                 {
1545                     code = SR.Sch_DupSimpleType;
1546                 }
1547                 else if (item is XmlSchemaElement)
1548                 {
1549                     code = SR.Sch_DupGlobalElement;
1550                 }
1551                 else if (item is XmlSchemaAttribute)
1552                 {
1553                     if (qname.Namespace == XmlReservedNs.NsXml)
1554                     {
1555                         XmlSchema schemaForXmlNS = Preprocessor.GetBuildInSchema();
1556                         XmlSchemaObject builtInAttribute = schemaForXmlNS.Attributes[qname];
1557                         if (existingObject == builtInAttribute)
1558                         { //replace built-in one
1559                             table.Insert(qname, item);
1560                             return true;
1561                         }
1562                         else if (item == builtInAttribute)
1563                         { //trying to overwrite customer's component with built-in, ignore built-in
1564                             return true;
1565                         }
1566                     }
1567                     code = SR.Sch_DupGlobalAttribute;
1568                 }
1569                 SendValidationEvent(new XmlSchemaException(code, qname.ToString()), XmlSeverityType.Error);
1570                 return false;
1571             }
1572             else
1573             {
1574                 table.Add(qname, item);
1575                 return true;
1576             }
1577         }
1578 
VerifyTables()1579         private void VerifyTables()
1580         {
1581             if (elements == null)
1582             {
1583                 elements = new XmlSchemaObjectTable();
1584             }
1585             if (attributes == null)
1586             {
1587                 attributes = new XmlSchemaObjectTable();
1588             }
1589             if (schemaTypes == null)
1590             {
1591                 schemaTypes = new XmlSchemaObjectTable();
1592             }
1593             if (substitutionGroups == null)
1594             {
1595                 substitutionGroups = new XmlSchemaObjectTable();
1596             }
1597         }
1598 
InternalValidationCallback(object sender, ValidationEventArgs e)1599         private void InternalValidationCallback(object sender, ValidationEventArgs e)
1600         {
1601             if (e.Severity == XmlSeverityType.Error)
1602             {
1603                 throw e.Exception;
1604             }
1605         }
1606 
SendValidationEvent(XmlSchemaException e, XmlSeverityType severity)1607         private void SendValidationEvent(XmlSchemaException e, XmlSeverityType severity)
1608         {
1609             if (_eventHandler != null)
1610             {
1611                 _eventHandler(this, new ValidationEventArgs(e, severity));
1612             }
1613             else
1614             {
1615                 throw e;
1616             }
1617         }
1618     };
1619 }
1620