#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #endregion using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.Serialization; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; #endif using System.Reflection; namespace Newtonsoft.Json.Utilities { internal static class EnumUtils { private static readonly ThreadSafeStore> EnumMemberNamesPerType = new ThreadSafeStore>(InitializeEnumType); private static BidirectionalDictionary InitializeEnumType(Type type) { BidirectionalDictionary map = new BidirectionalDictionary( StringComparer.OrdinalIgnoreCase, StringComparer.OrdinalIgnoreCase); foreach (FieldInfo f in type.GetFields()) { string n1 = f.Name; string n2; #if !NET20 n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true) .Cast() .Select(a => a.Value) .SingleOrDefault() ?? f.Name; #else n2 = f.Name; #endif string s; if (map.TryGetBySecond(n2, out s)) { throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, n2, type.Name)); } map.Set(n1, n2); } return map; } public static IList GetFlagsValues(T value) where T : struct { Type enumType = typeof(T); if (!enumType.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException("Enum type {0} is not a set of flags.".FormatWith(CultureInfo.InvariantCulture, enumType)); } Type underlyingType = Enum.GetUnderlyingType(value.GetType()); ulong num = Convert.ToUInt64(value, CultureInfo.InvariantCulture); IList> enumNameValues = GetNamesAndValues(); IList selectedFlagsValues = new List(); foreach (EnumValue enumNameValue in enumNameValues) { if ((num & enumNameValue.Value) == enumNameValue.Value && enumNameValue.Value != 0) { selectedFlagsValues.Add((T)Convert.ChangeType(enumNameValue.Value, underlyingType, CultureInfo.CurrentCulture)); } } if (selectedFlagsValues.Count == 0 && enumNameValues.SingleOrDefault(v => v.Value == 0) != null) { selectedFlagsValues.Add(default(T)); } return selectedFlagsValues; } /// /// Gets a dictionary of the names and values of an Enum type. /// /// public static IList> GetNamesAndValues() where T : struct { return GetNamesAndValues(typeof(T)); } /// /// Gets a dictionary of the names and values of an Enum type. /// /// The enum type to get names and values for. /// public static IList> GetNamesAndValues(Type enumType) where TUnderlyingType : struct { if (enumType == null) { throw new ArgumentNullException(nameof(enumType)); } if (!enumType.IsEnum()) { throw new ArgumentException("Type {0} is not an Enum.".FormatWith(CultureInfo.InvariantCulture, enumType), nameof(enumType)); } IList enumValues = GetValues(enumType); IList enumNames = GetNames(enumType); IList> nameValues = new List>(); for (int i = 0; i < enumValues.Count; i++) { try { nameValues.Add(new EnumValue(enumNames[i], (TUnderlyingType)Convert.ChangeType(enumValues[i], typeof(TUnderlyingType), CultureInfo.CurrentCulture))); } catch (OverflowException e) { throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Value from enum with the underlying type of {0} cannot be added to dictionary with a value type of {1}. Value was too large: {2}", Enum.GetUnderlyingType(enumType), typeof(TUnderlyingType), Convert.ToUInt64(enumValues[i], CultureInfo.InvariantCulture)), e); } } return nameValues; } public static IList GetValues(Type enumType) { if (!enumType.IsEnum()) { throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); } List values = new List(); var fields = enumType.GetFields().Where(f => f.IsLiteral); foreach (FieldInfo field in fields) { object value = field.GetValue(enumType); values.Add(value); } return values; } public static IList GetNames(Type enumType) { if (!enumType.IsEnum()) { throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); } List values = new List(); var fields = enumType.GetFields().Where(f => f.IsLiteral); foreach (FieldInfo field in fields) { values.Add(field.Name); } return values; } public static object ParseEnumName(string enumText, bool isNullable, Type t) { if (enumText == string.Empty && isNullable) { return null; } string finalEnumText; BidirectionalDictionary map = EnumMemberNamesPerType.Get(t); if (enumText.IndexOf(',') != -1) { string[] names = enumText.Split(','); for (int i = 0; i < names.Length; i++) { string name = names[i].Trim(); names[i] = ResolvedEnumName(map, name); } finalEnumText = string.Join(", ", names); } else { finalEnumText = ResolvedEnumName(map, enumText); } return Enum.Parse(t, finalEnumText, true); } public static string ToEnumName(Type enumType, string enumText, bool camelCaseText) { BidirectionalDictionary map = EnumMemberNamesPerType.Get(enumType); string[] names = enumText.Split(','); for (int i = 0; i < names.Length; i++) { string name = names[i].Trim(); string resolvedEnumName; map.TryGetByFirst(name, out resolvedEnumName); resolvedEnumName = resolvedEnumName ?? name; if (camelCaseText) { resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName); } names[i] = resolvedEnumName; } string finalName = string.Join(", ", names); return finalName; } private static string ResolvedEnumName(BidirectionalDictionary map, string enumText) { string resolvedEnumName; map.TryGetBySecond(enumText, out resolvedEnumName); resolvedEnumName = resolvedEnumName ?? enumText; return resolvedEnumName; } } }