public static object ParseEnum(Type enumType, string value, bool disallowNumber) { ValidationUtils.ArgumentNotNull(enumType, nameof(enumType)); ValidationUtils.ArgumentNotNull(value, nameof(value)); if (!enumType.IsEnum()) { throw new ArgumentException("Type provided must be an Enum.", nameof(enumType)); } EnumInfo entry = ValuesAndNamesPerEnum.Get(enumType); string[] enumNames = entry.Names; string[] resolvedNames = entry.ResolvedNames; ulong[] enumValues = entry.Values; // first check if the entire text (including commas) matches a resolved name int?matchingIndex = FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.Ordinal); if (matchingIndex != null) { return(Enum.ToObject(enumType, enumValues[matchingIndex.Value])); } int firstNonWhitespaceIndex = -1; for (int i = 0; i < value.Length; i++) { if (!char.IsWhiteSpace(value[i])) { firstNonWhitespaceIndex = i; break; } } if (firstNonWhitespaceIndex == -1) { throw new ArgumentException("Must specify valid information for parsing in the string."); } // check whether string is a number and parse as a number value char firstNonWhitespaceChar = value[firstNonWhitespaceIndex]; if (char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '-' || firstNonWhitespaceChar == '+') { Type underlyingType = Enum.GetUnderlyingType(enumType); value = value.Trim(); object temp = null; try { temp = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture); } catch (FormatException) { // We need to Parse this as a String instead. There are cases // when you tlbimp enums that can have values of the form "3D". // Don't fix this code. } if (temp != null) { if (disallowNumber) { throw new FormatException("Integer string '{0}' is not allowed.".FormatWith(CultureInfo.InvariantCulture, value)); } return(Enum.ToObject(enumType, temp)); } } ulong result = 0; int valueIndex = firstNonWhitespaceIndex; while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma { // Find the next separator, if there is one, otherwise the end of the string. int endIndex = value.IndexOf(EnumSeparatorChar, valueIndex); if (endIndex == -1) { endIndex = value.Length; } // Shift the starting and ending indices to eliminate whitespace int endIndexNoWhitespace = endIndex; while (valueIndex < endIndex && char.IsWhiteSpace(value[valueIndex])) { valueIndex++; } while (endIndexNoWhitespace > valueIndex && char.IsWhiteSpace(value[endIndexNoWhitespace - 1])) { endIndexNoWhitespace--; } int valueSubstringLength = endIndexNoWhitespace - valueIndex; // match with case sensitivity matchingIndex = MatchName(value, enumNames, resolvedNames, valueIndex, valueSubstringLength, StringComparison.Ordinal); // if no match found, attempt case insensitive search if (matchingIndex == null) { matchingIndex = MatchName(value, enumNames, resolvedNames, valueIndex, valueSubstringLength, StringComparison.OrdinalIgnoreCase); } if (matchingIndex == null) { // still can't find a match // before we throw an error, check whether the entire string has a case insensitive match against resolve names matchingIndex = FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.OrdinalIgnoreCase); if (matchingIndex != null) { return(Enum.ToObject(enumType, enumValues[matchingIndex.Value])); } // no match so error throw new ArgumentException("Requested value '{0}' was not found.".FormatWith(CultureInfo.InvariantCulture, value)); } result |= enumValues[matchingIndex.Value]; // Move our pointer to the ending index to go again. valueIndex = endIndex + 1; } return(Enum.ToObject(enumType, result)); }
private static String InternalFlagsFormat(EnumInfo entry, ulong result, bool camelCaseText) { string[] resolvedNames = entry.ResolvedNames; ulong[] values = entry.Values; int index = values.Length - 1; StringBuilder sb = new StringBuilder(); bool firstTime = true; ulong saveResult = result; // We will not optimize this code further to keep it maintainable. There are some boundary checks that can be applied // to minimize the comparsions required. This code works the same for the best/worst case. In general the number of // items in an enum are sufficiently small and not worth the optimization. while (index >= 0) { if (index == 0 && values[index] == 0) { break; } if ((result & values[index]) == values[index]) { result -= values[index]; if (!firstTime) { sb.Insert(0, EnumSeparatorString); } string resolvedName = resolvedNames[index]; sb.Insert(0, camelCaseText ? StringUtils.ToCamelCase(resolvedName) : resolvedName); firstTime = false; } index--; } string returnString; if (result != 0) { // We were unable to represent this number as a bitwise or of valid flags returnString = null; // return null so the caller knows to .ToString() the input } else if (saveResult == 0) { // For the cases when we have zero if (values.Length > 0 && values[0] == 0) { returnString = resolvedNames[0]; // Zero was one of the enum values. if (camelCaseText) { returnString = StringUtils.ToCamelCase(returnString); } } else { returnString = null; } } else { returnString = sb.ToString(); // Return the string representation } return(returnString); }