Ejemplo n.º 1
0
        // TODO: Put strings into the resources.
        //
        /// <summary>
        /// Выполняет парсинг набора пар ключ-значение из строки.
        /// </summary>
        /// <param name="argument">
        /// Аргумент входной строки.
        /// </param>
        /// <param name="keyComparer">
        /// Компаратор ключей, используемый для проверки уникальности ключей.
        /// <para>По умолчанию используется <see cref="StringComparer"/>.<see cref="StringComparer.OrdinalIgnoreCase"/>.</para>
        /// </param>
        /// <param name="keyValueDelimiter">
        /// Символ-разделитель ключа и значения.
        /// <para>По умолчанию используется '='.</para>
        /// </param>
        /// <param name="keyValuePairDelimiter">
        /// Символ-разделитель пар ключ-значение.
        /// <para>По умолчанию используется ';'.</para>
        /// </param>
        /// <param name="escapeDelimiterMarker">
        /// Символ отмены (экранирования).
        /// <para>По умолчанию используется '/'.</para>
        /// </param>
        /// <returns>Словарь пар ключ-значение <seealso cref="IDictionary{TKey, TValue}"/>.</returns>
        public static IDictionary <string, string> ParseKeyValuePairs(
            this ArgumentUtilitiesHandle <string> argument,
            StringComparer keyComparer = null,
            char keyValueDelimiter     = '=',
            char keyValuePairDelimiter = ';',
            char escapeDelimiterMarker = '/')
        {
            //
            if (keyValueDelimiter == keyValuePairDelimiter)
            {
                throw
                    new ArgumentException(
                        message: $"Значение аргумента не может равняться значению аргумента '{nameof(keyValuePairDelimiter)}'.",
                        paramName: nameof(keyValueDelimiter));
            }
            else if (keyValuePairDelimiter == escapeDelimiterMarker)
            {
                throw
                    new ArgumentException(
                        message: $"Значение аргумента не может равняться значению аргумента '{nameof(escapeDelimiterMarker)}'.",
                        paramName: nameof(keyValuePairDelimiter));
            }
            else if (keyValueDelimiter == escapeDelimiterMarker)
            {
                throw
                    new ArgumentException(
                        message: $"Значение аргумента не может равняться значению аргумента '{nameof(escapeDelimiterMarker)}'.",
                        paramName: nameof(keyValueDelimiter));
            }
            //
            var input            = argument.Value;
            var resultDictionary = new Dictionary <string, string>(keyComparer = keyComparer ?? StringComparer.OrdinalIgnoreCase);

            if (string.IsNullOrWhiteSpace(input))
            {
                return(resultDictionary);
            }
            else if (input.Length < 2)
            {
                argument.EnsureHasMinLength(minLength: 2);
                throw new ArgumentException(paramName: argument.Name, message: FormatXResource(locator: typeof(string), subpath: "TooShort"));
            }
            else
            {
                var inputBuffer = new StringBuilder(input);
                if (inputBuffer.Last() == keyValuePairDelimiter)
                {
                    if (inputBuffer.Last(offset: 1) == escapeDelimiterMarker)
                    {
                        inputBuffer.Append(keyValuePairDelimiter);
                    }
                }
                else if (inputBuffer.Last() == escapeDelimiterMarker)
                {
                    if (inputBuffer.Last(offset: 1) != escapeDelimiterMarker)
                    {
                        inputBuffer.Append(escapeDelimiterMarker);
                    }
                    inputBuffer.Append(keyValuePairDelimiter);
                }
                else
                {
                    inputBuffer.Append(keyValuePairDelimiter);
                }
                //
                var keyContext     = new StringBuilder();
                var valueContext   = new StringBuilder();
                var currentContext = keyContext;
                var escapeContext  = false;
                for (var inputCharIndex = 0; inputCharIndex < inputBuffer.Length; inputCharIndex++)
                {
                    var inputChar = inputBuffer[inputCharIndex];
                    if (inputChar == keyValueDelimiter)
                    {
                        if (escapeContext)
                        {
                            currentContext.Append(inputChar);
                            escapeContext = false;
                        }
                        else if (ReferenceEquals(keyContext, currentContext) && keyContext.Length > 0)
                        {
                            currentContext = valueContext;
                        }
                        else
                        {
                            goto throw_format_exc;
                        }
                    }
                    else if (inputChar == keyValuePairDelimiter)
                    {
                        if (escapeContext)
                        {
                            currentContext.Append(inputChar);
                            escapeContext = false;
                        }
                        else if (ReferenceEquals(valueContext, currentContext))
                        {
                            var key = keyContext.ToString();
                            keyContext.Clear();
                            var value = valueContext.ToString();
                            valueContext.Clear();
                            if (resultDictionary.ContainsKey(key))
                            {
                                throw
                                    new ArgumentException(
                                        message: $"{(argument.IsProp ? $"Указано недопустимое значение свойства '{argument.Name}'.{Environment.NewLine}" : string.Empty)}Невозможно выполнить парсинг набора пар ключ-значение из входной строки. Один из ключей во входной строке не уникален.{Environment.NewLine}\tВходная строка:{input.FmtStr().GNLI2()}{Environment.NewLine}\tНеуникальный ключ:{key.FmtStr().GNLI2()}{Environment.NewLine}\tИспользуемый компаратор ключей:{keyComparer.FmtStr().GNLI2()}",
                                        paramName: argument.Name);
                            }
                            resultDictionary.Add(key, value);
                            currentContext = keyContext;
                        }
                        else
                        {
                            goto throw_format_exc;
                        }
                    }
                    else if (inputChar == escapeDelimiterMarker)
                    {
                        if (escapeContext)
                        {
                            currentContext.Append(inputChar);
                            escapeContext = false;
                        }
                        else
                        {
                            escapeContext = true;
                        }
                    }
                    else if (escapeContext)
                    {
                        currentContext.Append(escapeDelimiterMarker);
                        currentContext.Append(inputChar);
                        escapeContext = false;
                    }
                    else
                    {
                        currentContext.Append(inputChar);
                    }
                }
                return(resultDictionary);
            }
throw_format_exc:
            throw
                new FormatException(
                    message: $"{(argument.IsProp ? $"Указано недопустимое значение свойства '{argument.Name}'.{Environment.NewLine}" : string.Empty)}Невозможно выполнить парсинг набора пар ключ-значение из входной строки, так как входная строка имеет некорректный формат.{Environment.NewLine}\tВходная строка:{input.FmtStr().GNLI2()}{Environment.NewLine}\tСпециальные символы:{Environment.NewLine}\t\tРазд. ключа и значения: '{keyValueDelimiter.ToString()}'{Environment.NewLine}\t\tРазд. пар ключ-значение: '{keyValuePairDelimiter.ToString()}'{Environment.NewLine}\t\tСимвол отмены (экранирования): '{escapeDelimiterMarker.ToString()}'");
        }