408 lines
12 KiB
C#
408 lines
12 KiB
C#
// 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
|
|
{
|
|
/// <summary>
|
|
/// An <see cref="IDictionary{string, object}"/> type for route values.
|
|
/// </summary>
|
|
public class RouteValueDictionary : IDictionary<string, object>, IReadOnlyDictionary<string, object>
|
|
{
|
|
/// <summary>
|
|
/// Creates an empty <see cref="RouteValueDictionary"/>.
|
|
/// </summary>
|
|
public RouteValueDictionary()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a <see cref="RouteValueDictionary"/> initialized with the specified <paramref name="values"/>.
|
|
/// </summary>
|
|
/// <param name="values">An object to initialize the dictionary. The value can be of type
|
|
/// <see cref="IDictionary{TKey, TValue}"/> or <see cref="IReadOnlyDictionary{TKey, TValue}"/>
|
|
/// or an object with public properties as key-value pairs.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// If the value is a dictionary or other <see cref="IEnumerable{KeyValuePair{string, object}}"/>,
|
|
/// 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.
|
|
/// </remarks>
|
|
public RouteValueDictionary(object values)
|
|
{
|
|
var otherDictionary = values as RouteValueDictionary;
|
|
if (otherDictionary != null)
|
|
{
|
|
if (otherDictionary.InnerDictionary != null)
|
|
{
|
|
InnerDictionary = new Dictionary<string, object>(
|
|
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<KeyValuePair<string, object>>;
|
|
if (keyValuePairCollection != null)
|
|
{
|
|
InnerDictionary = new Dictionary<string, object>(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<string, object> InnerDictionary { get; set; }
|
|
|
|
private PropertyHelper[] Properties { get; }
|
|
|
|
private object Value { get; }
|
|
|
|
/// <inheritdoc />
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the comparer for this dictionary.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This will always be a reference to <see cref="StringComparer.OrdinalIgnoreCase"/>
|
|
/// </remarks>
|
|
public IEqualityComparer<string> Comparer => StringComparer.OrdinalIgnoreCase;
|
|
|
|
/// <inheritdoc />
|
|
public int Count => InnerDictionary?.Count ?? Properties?.Length ?? 0;
|
|
|
|
/// <inheritdoc />
|
|
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
|
|
|
|
/// <inheritdoc />
|
|
public ICollection<string> Keys
|
|
{
|
|
get
|
|
{
|
|
EnsureWritable();
|
|
return InnerDictionary.Keys;
|
|
}
|
|
}
|
|
|
|
IEnumerable<string> IReadOnlyDictionary<string, object>.Keys
|
|
{
|
|
get
|
|
{
|
|
EnsureWritable();
|
|
return InnerDictionary.Keys;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public ICollection<object> Values
|
|
{
|
|
get
|
|
{
|
|
EnsureWritable();
|
|
return InnerDictionary.Values;
|
|
}
|
|
}
|
|
|
|
IEnumerable<object> IReadOnlyDictionary<string, object>.Values
|
|
{
|
|
get
|
|
{
|
|
EnsureWritable();
|
|
return InnerDictionary.Values;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
|
{
|
|
EnsureWritable();
|
|
((ICollection<KeyValuePair<string, object>>)InnerDictionary).Add(item);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Add(string key, object value)
|
|
{
|
|
if (key == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(key));
|
|
}
|
|
|
|
EnsureWritable();
|
|
InnerDictionary.Add(key, value);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Clear()
|
|
{
|
|
EnsureWritable();
|
|
InnerDictionary.Clear();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
|
{
|
|
EnsureWritable();
|
|
return ((ICollection<KeyValuePair<string, object>>)InnerDictionary).Contains(item);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
void ICollection<KeyValuePair<string, object>>.CopyTo(
|
|
KeyValuePair<string, object>[] array,
|
|
int arrayIndex)
|
|
{
|
|
if (array == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(array));
|
|
}
|
|
|
|
EnsureWritable();
|
|
((ICollection<KeyValuePair<string, object>>)InnerDictionary).CopyTo(array, arrayIndex);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Enumerator GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
|
{
|
|
EnsureWritable();
|
|
return ((ICollection<KeyValuePair<string, object>>)InnerDictionary).Remove(item);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool Remove(string key)
|
|
{
|
|
if (key == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(key));
|
|
}
|
|
|
|
EnsureWritable();
|
|
return InnerDictionary.Remove(key);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
else if (InnerDictionary == null)
|
|
{
|
|
InnerDictionary = new Dictionary<string, object>(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<KeyValuePair<string, object>>
|
|
{
|
|
private readonly RouteValueDictionary _dictionary;
|
|
|
|
private int _index;
|
|
private Dictionary<string, object>.Enumerator _enumerator;
|
|
|
|
public Enumerator(RouteValueDictionary dictionary)
|
|
{
|
|
if (dictionary == null)
|
|
{
|
|
throw new ArgumentNullException();
|
|
}
|
|
|
|
_dictionary = dictionary;
|
|
|
|
Current = default(KeyValuePair<string, object>);
|
|
_index = -1;
|
|
_enumerator = _dictionary.InnerDictionary == null ?
|
|
default(Dictionary<string, object>.Enumerator) :
|
|
_dictionary.InnerDictionary.GetEnumerator();
|
|
}
|
|
|
|
public KeyValuePair<string, object> 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<string, object>(property.Name, value);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Current = default(KeyValuePair<string, object>);
|
|
return false;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
Current = default(KeyValuePair<string, object>);
|
|
_index = -1;
|
|
_enumerator = _dictionary?.InnerDictionary == null ?
|
|
default(Dictionary<string, object>.Enumerator) :
|
|
_dictionary.InnerDictionary.GetEnumerator();
|
|
}
|
|
}
|
|
}
|
|
}
|