public static MarkupExtensionInfo Parse(string input)
        {
            if (!MarkupExtensionPattern.IsMatch(input))
            {
                throw new InvalidOperationException($"{input} is not a MarkupExtension.");
            }

            var resultInfo = new MarkupExtensionInfo();

            using (var reader = new StringReader(input))
            {
                var parsingMode = MarkupExtensionParsingModeEnum.Start;

                try
                {
                    //Debug.Print("Parsing '{0}'", input);
                    //Debug.Indent();

                    while ((MarkupExtensionParsingModeEnum.End != parsingMode)
                        && (MarkupExtensionParsingModeEnum.Unexpected != parsingMode))
                    {
                        //Debug.Print(context.ToString());
                        //Debug.Indent();

                        switch (parsingMode)
                        {
                            case MarkupExtensionParsingModeEnum.Start:
                                parsingMode = reader.ReadMarkupExtensionStart();
                                break;
                            case MarkupExtensionParsingModeEnum.MarkupName:
                                parsingMode = reader.ReadMarkupName(resultInfo);
                                break;
                            case MarkupExtensionParsingModeEnum.NameValuePair:
                                parsingMode = reader.ReadNameValuePair(resultInfo);
                                break;
                        }

                        //Debug.Unindent();
                    }
                }
                catch (Exception e)
                {
                    throw new InvalidDataException($"Cannot parse markup extension string:\r\n \"{input}\"", e);
                }
            }

            return resultInfo;
        }
        private static MarkupExtensionParsingModeEnum ReadMarkupName(this StringReader reader, MarkupExtensionInfo info)
        {
            char[] stopChars = {' ', '}'};
            var resultParsingMode = MarkupExtensionParsingModeEnum.Unexpected;
            var buffer = new StringBuilder();

            while (!reader.IsEnd())
            {
                char c = reader.ReadChar();

                if (stopChars.Contains(c))
                {
                    switch (c)
                    {
                        case ' ':
                            resultParsingMode = MarkupExtensionParsingModeEnum.NameValuePair;
                            break;
                        case '}':
                            resultParsingMode = MarkupExtensionParsingModeEnum.End;
                            break;
                        default:
                            throw new InvalidDataException($"[{nameof(ReadMarkupName)}] Should not encounter '{c}'.");
                    }

                    info.Name = buffer.ToString().Trim();
                    buffer.Clear();

                    break;
                }
                
                buffer.Append(c);
            }

            if (MarkupExtensionParsingModeEnum.Unexpected == resultParsingMode)
            {
                throw new InvalidDataException($"[{nameof(ReadMarkupName)}] Invalid result context: {resultParsingMode}");
            }

            return resultParsingMode;
        }
        private static MarkupExtensionParsingModeEnum ReadNameValuePair(
            this StringReader reader,
            MarkupExtensionInfo info)
        {
            char[] stopChars = {',', '=', '}'};

            MarkupExtensionParsingModeEnum resultParsingMode;
            string key = null;
            object value = null;

            reader.SeekTill(_ => !Char.IsWhiteSpace(_));

            // When '{' is the starting char, the following must be a value instead of a key.
            //
            // E.g.,
            //    <Setter x:Uid="Setter_75"
            //            Property="Foreground"
            //            Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
            //
            // In other words, "key" shall not start with '{', as it won't be a valid property name.
            if (reader.PeekChar() != '{')
            {
                string temp = reader.ReadTill(stopChars.Contains).Trim();
                char keyValueIndicatorChar = reader.PeekChar();

                switch (keyValueIndicatorChar)
                {
                    case ',':
                    case '}':
                        value = temp;
                        break;
                    case '=':
                        key = temp;
                        // Consume the '='
                        reader.Read();
                        break;
                    default:
                        throw new InvalidDataException($"[{nameof(ReadNameValuePair)}] Should not encounter '{keyValueIndicatorChar}'.");
                }
            }

            if (value == null)
            {
                reader.SeekTill(_ => !(Char.IsWhiteSpace(_)));

                string input = reader.ReadValueString();

                if (MarkupExtensionPattern.IsMatch(input))
                {
                    value = Parse(input);
                }
                else
                {
                    value = input;
                }
            }

            if (String.IsNullOrEmpty(key))
            {
                info.ValueOnlyProperties.Add(value);
            }
            else
            {
                info.KeyValueProperties.Add(new KeyValuePair<string, object>(key, value));
            }

            reader.SeekTill(_ => !Char.IsWhiteSpace(_));

            char stopChar = reader.ReadChar();

            switch (stopChar)
            {
                case ',':
                    resultParsingMode = MarkupExtensionParsingModeEnum.NameValuePair;
                    break;
                case '}':
                    resultParsingMode = MarkupExtensionParsingModeEnum.End;
                    break;
                default:
                    throw new InvalidDataException($"[{nameof(ReadNameValuePair)}] Should not encounter '{stopChar}'.");
            }

            if (MarkupExtensionParsingModeEnum.Unexpected == resultParsingMode)
            {
                throw new InvalidDataException($"[{nameof(ReadNameValuePair)}] Invalid result context: {resultParsingMode}");
            }

            return resultParsingMode;
        }