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; }