| File: System\Xml\Schema\NamespaceList.cs | Web Access |
| Project: src\runtime\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); } } } }