File: System\Xaml\NameScope.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Xaml\System.Xaml.csproj (System.Xaml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
// Used to store mapping information for names occuring
// within the logical tree section.
 
using System.Collections;
using System.Collections.Specialized;
using System.Windows.Markup;
 
namespace System.Xaml
{
    /// <summary>
    /// Used to store mapping information for names occuring
    /// within the logical tree section.
    /// </summary>
    internal class NameScope : INameScopeDictionary
    {
        /// <summary>
        /// Register Name-Object Map
        /// </summary>
        /// <param name="name">name to be registered</param>
        /// <param name="scopedElement">object mapped to name</param>
        public void RegisterName(string name, object scopedElement)
        {
            ArgumentNullException.ThrowIfNull(name);
            ArgumentNullException.ThrowIfNull(scopedElement);
 
            if (name.Length == 0)
            {
                throw new ArgumentException(SR.NameScopeNameNotEmptyString);
            }
 
            if (!NameValidationHelper.IsValidIdentifierName(name))
            {
                throw new ArgumentException(SR.Format(SR.NameScopeInvalidIdentifierName, name));
            }
 
            if (_nameMap is null)
            {
                _nameMap = new HybridDictionary();
                _nameMap[name] = scopedElement;
            }
            else
            {
                object nameContext = _nameMap[name];
                // first time adding the Name, set it
                if (nameContext is null)
                {
                    _nameMap[name] = scopedElement;
                }
                else if (scopedElement != nameContext)
                {
                    throw new ArgumentException(SR.Format(SR.NameScopeDuplicateNamesNotAllowed, name));
                }
            }
        }
 
        /// <summary>
        /// Unregister Name-Object Map
        /// </summary>
        /// <param name="name">name to be registered</param>
        public void UnregisterName(string name)
        {
            ArgumentNullException.ThrowIfNull(name);
 
            if (name.Length == 0)
            {
                throw new ArgumentException(SR.NameScopeNameNotEmptyString);
            }
 
            if (_nameMap?[name] is null)
            {
                throw new ArgumentException(SR.Format(SR.NameScopeNameNotFound, name));
            }
 
            _nameMap.Remove(name);
        }
 
        /// <summary>
        /// Find - Find the corresponding object given a Name
        /// </summary>
        /// <param name="name">Name for which context needs to be retrieved</param>
        /// <returns>corresponding Context if found, else null</returns>
        public object FindName(string name)
        {
            if (_nameMap is null || string.IsNullOrEmpty(name))
            {
                return null;
            }
 
            return _nameMap[name];
        }
 
        // This is a HybridDictionary of Name-Object maps
        private HybridDictionary _nameMap;
 
        IEnumerator<KeyValuePair<string, object>> GetEnumerator() => new Enumerator(_nameMap);
 
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
        IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() => GetEnumerator();
 
        public int Count => _nameMap?.Count ?? 0;
 
        public bool IsReadOnly => false;
 
        public void Clear() => _nameMap = null;
 
        public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
        {
            if (_nameMap is null)
            {
                array = null;
                return;
            }
 
            foreach (DictionaryEntry entry in _nameMap)
            {
                array[arrayIndex++] = new KeyValuePair<string, object>((string)entry.Key, entry.Value);
            }
        }
 
        public bool Remove(KeyValuePair<string, object> item)
        {
            if (!Contains(item))
            {
                return false;
            }
 
            if (item.Value != this[item.Key])
            {
                return false;
            }
            return Remove(item.Key);
        }
 
        public void Add(KeyValuePair<string, object> item)
        {
            if (item.Key is null)
            {
                throw new ArgumentException(SR.Format(SR.ReferenceIsNull, "item.Key"), nameof(item));
            }
            if (item.Value is null)
            {
                throw new ArgumentException(SR.Format(SR.ReferenceIsNull, "item.Value"), nameof(item));
            }
 
            Add(item.Key, item.Value);
        }
 
        public bool Contains(KeyValuePair<string, object> item)
        {
            if (item.Key is null)
            {
                throw new ArgumentException(SR.Format(SR.ReferenceIsNull, "item.Key"), nameof(item));
            }
            return ContainsKey(item.Key);
        }
 
        public object this[string key]
        {
            get
            {
                ArgumentNullException.ThrowIfNull(key);
                return FindName(key);
            }
            set
            {
                ArgumentNullException.ThrowIfNull(key);
                ArgumentNullException.ThrowIfNull(value);
 
                RegisterName(key, value);
            }
        }
 
        public void Add(string key, object value)
        {
            ArgumentNullException.ThrowIfNull(key);
 
            RegisterName(key, value);
        }
 
        public bool ContainsKey(string key)
        {
            ArgumentNullException.ThrowIfNull(key);
 
            object value = FindName(key);
            return value is not null;
        }
 
        public bool Remove(string key)
        {
            if (!ContainsKey(key))
            {
                return false;
            }
 
            UnregisterName(key);
            return true;
        }
 
        public bool TryGetValue(string key, out object value)
        {
            if (!ContainsKey(key))
            {
                value = null;
                return false;
            }
 
            value = FindName(key);
            return true;
        }
 
        public ICollection<string> Keys
        {
            get
            {
                if (_nameMap is null)
                {
                    return null;
                }
 
                var list = new List<string>(_nameMap.Keys.Count);
                foreach (string key in _nameMap.Keys)
                {
                    list.Add(key);
                }
                return list;
            }
        }
 
        public ICollection<object> Values
        {
            get
            {
                if (_nameMap is null)
                {
                    return null;
                }
 
                var list = new List<object>(_nameMap.Values.Count);
                foreach (object value in _nameMap.Values)
                {
                    list.Add(value);
                }
                return list;
            }
        }
 
        private class Enumerator : IEnumerator<KeyValuePair<string, object>>
        {
            private IDictionaryEnumerator _enumerator;
 
            public Enumerator(HybridDictionary nameMap)
            {
                _enumerator = nameMap?.GetEnumerator();
            }
 
            public void Dispose() => GC.SuppressFinalize(this);
 
            public KeyValuePair<string, object> Current
            {
                get
                {
                    if (_enumerator is null)
                    {
                        return default(KeyValuePair<string, object>);
                    }
 
                    return new KeyValuePair<string, object>((string)_enumerator.Key, _enumerator.Value);
                }
            }
 
            public bool MoveNext() => _enumerator?.MoveNext() ?? false;
 
            object IEnumerator.Current => Current;
 
            void IEnumerator.Reset() => _enumerator?.Reset();
        }
    }
}