// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Routing { /// /// An type for route values. /// public class RouteValueDictionary : IDictionary, IReadOnlyDictionary { /// /// Creates an empty . /// public RouteValueDictionary() { } /// /// Creates a initialized with the specified . /// /// An object to initialize the dictionary. The value can be of type /// or /// or an object with public properties as key-value pairs. /// /// /// If the value is a dictionary or other , /// then its entries are copied. Otherwise the object is interpreted as a set of key-value pairs where the /// property names are keys, and property values are the values, and copied into the dictionary. /// Only public instance non-index properties are considered. /// public RouteValueDictionary(object values) { var otherDictionary = values as RouteValueDictionary; if (otherDictionary != null) { if (otherDictionary.InnerDictionary != null) { InnerDictionary = new Dictionary( otherDictionary.InnerDictionary.Count, StringComparer.OrdinalIgnoreCase); foreach (var kvp in otherDictionary.InnerDictionary) { InnerDictionary[kvp.Key] = kvp.Value; } return; } else if (otherDictionary.Properties != null) { Properties = otherDictionary.Properties; Value = otherDictionary.Value; return; } else { return; } } var keyValuePairCollection = values as IEnumerable>; if (keyValuePairCollection != null) { InnerDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var kvp in keyValuePairCollection) { InnerDictionary[kvp.Key] = kvp.Value; } return; } if (values != null) { Properties = PropertyHelper.GetVisibleProperties(values); Value = values; return; } } private Dictionary InnerDictionary { get; set; } private PropertyHelper[] Properties { get; } private object Value { get; } /// public object this[string key] { get { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } object value; TryGetValue(key, out value); return value; } set { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } EnsureWritable(); InnerDictionary[key] = value; } } /// /// Gets the comparer for this dictionary. /// /// /// This will always be a reference to /// public IEqualityComparer Comparer => StringComparer.OrdinalIgnoreCase; /// public int Count => InnerDictionary?.Count ?? Properties?.Length ?? 0; /// bool ICollection>.IsReadOnly => false; /// public ICollection Keys { get { EnsureWritable(); return InnerDictionary.Keys; } } IEnumerable IReadOnlyDictionary.Keys { get { EnsureWritable(); return InnerDictionary.Keys; } } /// public ICollection Values { get { EnsureWritable(); return InnerDictionary.Values; } } IEnumerable IReadOnlyDictionary.Values { get { EnsureWritable(); return InnerDictionary.Values; } } /// void ICollection>.Add(KeyValuePair item) { EnsureWritable(); ((ICollection>)InnerDictionary).Add(item); } /// public void Add(string key, object value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } EnsureWritable(); InnerDictionary.Add(key, value); } /// public void Clear() { EnsureWritable(); InnerDictionary.Clear(); } /// bool ICollection>.Contains(KeyValuePair item) { EnsureWritable(); return ((ICollection>)InnerDictionary).Contains(item); } /// public bool ContainsKey(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (InnerDictionary != null) { return InnerDictionary.ContainsKey(key); } else if (Properties != null) { for (var i = 0; i < Properties.Length; i++) { var property = Properties[i]; if (Comparer.Equals(property.Name, key)) { return true; } } return false; } else { return false; } } /// void ICollection>.CopyTo( KeyValuePair[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException(nameof(array)); } EnsureWritable(); ((ICollection>)InnerDictionary).CopyTo(array, arrayIndex); } /// public Enumerator GetEnumerator() { return new Enumerator(this); } /// IEnumerator> IEnumerable>.GetEnumerator() { return GetEnumerator(); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// bool ICollection>.Remove(KeyValuePair item) { EnsureWritable(); return ((ICollection>)InnerDictionary).Remove(item); } /// public bool Remove(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } EnsureWritable(); return InnerDictionary.Remove(key); } /// public bool TryGetValue(string key, out object value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (InnerDictionary != null) { return InnerDictionary.TryGetValue(key, out value); } else if (Properties != null) { for (var i = 0; i < Properties.Length; i++) { var property = Properties[i]; if (Comparer.Equals(property.Name, key)) { value = property.ValueGetter(Value); return true; } } value = null; return false; } else { value = null; return false; } } private void EnsureWritable() { if (InnerDictionary == null && Properties == null) { InnerDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); } else if (InnerDictionary == null) { InnerDictionary = new Dictionary(Properties.Length, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < Properties.Length; i++) { var property = Properties[i]; InnerDictionary.Add(property.Property.Name, property.ValueGetter(Value)); } } } public struct Enumerator : IEnumerator> { private readonly RouteValueDictionary _dictionary; private int _index; private Dictionary.Enumerator _enumerator; public Enumerator(RouteValueDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException(); } _dictionary = dictionary; Current = default(KeyValuePair); _index = -1; _enumerator = _dictionary.InnerDictionary == null ? default(Dictionary.Enumerator) : _dictionary.InnerDictionary.GetEnumerator(); } public KeyValuePair Current { get; private set; } object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (_dictionary?.InnerDictionary != null) { if (_enumerator.MoveNext()) { Current = _enumerator.Current; return true; } } else if (_dictionary?.Properties != null) { _index++; if (_index < _dictionary.Properties.Length) { var property = _dictionary.Properties[_index]; var value = property.ValueGetter(_dictionary.Value); Current = new KeyValuePair(property.Name, value); return true; } } Current = default(KeyValuePair); return false; } public void Reset() { Current = default(KeyValuePair); _index = -1; _enumerator = _dictionary?.InnerDictionary == null ? default(Dictionary.Enumerator) : _dictionary.InnerDictionary.GetEnumerator(); } } } }