< Summary - SonghayCore

Line coverage
81%
Covered lines: 204
Uncovered lines: 46
Coverable lines: 250
Total lines: 680
Line coverage: 81.6%
Branch coverage
60%
Covered branches: 84
Total branches: 138
Branch coverage: 60.8%
Method coverage

Method coverage is only available for sponsors.

Upgrade to PRO version

Metrics

File(s)

/home/rasx/sourceRoot/SonghayCore/SonghayCore/Extensions/StringExtensions._.cs

#LineLine coverage
 1namespace Songhay.Extensions;
 2
 3/// <summary>
 4/// Extensions of <see cref="string"/>.
 5/// </summary>
 6public static partial class StringExtensions
 7{
 8    /// <summary>
 9    /// Returns <c>true</c> when the strings are equal without regard to cultural locales
 10    /// or casing.
 11    /// </summary>
 12    /// <param name="input">The input.</param>
 13    /// <param name="otherString">The other <see cref="string"/>.</param>
 14    public static bool EqualsInvariant(this string? input, string? otherString) =>
 1215        input.EqualsInvariant(otherString, ignoreCase: true);
 16
 17    /// <summary>
 18    /// Returns <c>true</c> when the strings are equal without regard to cultural locales.
 19    /// </summary>
 20    /// <param name="input">The input.</param>
 21    /// <param name="otherString">The other <see cref="string"/>.</param>
 22    /// <param name="ignoreCase"></param>
 23    public static bool EqualsInvariant(this string? input, string? otherString, bool ignoreCase)
 1224    {
 1225        return ignoreCase
 1226            ? string.Equals(input, otherString, StringComparison.InvariantCultureIgnoreCase)
 1227            : string.Equals(input, otherString, StringComparison.InvariantCulture);
 1228    }
 29
 30    /// <summary>
 31    /// Escapes the interpolation tokens of <see cref="string.Format(string, object[])"/>.
 32    /// </summary>
 33    /// <param name="input">The input.</param>
 34    public static string? EscapeInterpolation(this string? input) =>
 135        string.IsNullOrWhiteSpace(input) ? input : input.Replace("{", "{{").Replace("}", "}}");
 36
 37    /// <summary>
 38    /// Converts camel-case <see cref="string"/> to <see cref="IEnumerable{T}"/>.
 39    /// </summary>
 40    /// <param name="input">The input.</param>
 41    public static IEnumerable<string> FromCamelCaseToEnumerable(this string? input) =>
 142        new string(input.InsertSpacesBeforeCaps().ToArray())
 743            .Split(' ').Where(i => !string.IsNullOrWhiteSpace(i));
 44
 45    /// <summary>
 46    /// Determines whether the specified input is in the comma-delimited values.
 47    /// </summary>
 48    /// <param name="input">The input.</param>
 49    /// <param name="delimitedValues">The delimited values.</param>
 350    public static bool In(this string? input, string? delimitedValues) => input.In(delimitedValues, ',');
 51
 52    /// <summary>
 53    /// Determines whether the specified input is in the delimited values.
 54    /// </summary>
 55    /// <param name="input">The input.</param>
 56    /// <param name="delimitedValues">The delimited values.</param>
 57    /// <param name="separator">The separator.</param>
 58    public static bool In(this string? input, string? delimitedValues, char separator)
 359    {
 560        if (string.IsNullOrWhiteSpace(input)) return false;
 161        if (string.IsNullOrWhiteSpace(delimitedValues)) return false;
 62
 163        var set = delimitedValues.Split(separator);
 64
 165        return set.Contains(input);
 366    }
 67
 68    /// <summary>
 69    /// Returns <see cref="string"/> in double quotes.
 70    /// </summary>
 71    /// <param name="input">The input.</param>
 72    public static string? InDoubleQuotes(this string? input)
 173    {
 174        if (string.IsNullOrWhiteSpace(input)) return null;
 75
 176        input = input.Replace("\"", "\"\"");
 77
 178        return $"\"{input}\"";
 179    }
 80
 81    /// <summary>
 82    /// Returns <see cref="string"/> in double quotes or default.
 83    /// </summary>
 84    /// <param name="input">The input.</param>
 85    /// <param name="defaultValue">The default value.</param>
 86    public static string? InDoubleQuotesOrDefault(this string? input, string defaultValue) =>
 287        string.IsNullOrWhiteSpace(input) ? defaultValue : input.InDoubleQuotes();
 88
 89    /// <summary>
 90    /// Inserts the spaces before caps.
 91    /// </summary>
 92    /// <param name="input">The input.</param>
 93    public static IEnumerable<char> InsertSpacesBeforeCaps(this string? input)
 294    {
 295        if (string.IsNullOrWhiteSpace(input)) yield return '\0';
 96
 7897        foreach (char c in input!)
 3698        {
 4399            if (char.IsUpper(c)) yield return ' ';
 36100            yield return c;
 36101        }
 2102    }
 103
 104    /// <summary>
 105    /// Determines whether the specified input is byte.
 106    /// </summary>
 107    /// <param name="input">The input.</param>
 108    /// <returns>
 109    ///   <c>true</c> if the specified input is byte; otherwise, <c>false</c>.
 110    /// </returns>
 0111    public static bool IsByte(this string? input) => input.IsByte(null);
 112
 113    /// <summary>
 114    /// Determines whether the specified input is byte.
 115    /// </summary>
 116    /// <param name="input">The input.</param>
 117    /// <param name="secondaryTest">The secondary test.</param>
 118    /// <returns>
 119    ///   <c>true</c> if the specified input is byte; otherwise, <c>false</c>.
 120    /// </returns>
 121    public static bool IsByte(this string? input, Predicate<byte>? secondaryTest)
 0122    {
 0123        var isExpectedType = byte.TryParse(input, out var testValue);
 124
 0125        return isExpectedType ? secondaryTest?.Invoke(testValue) ?? isExpectedType : isExpectedType;
 0126    }
 127
 128    /// <summary>
 129    /// Determines whether the specified input is decimal.
 130    /// </summary>
 131    /// <param name="input">The input.</param>
 132    /// <returns>
 133    ///   <c>true</c> if the specified input is decimal; otherwise, <c>false</c>.
 134    /// </returns>
 0135    public static bool IsDecimal(this string? input) => input.IsDecimal(null);
 136
 137    /// <summary>
 138    /// Determines whether the specified input is decimal.
 139    /// </summary>
 140    /// <param name="input">The input.</param>
 141    /// <param name="secondaryTest">The secondary test.</param>
 142    /// <returns>
 143    ///   <c>true</c> if the specified input is decimal; otherwise, <c>false</c>.
 144    /// </returns>
 145    public static bool IsDecimal(this string? input, Predicate<decimal>? secondaryTest)
 0146    {
 0147        var isExpectedType = decimal.TryParse(input, out var testValue);
 148
 0149        return isExpectedType ? secondaryTest?.Invoke(testValue) ?? isExpectedType : isExpectedType;
 0150    }
 151
 152    /// <summary>
 153    /// Determines whether the specified input is integer.
 154    /// </summary>
 155    /// <param name="input">The input.</param>
 156    /// <returns>
 157    ///   <c>true</c> if the specified input is integer; otherwise, <c>false</c>.
 158    /// </returns>
 0159    public static bool IsInteger(this string? input) => input.IsInteger(null);
 160
 161    /// <summary>
 162    /// Determines whether the specified input is integer.
 163    /// </summary>
 164    /// <param name="input">The input.</param>
 165    /// <param name="secondaryTest">The secondary test.</param>
 166    /// <returns>
 167    ///   <c>true</c> if the specified input is integer; otherwise, <c>false</c>.
 168    /// </returns>
 169    public static bool IsInteger(this string? input, Predicate<int>? secondaryTest)
 0170    {
 0171        var isExpectedType = int.TryParse(input, out var testValue);
 172
 0173        return isExpectedType ? secondaryTest?.Invoke(testValue) ?? isExpectedType : isExpectedType;
 0174    }
 175
 176    /// <summary>
 177    /// Determines whether the specified input is long.
 178    /// </summary>
 179    /// <param name="input">The input.</param>
 180    /// <returns>
 181    ///   <c>true</c> if the specified input is long; otherwise, <c>false</c>.
 182    /// </returns>
 0183    public static bool IsLong(this string? input) => input.IsLong(null);
 184
 185    /// <summary>
 186    /// Determines whether the specified input is long.
 187    /// </summary>
 188    /// <param name="input">The input.</param>
 189    /// <param name="secondaryTest">The secondary test.</param>
 190    /// <returns>
 191    ///   <c>true</c> if the specified input is long; otherwise, <c>false</c>.
 192    /// </returns>
 193    public static bool IsLong(this string? input, Predicate<long>? secondaryTest)
 0194    {
 0195        var isExpectedType = long.TryParse(input, out var testValue);
 196
 0197        return isExpectedType ? secondaryTest?.Invoke(testValue) ?? isExpectedType : isExpectedType;
 0198    }
 199
 200    /// <summary>
 201    /// Determines whether the specified input is a telephone number.
 202    /// </summary>
 203    /// <param name="input">The input.</param>
 204    /// <returns>
 205    ///   <c>true</c> if is telephone number; otherwise, <c>false</c>.
 206    /// </returns>
 207    public static bool IsTelephoneNumber(this string? input) =>
 4208        !string.IsNullOrWhiteSpace(input) &&
 4209        Regex.IsMatch(input, @"^\(?[0-9]\d{2}\)?([-., ])?[0-9]\d{2}([-., ])?\d{4}$");
 210
 211    /// <summary>
 212    /// Determines whether the specified input is a UNC.
 213    /// </summary>
 214    /// <param name="input">The input.</param>
 215    /// <returns>
 216    ///   <c>true</c> if is a UNC; otherwise, <c>false</c>.
 217    /// </returns>
 218    /// <remarks>
 219    ///  📚 https://stackoverflow.com/a/47531093/22944
 220    ///  📚 https://en.wikipedia.org/wiki/Path_(computing)#Uniform_Naming_Convention
 221    /// </remarks>
 222    public static bool IsUnc(this string? input) =>
 4223        !string.IsNullOrWhiteSpace(input) &&
 4224        Regex.IsMatch(input, @"^(\\(\\[^\s\\]+)+|([A-Za-z]:(\\)?|[A-z]:(\\[^\s\\]+)+))(\\)?$");
 225
 226    /// <summary>
 227    /// Determines whether the specified input looks like an email address.
 228    /// </summary>
 229    /// <param name="input">The input.</param>
 230    /// <returns>
 231    ///   <c>true</c> if seems to be an email address; otherwise, <c>false</c>.
 232    /// </returns>
 233    /// <remarks>
 234    /// “In short, don’t expect a single, usable regex to do a proper job.
 235    /// And the best regex will validate the syntax, not the validity
 236    /// of an e-mail (jhohn@example.com is correct but it will probably bounce…).”
 237    /// [http://stackoverflow.com/questions/201323/how-to-use-a-regular-expression-to-validate-an-email-addresses]
 238    /// </remarks>
 239    public static bool LooksLikeEmailAddress(this string? input) =>
 0240        !string.IsNullOrWhiteSpace(input) &&
 0241        Regex.IsMatch(input, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
 242
 243    /// <summary>
 244    /// Remove accent from strings
 245    /// </summary>
 246    /// <example>
 247    ///  input:  "Příliš žluťoučký kůň úpěl ďábelské ódy."
 248    ///  result: "Prilis zlutoucky kun upel dabelske ody."
 249    /// </example>
 250    /// <param name="input">The input.</param>
 251    /// <remarks>
 252    /// From Tomas Kubes, http://www.codeproject.com/Articles/31050/String-Extension-Collection-for-C
 253    /// Also, see http://stackoverflow.com/questions/249087/how-do-i-remove-diacritics-accents-from-a-string-in-net
 254    /// </remarks>
 255    /// <returns><see cref="string"/> without accents</returns>
 256    public static string RemoveDiacritics(this string input)
 0257    {
 0258        string stFormD = input.Normalize(NormalizationForm.FormD);
 0259        StringBuilder sb = new StringBuilder();
 260
 0261        foreach (var t in stFormD)
 0262        {
 0263            UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t);
 0264            if (uc != UnicodeCategory.NonSpacingMark)
 0265            {
 0266                sb.Append(t);
 0267            }
 0268        }
 269
 0270        return sb.ToString().Normalize(NormalizationForm.FormC);
 0271    }
 272}

/home/rasx/sourceRoot/SonghayCore/SonghayCore/Extensions/StringExtensions.Conversions.cs

#LineLine coverage
 1namespace Songhay.Extensions;
 2
 3public static partial class StringExtensions
 4{
 5    /// <summary>
 6    /// Replaces “snake” underscores with caps of first <see cref="char"/>
 7    /// after the underscore.
 8    /// </summary>
 9    /// <param name="input"></param>
 110    public static string? FromSnakeToCaps(this string? input) => string.IsNullOrWhiteSpace(input)
 111        ? input
 312        : input.Split('_').Aggregate((a, i) => $"{a}{i.ToPascalCase()}");
 13
 14    /// <summary>
 15    /// Reverse the string
 16    /// from http://en.wikipedia.org/wiki/Extension_method
 17    /// </summary>
 18    /// <param name="input"></param>
 19    /// <remarks>
 20    /// Based on work by Tomas Kubes, http://www.codeproject.com/Articles/31050/String-Extension-Collection-for-C
 21    /// </remarks>
 222    public static string? Reverse(this string? input) => string.IsNullOrWhiteSpace(input)
 223        ? input
 224        : input.ToCharArray().Reverse().FromCharsToString();
 25
 26    /// <summary>
 27    /// Converts the <see cref="String" /> into a ASCII letters with spacer <c>\0</c>.
 28    /// </summary>
 29    /// <param name="input">The input.</param>
 030    public static string? ToAsciiLettersWithSpacer(this string? input) => input.ToAsciiLettersWithSpacer('\0');
 31
 32    /// <summary>
 33    /// Converts the <see cref="String" /> into ASCII letters with spacer.
 34    /// </summary>
 35    /// <param name="input">The input.</param>
 36    /// <param name="spacer">The spacer.</param>
 37    /// <remarks>
 38    /// 📖 https://en.wikipedia.org/wiki/ASCII
 39    /// 📖 https://stackoverflow.com/a/7826216/22944
 40    /// </remarks>
 41    public static string? ToAsciiLettersWithSpacer(this string? input, char spacer)
 542    {
 743        if (string.IsNullOrWhiteSpace(input)) return null;
 44
 45        const int asciiDecimalMaximum = 127;
 46
 47        int GetAsciiDecimal(char c)
 3748        {
 3749            int i = c;
 3750            return i;
 3751        }
 52
 353        var chars = input
 3754            .Where(c => GetAsciiDecimal(c) <= asciiDecimalMaximum)
 355            .ToArray();
 56
 357        if (spacer == '\0')
 258        {
 259            return chars.Any() ? new string(chars.ToArray()) : null;
 60        }
 61
 162        return chars.Any() ? new string(chars.ToArray()).Trim(new[] {spacer}) : null;
 563    }
 64
 65    /// <summary>
 66    /// Converts the <see cref="String"/> into a blog slug.
 67    /// </summary>
 68    /// <param name="input">The input.</param>
 69    public static string ToBlogSlug(this string? input)
 470    {
 471        input.ThrowWhenNullOrWhiteSpace();
 72
 73        // Remove/replace entities:
 474        input = input.Replace("&amp;", "and");
 475        input = Regex.Replace(input, @"\&\w+\;", string.Empty, RegexOptions.IgnoreCase);
 476        input = Regex.Replace(input, @"\&\#\d+\;", string.Empty, RegexOptions.IgnoreCase);
 77
 78        // Replace any characters that are not alphanumeric with hyphen:
 479        input = Regex.Replace(input, "[^a-z^0-9]", "-", RegexOptions.IgnoreCase);
 80
 81        // Replace all double hyphens with single hyphen
 482        var pattern = "--";
 883        while (Regex.IsMatch(input, pattern)) input = Regex.Replace(input, pattern, "-", RegexOptions.IgnoreCase);
 84
 85        // Remove leading and trailing hyphens ("-")
 486        pattern = "^-|-$";
 487        input = Regex.Replace(input, pattern, "", RegexOptions.IgnoreCase);
 88
 489        return input.ToLower();
 490    }
 91
 92    /// <summary>
 93    /// Converts the <see cref="String"/> into camel case
 94    /// by lower-casing the first character.
 95    /// </summary>
 96    /// <param name="input">The input.</param>
 197    public static string? ToCamelCase(this string? input) => string.IsNullOrWhiteSpace(input)
 198        ? input
 199        : $"{input[0].ToString().ToLowerInvariant()}{input[1..]}";
 100
 101    /// <summary>
 102    /// Converts the <see cref="String"/> into digits only.
 103    /// </summary>
 104    /// <param name="input">The input.</param>
 105    public static string? ToDigitsOnly(this string? input)
 3106    {
 3107        if (string.IsNullOrWhiteSpace(input)) return null;
 108
 3109        var digitsOnly = new Regex(@"[^\d]");
 110
 3111        return digitsOnly.Replace(input, string.Empty);
 3112    }
 113
 114    /// <summary>
 115    /// Prepares a string to be converted to <c>int</c>.
 116    /// </summary>
 117    /// <param name="input">The input.</param>
 0118    public static string? ToIntString(this string? input) => input.ToIntString("0");
 119
 120    /// <summary>
 121    /// Prepares a string to be converted to <c>int</c>.
 122    /// </summary>
 123    /// <param name="input">The input.</param>
 124    /// <param name="defaultValue">The default value ("0" by default).</param>
 125    public static string? ToIntString(this string? input, string defaultValue)
 3126    {
 3127        if (input == null) return null;
 128
 3129        var output = defaultValue;
 3130        var array = input.Split('.');
 131
 3132        if (array.Length == 0) return output;
 3133        if (string.IsNullOrWhiteSpace(array[0].Trim())) return output;
 134
 3135        output = array[0].ToDigitsOnly();
 5136        if (string.IsNullOrWhiteSpace(output)) return defaultValue;
 137
 1138        return output;
 3139    }
 140
 141    /// <summary>
 142    /// Returns the number of directory levels
 143    /// based on the conventions <c>../</c> or <c>..\</c>.
 144    /// </summary>
 145    /// <param name="path">The path.</param>
 146    public static int ToNumberOfDirectoryLevels(this string? path)
 2147    {
 2148        if (string.IsNullOrWhiteSpace(path)) return 0;
 149
 2150        var matches = Regex.Matches(path, @"\.\./|\.\.\\");
 151
 2152        return matches.Count;
 2153    }
 154
 155    /// <summary>
 156    /// Converts the <see cref="string"/> into a numeric format for parsing.
 157    /// </summary>
 158    /// <param name="input">The input.</param>
 159    /// <returns>
 160    /// Returns a numeric string ready for integer or float parsing.
 161    /// </returns>
 162    /// <remarks>
 163    /// This member does not support parenthesis as indicators of negative numbers.
 164    /// </remarks>
 4165    public static string? ToNumericString(this string? input) => input.ToNumericString("0");
 166
 167    /// <summary>
 168    /// Converts the <see cref="string"/> into a numeric format for parsing.
 169    /// </summary>
 170    /// <param name="input">The input.</param>
 171    /// <param name="defaultValue">The default value ("0" by default).</param>
 172    /// <returns>
 173    /// Returns a numeric string ready for integer or float parsing.
 174    /// </returns>
 175    /// <remarks>
 176    /// This member does not support parenthesis as indicators of negative numbers.
 177    /// </remarks>
 178    public static string? ToNumericString(this string? input, string? defaultValue)
 8179    {
 12180        if (string.IsNullOrWhiteSpace(input)) return defaultValue;
 181
 74182        bool IsNumericChar(char i) => char.IsDigit(i)
 74183            || i.Equals('.')
 74184            || i.Equals('-')
 185            ;
 186
 4187        return string.IsNullOrWhiteSpace(input)
 4188            ? defaultValue
 4189            : new string(input.Trim().Where(IsNumericChar).ToArray());
 8190    }
 191
 192    /// <summary>
 193    /// Converts the <see cref="String"/> into camel case
 194    /// by upper-casing the first character.
 195    /// </summary>
 196    /// <param name="input">The input.</param>
 3197    public static string? ToPascalCase(this string? input) => string.IsNullOrWhiteSpace(input)
 3198        ? input
 3199        : $"{input[0].ToString().ToUpperInvariant()}{input[1..]}";
 200
 201    /// <summary>
 202    /// Converts the <see cref="String"/> into camel case
 203    /// then replaces every upper case character
 204    /// with an underscore and its lowercase equivalent.
 205    /// </summary>
 206    /// <param name="input">The input.</param>
 207    public static string? ToSnakeCase(this string? input)
 1208    {
 1209        if (string.IsNullOrWhiteSpace(input)) return input;
 210
 1211        return input.ToCamelCase()
 1212            .InsertSpacesBeforeCaps()
 1213            .FromCharsToString()
 1214            ?.Replace(' ', '_')
 1215            .ToLowerInvariant();
 1216    }
 217
 218    /// <summary>
 219    /// Formats the <see cref="string"/> into a shortened form,
 220    /// showing the search text in context.
 221    /// </summary>
 222    /// <param name="input">The input.</param>
 223    /// <param name="searchText">The search text.</param>
 224    /// <param name="contextLength">Length of the context.</param>
 225    public static string? ToSubstringInContext(this string? input, string? searchText, int contextLength)
 2226    {
 2227        if (string.IsNullOrWhiteSpace(input)) return input;
 2228        if (string.IsNullOrWhiteSpace(searchText)) return input;
 229
 2230        if (input.Contains(searchText))
 2231        {
 2232            if (searchText.Length >= contextLength)
 1233                return searchText[..contextLength];
 234
 1235            var edgesLength = Convert.ToInt32(Math.Ceiling((contextLength - searchText.Length) / 2d));
 236
 1237            var i0 = input.IndexOf(searchText, StringComparison.Ordinal) - edgesLength;
 1238            if (i0 < 0) i0 = 0;
 239
 1240            var i1 = i0 + searchText.Length + edgesLength;
 1241            if (i1 > (input.Length - 1)) i1 = input.Length;
 242
 1243            return input.Substring(i0, i1);
 244        }
 245        else
 0246        {
 0247            var i0 = 0;
 0248            var i1 = contextLength - 1;
 0249            if (i1 > (input.Length - 1)) i1 = input.Length;
 250
 0251            return input.Substring(i0, i1);
 252        }
 2253    }
 254
 255    /// <summary>
 256    /// Truncates the specified input to 16 characters.
 257    /// <param name="input">The input.</param>
 258    /// </summary>
 0259    public static string? Truncate(this string? input) => input.Truncate(length: 16, ellipsis: "…");
 260
 261    /// <summary>
 262    /// Truncates the specified input to 16 characters.
 263    /// <param name="input">The input.</param>
 264    /// <param name="length">The length.</param>
 265    /// </summary>
 2266    public static string? Truncate(this string? input, int length) => input.Truncate(length, ellipsis: "…");
 267
 268    /// <summary>
 269    /// Truncates the specified input.
 270    /// </summary>
 271    /// <param name="input">The input.</param>
 272    /// <param name="length">The length.</param>
 273    /// <param name="ellipsis"></param>
 274    public static string? Truncate(this string? input, int length, string ellipsis)
 2275    {
 2276        if (string.IsNullOrWhiteSpace(input)) return input;
 3277        if (input.Length <= length) return input;
 1278        if (length <= 0) length = 0;
 279
 1280        return string.Concat(input[..length].TrimEnd(), ellipsis);
 2281    }
 282}

/home/rasx/sourceRoot/SonghayCore/SonghayCore/Extensions/StringExtensions.CsvSplit.cs

#LineLine coverage
 1using Songhay.Exceptions;
 2
 3namespace Songhay.Extensions;
 4
 5public static partial class StringExtensions
 6{
 7    /// <summary>
 8    /// Splits CSV text format into an array of <see cref="string"/>.
 9    /// </summary>
 10    /// <param name="source">The source.</param>
 11    /// <remarks>
 12    /// This code is based on “LINQ to TEXT and LINQ to CSV” by Eric Lippert
 13    /// [http://blogs.msdn.com/b/ericwhite/archive/2008/09/30/linq-to-text-and-linq-to-csv.aspx]
 14    /// </remarks>
 15    public static string[]? CsvSplit(this string? source)
 1016    {
 1017        if (string.IsNullOrWhiteSpace(source)) return null;
 18
 1019        List<string> splitString = new();
 1020        List<int> slashesToRemove = new();
 1021        State state = State.AtBeginningOfToken;
 1022        char[] sourceCharArray = source.ToCharArray();
 1023        int tokenStart = 0;
 1024        int len = sourceCharArray.Length;
 29825        for (int i = 0; i < len; ++i)
 14026        {
 14027            switch (state)
 28            {
 29                case State.AtBeginningOfToken:
 2830                    if (sourceCharArray[i] == '"')
 1431                    {
 1432                        state = State.InQuotedToken;
 1433                        slashesToRemove = new List<int>();
 1434                        continue;
 35                    }
 36
 1437                    if (sourceCharArray[i] == ',')
 438                    {
 439                        splitString.Add("");
 440                        tokenStart = i + 1;
 441                        continue;
 42                    }
 43
 1044                    state = State.InNonQuotedToken;
 1045                    continue;
 46                case State.InNonQuotedToken:
 2847                    if (sourceCharArray[i] == ',')
 848                    {
 849                        splitString.Add(
 850                            source.Substring(tokenStart, i - tokenStart));
 851                        state = State.AtBeginningOfToken;
 852                        tokenStart = i + 1;
 853                    }
 54
 2855                    continue;
 56                case State.InQuotedToken:
 7157                    if (sourceCharArray[i] == '"')
 1258                    {
 1259                        state = State.ExpectingComma;
 1260                        continue;
 61                    }
 62
 5963                    if (sourceCharArray[i] == '\\')
 464                    {
 465                        state = State.InEscapedCharacter;
 466                        slashesToRemove.Add(i - tokenStart);
 467                    }
 68
 5969                    continue;
 70                case State.ExpectingComma:
 1071                    if (sourceCharArray[i] != ',')
 172                        throw new CsvParseException("Expecting comma");
 973                    string stringWithSlashes =
 974                        source.Substring(tokenStart, i - tokenStart);
 3375                    foreach (int item in slashesToRemove.Reverse<int>())
 376                        stringWithSlashes =
 377                            stringWithSlashes.Remove(item, 1);
 978                    splitString.Add(
 979                        stringWithSlashes.Substring(1,
 980                            stringWithSlashes.Length - 2));
 981                    state = State.AtBeginningOfToken;
 982                    tokenStart = i + 1;
 983                    continue;
 84                case State.InEscapedCharacter:
 385                    state = State.InQuotedToken;
 386                    continue;
 87            }
 088        }
 89
 990        switch (state)
 91        {
 92            case State.AtBeginningOfToken:
 393                splitString.Add("");
 394                return splitString.ToArray();
 95            case State.InNonQuotedToken:
 296                splitString.Add(
 297                    source.Substring(tokenStart,
 298                        source.Length - tokenStart));
 299                return splitString.ToArray();
 100            case State.InQuotedToken:
 1101                throw new CsvParseException("Expecting ending quote");
 102            case State.ExpectingComma:
 2103                string stringWithSlashes =
 2104                    source.Substring(tokenStart, source.Length - tokenStart);
 6105                foreach (int item in slashesToRemove.Reverse<int>())
 0106                    stringWithSlashes = stringWithSlashes.Remove(item, 1);
 2107                splitString.Add(
 2108                    stringWithSlashes.Substring(1,
 2109                        stringWithSlashes.Length - 2));
 2110                return splitString.ToArray();
 111            case State.InEscapedCharacter:
 1112                throw new CsvParseException("Expecting escaped character");
 113        }
 114
 0115        throw new CsvParseException("Unexpected error");
 7116    }
 117
 118    enum State
 119    {
 120        AtBeginningOfToken,
 121        InNonQuotedToken,
 122        InQuotedToken,
 123        ExpectingComma,
 124        InEscapedCharacter
 125    }
 126}