// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Linq; using System.Web.Http.Properties; namespace System.Web.Http { /// /// Extension methods for . /// [EditorBrowsable(EditorBrowsableState.Never)] internal static class DictionaryExtensions { /// /// Gets the value of associated with the specified key or default value if /// either the key is not present or the value is not of type . /// /// The type of the value associated with the specified key. /// The instance where TValue is object. /// The key whose value to get. /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. /// true if key was found, value is non-null, and value is of type ; otherwise false. public static bool TryGetValue(this IDictionary collection, string key, out T value) { if (collection == null) { throw Error.ArgumentNull("collection"); } object valueObj; if (collection.TryGetValue(key, out valueObj)) { if (valueObj is T) { value = (T)valueObj; return true; } } value = default(T); return false; } /// /// Gets the value of associated with the specified key or throw an /// if either the key is not present or the value is not of type . /// /// The instance where TValue is object. /// The key whose value to get. /// An instance of type . public static T GetValue(this IDictionary collection, string key) { if (collection == null) { throw Error.ArgumentNull("collection"); } T value; if (collection.TryGetValue(key, out value)) { return value; } throw Error.InvalidOperation(CommonWebApiResources.DictionaryMissingRequiredValue, collection.GetType().Name, key, typeof(T).Name); } internal static IEnumerable> FindKeysWithPrefix(this IDictionary dictionary, string prefix) { if (dictionary == null) { throw Error.ArgumentNull("dictionary"); } if (prefix == null) { throw Error.ArgumentNull("prefix"); } TValue exactMatchValue; if (dictionary.TryGetValue(prefix, out exactMatchValue)) { yield return new KeyValuePair(prefix, exactMatchValue); } foreach (var entry in dictionary) { string key = entry.Key; if (key.Length <= prefix.Length) { continue; } if (!key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { continue; } // Everything is prefixed by the empty string if (prefix.Length == 0) { yield return entry; } else { char charAfterPrefix = key[prefix.Length]; switch (charAfterPrefix) { case '[': case '.': yield return entry; break; } } } } internal static bool DoesAnyKeyHavePrefix(this IDictionary dictionary, string prefix) { return FindKeysWithPrefix(dictionary, prefix).Any(); } /// /// Adds a key/value pair of type to the /// if the key does not already exist. /// /// The actual type of the dictionary value. /// A dictionary. /// The key of the element to add. /// The function used to generate a value for the . /// The value for the key. This will be either the existing value for the if the key is already in the dictionary, /// or the new value for the key as returned by if the key was not in the dictionary. internal static T GetOrAdd(this ConcurrentDictionary concurrentPropertyBag, object key, Func factory) { Contract.Assert(concurrentPropertyBag != null); Contract.Assert(key != null); Contract.Assert(factory != null); // SIMPLIFYING ASSUMPTION: this method is internal and keys are private so it's assumed that client code won't be able to // replace the value with an object of a different type. return (T)concurrentPropertyBag.GetOrAdd(key, valueFactory: k => factory(k)); } } }