using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Web; namespace Umbraco.Core { /// /// Extension methods for Dictionary & ConcurrentDictionary /// internal static class DictionaryExtensions { /// /// Method to Get a value by the key. If the key doesn't exist it will create a new TVal object for the key and return it. /// /// /// /// /// /// public static TVal GetOrCreate(this IDictionary dict, TKey key) where TVal : class, new() { if (dict.ContainsKey(key) == false) { dict.Add(key, new TVal()); } return dict[key]; } /// /// Updates an item with the specified key with the specified value /// /// /// /// /// /// /// /// /// Taken from: http://stackoverflow.com/questions/12240219/is-there-a-way-to-use-concurrentdictionary-tryupdate-with-a-lambda-expression /// /// If there is an item in the dictionary with the key, it will keep trying to update it until it can /// public static bool TryUpdate(this ConcurrentDictionary dict, TKey key, Func updateFactory) { TValue curValue; while (dict.TryGetValue(key, out curValue)) { if (dict.TryUpdate(key, updateFactory(curValue), curValue)) return true; //if we're looping either the key was removed by another thread, or another thread //changed the value, so we start again. } return false; } /// /// Updates an item with the specified key with the specified value /// /// /// /// /// /// /// /// /// Taken from: http://stackoverflow.com/questions/12240219/is-there-a-way-to-use-concurrentdictionary-tryupdate-with-a-lambda-expression /// /// WARNING: If the value changes after we've retrieved it, then the item will not be updated /// public static bool TryUpdateOptimisitic(this ConcurrentDictionary dict, TKey key, Func updateFactory) { TValue curValue; if (!dict.TryGetValue(key, out curValue)) return false; dict.TryUpdate(key, updateFactory(curValue), curValue); return true;//note we return true whether we succeed or not, see explanation below. } /// /// Converts a dictionary to another type by only using direct casting /// /// /// /// /// public static IDictionary ConvertTo(this IDictionary d) { var result = new Dictionary(); foreach (DictionaryEntry v in d) { result.Add((TKeyOut)v.Key, (TValOut)v.Value); } return result; } /// /// Converts a dictionary to another type using the specified converters /// /// /// /// /// /// /// public static IDictionary ConvertTo(this IDictionary d, Func keyConverter, Func valConverter) { var result = new Dictionary(); foreach (DictionaryEntry v in d) { result.Add(keyConverter(v.Key), valConverter(v.Value)); } return result; } /// /// Converts a dictionary to a NameValueCollection /// /// /// public static NameValueCollection ToNameValueCollection(this IDictionary d) { var n = new NameValueCollection(); foreach (var i in d) { n.Add(i.Key, i.Value); } return n; } /// /// Merges all key/values from the sources dictionaries into the destination dictionary /// /// /// /// /// The source dictionary to merge other dictionaries into /// /// By default all values will be retained in the destination if the same keys exist in the sources but /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that /// it will just use the last found key/value if this is true. /// /// The other dictionaries to merge values from public static void MergeLeft(this T destination, IEnumerable> sources, bool overwrite = false) where T : IDictionary { foreach (var p in sources.SelectMany(src => src).Where(p => overwrite || destination.ContainsKey(p.Key) == false)) { destination[p.Key] = p.Value; } } /// /// Merges all key/values from the sources dictionaries into the destination dictionary /// /// /// /// /// The source dictionary to merge other dictionaries into /// /// By default all values will be retained in the destination if the same keys exist in the sources but /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that /// it will just use the last found key/value if this is true. /// /// The other dictionary to merge values from public static void MergeLeft(this T destination, IDictionary source, bool overwrite = false) where T : IDictionary { destination.MergeLeft(new[] {source}, overwrite); } /// /// Returns the value of the key value based on the key, if the key is not found, a null value is returned /// /// The type of the key. /// The type of the val. /// The d. /// The key. /// The default value. /// public static TVal GetValue(this IDictionary d, TKey key, TVal defaultValue = default(TVal)) { if (d.ContainsKey(key)) { return d[key]; } return defaultValue; } /// /// Returns the value of the key value based on the key as it's string value, if the key is not found, then an empty string is returned /// /// /// /// public static string GetValueAsString(this IDictionary d, TKey key) { if (d.ContainsKey(key)) { return d[key].ToString(); } return String.Empty; } /// /// Returns the value of the key value based on the key as it's string value, if the key is not found or is an empty string, then the provided default value is returned /// /// /// /// /// public static string GetValueAsString(this IDictionary d, TKey key, string defaultValue) { if (d.ContainsKey(key)) { var value = d[key].ToString(); if (value != string.Empty) return value; } return defaultValue; } /// contains key ignore case. /// The dictionary. /// The key. /// Value Type /// The contains key ignore case. public static bool ContainsKeyIgnoreCase(this IDictionary dictionary, string key) { return dictionary.Keys.InvariantContains(key); } /// /// Converts a dictionary object to a query string representation such as: /// firstname=shannon&lastname=deminick /// /// /// public static string ToQueryString(this IDictionary d) { if (!d.Any()) return ""; var builder = new StringBuilder(); foreach (var i in d) { builder.Append(String.Format("{0}={1}&", HttpUtility.UrlEncode(i.Key), i.Value == null ? string.Empty : HttpUtility.UrlEncode(i.Value.ToString()))); } return builder.ToString().TrimEnd('&'); } /// The get entry ignore case. /// The dictionary. /// The key. /// The type /// The entry public static TValue GetValueIgnoreCase(this IDictionary dictionary, string key) { return dictionary.GetValueIgnoreCase(key, default(TValue)); } /// The get entry ignore case. /// The dictionary. /// The key. /// The default value. /// The type /// The entry public static TValue GetValueIgnoreCase(this IDictionary dictionary, string key, TValue defaultValue) { key = dictionary.Keys.FirstOrDefault(i => i.InvariantEquals(key)); return key.IsNullOrWhiteSpace() == false ? dictionary[key] : defaultValue; } } }