File: System\Xml\Schema\NamespaceList.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.Collections;
using System.Diagnostics;
using System.Text;
 
namespace System.Xml.Schema
{
    internal class NamespaceList
    {
        public enum ListType
        {
            Any,
            Other,
            Set
        };
 
        private ListType _type = ListType.Any;
        private Hashtable? _set;
        private readonly string? _targetNamespace;
 
        public NamespaceList()
        {
        }
 
        public NamespaceList(string namespaces, string targetNamespace)
        {
            Debug.Assert(targetNamespace != null);
            _targetNamespace = targetNamespace;
            namespaces = namespaces.Trim();
            if (namespaces == "##any" || namespaces.Length == 0)
            {
                _type = ListType.Any;
            }
            else if (namespaces == "##other")
            {
                _type = ListType.Other;
            }
            else
            {
                _type = ListType.Set;
                _set = new Hashtable();
                string[] splitString = XmlConvert.SplitString(namespaces);
                for (int i = 0; i < splitString.Length; ++i)
                {
                    if (splitString[i] == "##local")
                    {
                        _set[string.Empty] = string.Empty;
                    }
                    else if (splitString[i] == "##targetNamespace")
                    {
                        _set[targetNamespace] = targetNamespace;
                    }
                    else
                    {
                        XmlConvert.ToUri(splitString[i]); // can throw
                        _set[splitString[i]] = splitString[i];
                    }
                }
            }
        }
 
        public NamespaceList Clone()
        {
            NamespaceList nsl = (NamespaceList)MemberwiseClone();
            if (_type == ListType.Set)
            {
                Debug.Assert(_set != null);
                nsl._set = (Hashtable)(_set.Clone());
            }
            return nsl;
        }
 
        public ListType Type
        {
            get { return _type; }
        }
 
        public string? Excluded
        {
            get { return _targetNamespace; }
        }
 
        public ICollection Enumerate
        {
            get
            {
                switch (_type)
                {
                    case ListType.Set:
                        return _set!.Keys;
                    case ListType.Other:
                    case ListType.Any:
                    default:
                        throw new InvalidOperationException();
                }
            }
        }
 
        public virtual bool Allows(string ns)
        {
            switch (_type)
            {
                case ListType.Any:
                    return true;
                case ListType.Other:
                    return ns != _targetNamespace && ns.Length != 0;
                case ListType.Set:
                    return _set![ns] != null;
            }
            Debug.Fail($"Unexpected type {_type}");
            return false;
        }
 
        public bool Allows(XmlQualifiedName qname)
        {
            return Allows(qname.Namespace);
        }
 
        public override string ToString()
        {
            switch (_type)
            {
                case ListType.Any:
                    return "##any";
                case ListType.Other:
                    return "##other";
                case ListType.Set:
                    StringBuilder sb = new StringBuilder();
                    bool first = true;
                    foreach (string? s in _set!.Keys)
                    {
                        if (first)
                        {
                            first = false;
                        }
                        else
                        {
                            sb.Append(' ');
                        }
                        if (s == _targetNamespace)
                        {
                            sb.Append("##targetNamespace");
                        }
                        else if (s!.Length == 0)
                        {
                            sb.Append("##local");
                        }
                        else
                        {
                            sb.Append(s);
                        }
                    }
                    return sb.ToString();
            }
            Debug.Fail($"Unexpected type {_type}");
            return string.Empty;
        }
 
        public static bool IsSubset(NamespaceList sub, NamespaceList super)
        {
            if (super._type == ListType.Any)
            {
                return true;
            }
            else if (sub._type == ListType.Other && super._type == ListType.Other)
            {
                return super._targetNamespace == sub._targetNamespace;
            }
            else if (sub._type == ListType.Set)
            {
                if (super._type == ListType.Other)
                {
                    return !sub._set!.Contains(super._targetNamespace!);
                }
                else
                {
                    Debug.Assert(super._type == ListType.Set);
                    foreach (string? ns in sub._set!.Keys)
                    {
                        if (!super._set!.Contains(ns!))
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }
            return false;
        }
 
 
        public static NamespaceList? Union(NamespaceList o1, NamespaceList o2, bool v1Compat)
        {
            NamespaceList? nslist = null;
            Debug.Assert(o1 != o2);
            if (o1._type == ListType.Any)
            { //clause 2 - o1 is Any
                nslist = new NamespaceList();
            }
            else if (o2._type == ListType.Any)
            { //clause 2 - o2 is Any
                nslist = new NamespaceList();
            }
            else if (o1._type == ListType.Set && o2._type == ListType.Set)
            { //clause 3 , both are sets
                nslist = o1.Clone();
                foreach (string? ns in o2._set!.Keys)
                {
                    nslist._set![ns!] = ns;
                }
            }
            else if (o1._type == ListType.Other && o2._type == ListType.Other)
            { //clause 4, both are negations
                if (o1._targetNamespace == o2._targetNamespace)
                { //negation of same value
                    nslist = o1.Clone();
                }
                else
                { //Not a breaking change, going from not expressible to not(absent)
                    nslist = new NamespaceList("##other", string.Empty); //clause 4, negations of different values, result is not(absent)
                }
            }
            else if (o1._type == ListType.Set && o2._type == ListType.Other)
            {
                if (v1Compat)
                {
                    if (o1._set!.Contains(o2._targetNamespace!))
                    {
                        nslist = new NamespaceList();
                    }
                    else
                    { //This was not there originally in V1, added for consistency since its not breaking
                        nslist = o2.Clone();
                    }
                }
                else
                {
                    if (o2._targetNamespace != string.Empty)
                    { //clause 5, o1 is set S, o2 is not(tns)
                        nslist = o1.CompareSetToOther(o2);
                    }
                    else if (o1._set!.Contains(string.Empty))
                    { //clause 6.1 - set S includes absent, o2 is not(absent)
                        nslist = new NamespaceList();
                    }
                    else
                    { //clause 6.2 - set S does not include absent, result is not(absent)
                        nslist = new NamespaceList("##other", string.Empty);
                    }
                }
            }
            else if (o2._type == ListType.Set && o1._type == ListType.Other)
            {
                if (v1Compat)
                {
                    if (o2._set!.Contains(o2._targetNamespace!))
                    {
                        nslist = new NamespaceList();
                    }
                    else
                    {
                        nslist = o1.Clone();
                    }
                }
                else
                { //New rules
                    if (o1._targetNamespace != string.Empty)
                    { //clause 5, o1 is set S, o2 is not(tns)
                        nslist = o2.CompareSetToOther(o1);
                    }
                    else if (o2._set!.Contains(string.Empty))
                    { //clause 6.1 - set S includes absent, o2 is not(absent)
                        nslist = new NamespaceList();
                    }
                    else
                    { //clause 6.2 - set S does not include absent, result is not(absent)
                        nslist = new NamespaceList("##other", string.Empty);
                    }
                }
            }
            return nslist;
        }
 
        private NamespaceList? CompareSetToOther(NamespaceList other)
        {
            //clause 5.1
            NamespaceList? nslist;
            if (_set!.Contains(other._targetNamespace!))
            { //S contains negated ns
                if (_set.Contains(string.Empty))
                { // AND S contains absent
                    nslist = new NamespaceList(); //any is the result
                }
                else
                { //clause 5.2
                    nslist = new NamespaceList("##other", string.Empty);
                }
            }
            else if (_set.Contains(string.Empty))
            { //clause 5.3 - Not expressible
                nslist = null;
            }
            else
            { //clause 5.4 - Set S does not contain negated ns or absent
                nslist = other.Clone();
            }
            return nslist;
        }
 
        public static NamespaceList? Intersection(NamespaceList o1, NamespaceList o2, bool v1Compat)
        {
            NamespaceList? nslist = null;
            Debug.Assert(o1 != o2); //clause 1
            if (o1._type == ListType.Any)
            { //clause 2 - o1 is any
                nslist = o2.Clone();
            }
            else if (o2._type == ListType.Any)
            { //clause 2 - o2 is any
                nslist = o1.Clone();
            }
            else if (o1._type == ListType.Set && o2._type == ListType.Other)
            { //Clause 3 o2 is other
                nslist = o1.Clone();
                nslist.RemoveNamespace(o2._targetNamespace!);
                if (!v1Compat)
                {
                    nslist.RemoveNamespace(string.Empty); //remove ##local
                }
            }
            else if (o1._type == ListType.Other && o2._type == ListType.Set)
            { //Clause 3 o1 is other
                nslist = o2.Clone();
                nslist.RemoveNamespace(o1._targetNamespace!);
                if (!v1Compat)
                {
                    nslist.RemoveNamespace(string.Empty); //remove ##local
                }
            }
            else if (o1._type == ListType.Set && o2._type == ListType.Set)
            { //clause 4
                nslist = new NamespaceList();
                nslist._type = ListType.Set;
                nslist._set = new Hashtable();
                foreach (string? ns in o1._set!.Keys)
                {
                    if (o2._set!.Contains(ns!))
                    {
                        nslist._set.Add(ns!, ns);
                    }
                }
            }
            else if (o1._type == ListType.Other && o2._type == ListType.Other)
            {
                if (o1._targetNamespace == o2._targetNamespace)
                { //negation of same namespace name
                    nslist = o1.Clone();
                    return nslist;
                }
                if (!v1Compat)
                {
                    if (o1._targetNamespace == string.Empty)
                    { // clause 6 - o1 is negation of absent
                        nslist = o2.Clone();
                    }
                    else if (o2._targetNamespace == string.Empty)
                    { //clause 6 - o1 is negation of absent
                        nslist = o1.Clone();
                    }
                }
                //if it comes here, its not expressible //clause 5
            }
            return nslist;
        }
 
        private void RemoveNamespace(string tns)
        {
            if (_set![tns] != null)
            {
                _set.Remove(tns);
            }
        }
    };
 
    internal sealed class NamespaceListV1Compat : NamespaceList
    {
        public NamespaceListV1Compat(string namespaces, string targetNamespace) : base(namespaces, targetNamespace) { }
 
        public override bool Allows(string ns)
        {
            if (this.Type == ListType.Other)
            {
                return ns != Excluded;
            }
            else
            {
                return base.Allows(ns);
            }
        }
    }
}