// 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.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Routing.Abstractions;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Routing
{
///
/// An type for route values.
///
public class RouteValueDictionary : IDictionary, IReadOnlyDictionary
{
internal Storage _storage;
///
/// Creates an empty .
///
public RouteValueDictionary()
{
_storage = EmptyStorage.Instance;
}
///
/// 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 of ,
/// 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 dictionary = values as RouteValueDictionary;
if (dictionary != null)
{
var listStorage = dictionary._storage as ListStorage;
if (listStorage != null)
{
_storage = new ListStorage(listStorage);
return;
}
var propertyStorage = dictionary._storage as PropertyStorage;
if (propertyStorage != null)
{
// PropertyStorage is immutable so we can just copy it.
_storage = dictionary._storage;
return;
}
// If we get here, it's an EmptyStorage.
_storage = EmptyStorage.Instance;
return;
}
var keyValueEnumerable = values as IEnumerable>;
if (keyValueEnumerable != null)
{
var listStorage = new ListStorage();
_storage = listStorage;
foreach (var kvp in keyValueEnumerable)
{
if (listStorage.ContainsKey(kvp.Key))
{
var message = Resources.FormatRouteValueDictionary_DuplicateKey(kvp.Key, nameof(RouteValueDictionary));
throw new ArgumentException(message, nameof(values));
}
listStorage._inner.Add(kvp);
}
return;
}
if (values != null)
{
_storage = new PropertyStorage(values);
return;
}
_storage = EmptyStorage.Instance;
}
///
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));
}
if (!_storage.TrySetValue(key, value))
{
Upgrade();
_storage.TrySetValue(key, value);
}
}
}
///
/// Gets the comparer for this dictionary.
///
///
/// This will always be a reference to
///
public IEqualityComparer Comparer => StringComparer.OrdinalIgnoreCase;
///
public int Count => _storage.Count;
///
bool ICollection>.IsReadOnly => false;
///
public ICollection Keys
{
get
{
Upgrade();
var list = ((ListStorage)_storage)._inner;
var keys = new string[list.Count];
for (var i = 0; i < keys.Length; i++)
{
keys[i] = list[i].Key;
}
return keys;
}
}
IEnumerable IReadOnlyDictionary.Keys
{
get
{
return Keys;
}
}
///
public ICollection