File: System\Xml\Schema\XmlSchemaCollection.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using System.Threading;
using System.Xml.Schema;
 
namespace System.Xml.Schema
{
    /// <summary>
    /// The XmlSchemaCollection contains a set of namespace URI's.
    /// Each namespace also have an associated private data cache
    /// corresponding to the XML-Data Schema or W3C XML Schema.
    /// The XmlSchemaCollection will able to load XSD and XDR schemas,
    /// and compile them into an internal "cooked schema representation".
    /// The Validate method then uses this internal representation for
    /// efficient runtime validation of any given subtree.
    /// </summary>
    [Obsolete("XmlSchemaCollection has been deprecated. Use System.Xml.Schema.XmlSchemaSet for schema compilation and validation instead.")]
    public sealed class XmlSchemaCollection : ICollection
    {
        private readonly Hashtable _collection;
        private readonly XmlNameTable _nameTable;
        private SchemaNames? _schemaNames;
        private readonly object? _wLock;
        private readonly bool _isThreadSafe = true;
        private ValidationEventHandler? _validationEventHandler;
        private XmlResolver? _xmlResolver;
 
 
        /// <summary>
        /// Construct a new empty schema collection.
        /// </summary>
        public XmlSchemaCollection() : this(new NameTable())
        {
        }
 
        /// <summary>
        /// Construct a new empty schema collection with associated XmlNameTable.
        /// The XmlNameTable is used when loading schemas.
        /// </summary>
        public XmlSchemaCollection(XmlNameTable nametable)
        {
            ArgumentNullException.ThrowIfNull(nametable);
 
            _nameTable = nametable;
            _collection = Hashtable.Synchronized(new Hashtable());
            _xmlResolver = null;
            _isThreadSafe = true;
            if (_isThreadSafe)
            {
                _wLock = new object();
            }
        }
 
        /// <summary>
        /// Returns the number of namespaces defined in this collection
        /// (whether or not there is an actual schema associated with those namespaces or not).
        /// </summary>
        public int Count
        {
            get { return _collection.Count; }
        }
 
        /// <summary>
        /// The default XmlNameTable used by the XmlSchemaCollection when loading new schemas.
        /// </summary>
        public XmlNameTable NameTable
        {
            get { return _nameTable; }
        }
 
        public event ValidationEventHandler ValidationEventHandler
        {
            add { _validationEventHandler += value; }
            remove { _validationEventHandler -= value; }
        }
 
        internal XmlResolver? XmlResolver
        {
            set
            {
                _xmlResolver = value;
            }
        }
 
 
        /// <summary>
        /// Add the schema located by the given URL into the schema collection.
        /// If the given schema references other namespaces, the schemas for those other
        /// namespaces are NOT automatically loaded.
        /// </summary>
        public XmlSchema? Add(string? ns, [StringSyntax(StringSyntaxAttribute.Uri)] string uri)
        {
            if (string.IsNullOrEmpty(uri))
                throw new ArgumentNullException(nameof(uri));
            XmlTextReader reader = new XmlTextReader(uri, _nameTable);
            reader.XmlResolver = _xmlResolver;
 
            XmlSchema? schema = null;
            try
            {
                schema = Add(ns, reader, _xmlResolver);
                while (reader.Read()) ; // wellformness check
            }
            finally
            {
                reader.Close();
            }
            return schema;
        }
 
        public XmlSchema? Add(string? ns, XmlReader reader)
        {
            return Add(ns, reader, _xmlResolver);
        }
 
        /// <summary>
        /// Add the given schema into the schema collection.
        /// If the given schema references other namespaces, the schemas for those
        /// other namespaces are NOT automatically loaded.
        /// </summary>
        public XmlSchema? Add(string? ns, XmlReader reader, XmlResolver? resolver)
        {
            ArgumentNullException.ThrowIfNull(reader);
 
            XmlNameTable readerNameTable = reader.NameTable;
            SchemaInfo schemaInfo = new SchemaInfo();
 
            Parser parser = new Parser(SchemaType.None, readerNameTable, GetSchemaNames(readerNameTable), _validationEventHandler);
            parser.XmlResolver = resolver;
            SchemaType schemaType;
            try
            {
                schemaType = parser.Parse(reader, ns);
            }
            catch (XmlSchemaException e)
            {
                SendValidationEvent(e);
                return null;
            }
 
            if (schemaType == SchemaType.XSD)
            {
                schemaInfo.SchemaType = SchemaType.XSD;
                return Add(ns, schemaInfo, parser.XmlSchema, true, resolver);
            }
            else
            {
                return Add(ns, parser.XdrSchema!, null, true, resolver);
            }
        }
 
        public XmlSchema? Add(XmlSchema schema)
        {
            return Add(schema, _xmlResolver);
        }
 
        public XmlSchema? Add(XmlSchema schema, XmlResolver? resolver)
        {
            ArgumentNullException.ThrowIfNull(schema);
 
            SchemaInfo schemaInfo = new SchemaInfo();
            schemaInfo.SchemaType = SchemaType.XSD;
            return Add(schema.TargetNamespace, schemaInfo, schema, true, resolver);
        }
 
        /// <summary>
        /// Adds all the namespaces defined in the given collection
        /// (including their associated schemas) to this collection.
        /// </summary>
        public void Add(XmlSchemaCollection schema)
        {
            ArgumentNullException.ThrowIfNull(schema);
 
            if (this == schema)
                return;
            IDictionaryEnumerator enumerator = schema._collection.GetEnumerator();
            while (enumerator.MoveNext())
            {
                XmlSchemaCollectionNode node = (XmlSchemaCollectionNode)enumerator.Value!;
                Add(node!.NamespaceURI!, node);
            }
        }
 
 
        /// <summary>
        /// Looks up the schema by its associated namespace URI
        /// </summary>
        public XmlSchema? this[string? ns]
        {
            get
            {
                XmlSchemaCollectionNode? node = (XmlSchemaCollectionNode?)_collection[ns ?? string.Empty];
                return node?.Schema;
            }
        }
 
        public bool Contains(XmlSchema schema)
        {
            ArgumentNullException.ThrowIfNull(schema);
 
            return this[schema.TargetNamespace] != null;
        }
 
        public bool Contains(string? ns)
        {
            return _collection[ns ?? string.Empty] != null;
        }
 
        /// <summary>
        /// Get a IEnumerator of the XmlSchemaCollection.
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new XmlSchemaCollectionEnumerator(_collection);
        }
 
        public XmlSchemaCollectionEnumerator GetEnumerator()
        {
            return new XmlSchemaCollectionEnumerator(_collection);
        }
 
        void ICollection.CopyTo(Array array, int index)
        {
            ArgumentNullException.ThrowIfNull(array);
            ArgumentOutOfRangeException.ThrowIfNegative(index);
 
            for (XmlSchemaCollectionEnumerator e = this.GetEnumerator(); e.MoveNext();)
            {
                ArgumentOutOfRangeException.ThrowIfEqual(index, array.Length);
                array.SetValue(e.Current, index++);
            }
        }
 
        public void CopyTo(XmlSchema[] array, int index)
        {
            ArgumentNullException.ThrowIfNull(array);
            ArgumentOutOfRangeException.ThrowIfNegative(index);
 
            for (XmlSchemaCollectionEnumerator e = this.GetEnumerator(); e.MoveNext();)
            {
                XmlSchema? schema = e.Current;
                if (schema != null)
                {
                    ArgumentOutOfRangeException.ThrowIfEqual(index, array.Length);
                    array[index++] = e.Current!;
                }
            }
        }
 
        bool ICollection.IsSynchronized
        {
            get { return true; }
        }
 
        object ICollection.SyncRoot
        {
            get { return this; }
        }
 
        int ICollection.Count
        {
            get { return _collection.Count; }
        }
 
        internal SchemaInfo? GetSchemaInfo(string? ns)
        {
            XmlSchemaCollectionNode? node = (XmlSchemaCollectionNode?)_collection[ns ?? string.Empty];
            return node?.SchemaInfo;
        }
 
        internal SchemaNames GetSchemaNames(XmlNameTable nt)
        {
            if (_nameTable != nt)
            {
                return new SchemaNames(nt);
            }
            else
            {
                return _schemaNames ??= new SchemaNames(_nameTable);
            }
        }
 
        internal XmlSchema? Add(string? ns, SchemaInfo schemaInfo, XmlSchema? schema, bool compile)
        {
            return Add(ns, schemaInfo, schema, compile, _xmlResolver);
        }
 
        private XmlSchema? Add(string? ns, SchemaInfo schemaInfo, XmlSchema? schema, bool compile, XmlResolver? resolver)
        {
            int errorCount = 0;
            if (schema != null)
            {
                if (schema.ErrorCount == 0 && compile)
                {
                    if (!schema.CompileSchema(this, resolver, schemaInfo, ns, _validationEventHandler, _nameTable, true))
                    {
                        errorCount = 1;
                    }
 
                    ns = schema.TargetNamespace ?? string.Empty;
                }
                errorCount += schema.ErrorCount;
            }
            else
            {
                errorCount += schemaInfo.ErrorCount;
                //ns = ns == null? string.Empty : NameTable.Add(ns);
                ns = NameTable.Add(ns!); //Added without checking for ns == null, since XDR cannot have null namespace
            }
            if (errorCount == 0)
            {
                XmlSchemaCollectionNode node = new XmlSchemaCollectionNode();
                node.NamespaceURI = ns;
                node.SchemaInfo = schemaInfo;
                node.Schema = schema;
                Add(ns!, node);
                return schema;
            }
 
            return null;
        }
 
        private void AddNonThreadSafe(string ns, XmlSchemaCollectionNode? node)
        {
            if (_collection[ns] != null)
                _collection.Remove(ns);
            _collection.Add(ns, node);
        }
 
        private void Add(string ns, XmlSchemaCollectionNode? node)
        {
            if (_isThreadSafe)
            {
                lock (_wLock!)
                {
                    AddNonThreadSafe(ns, node);
                }
            }
            else
            {
                AddNonThreadSafe(ns, node);
            }
        }
 
        private void SendValidationEvent(XmlSchemaException e)
        {
            if (_validationEventHandler != null)
            {
                _validationEventHandler(this, new ValidationEventArgs(e));
            }
            else
            {
                throw e;
            }
        }
 
        internal ValidationEventHandler? EventHandler
        {
            get
            {
                return _validationEventHandler;
            }
            set
            {
                _validationEventHandler = value;
            }
        }
    };
 
    internal sealed class XmlSchemaCollectionNode
    {
        private string? _namespaceUri;
        private SchemaInfo? _schemaInfo;
        private XmlSchema? _schema;
 
        internal string? NamespaceURI
        {
            get { return _namespaceUri; }
            set { _namespaceUri = value; }
        }
 
        internal SchemaInfo? SchemaInfo
        {
            get { return _schemaInfo; }
            set { _schemaInfo = value; }
        }
 
        internal XmlSchema? Schema
        {
            get { return _schema; }
            set { _schema = value; }
        }
    }
 
    public sealed class XmlSchemaCollectionEnumerator : IEnumerator
    {
        private readonly IDictionaryEnumerator _enumerator;
 
        internal XmlSchemaCollectionEnumerator(Hashtable collection)
        {
            _enumerator = collection.GetEnumerator();
        }
 
        void IEnumerator.Reset()
        {
            _enumerator.Reset();
        }
 
        bool IEnumerator.MoveNext()
        {
            return _enumerator.MoveNext();
        }
 
        public bool MoveNext()
        {
            return _enumerator.MoveNext();
        }
 
        object? IEnumerator.Current
        {
            get { return this.Current; }
        }
 
        public XmlSchema? Current
        {
            get
            {
                XmlSchemaCollectionNode? n = (XmlSchemaCollectionNode?)_enumerator.Value;
                if (n != null)
                    return n.Schema;
                else
                    return null;
            }
        }
 
        internal XmlSchemaCollectionNode? CurrentNode
        {
            get
            {
                XmlSchemaCollectionNode? n = (XmlSchemaCollectionNode?)_enumerator.Value;
                return n;
            }
        }
    }
}