/// <summary> /// Parses attributes from the tag using the given /// <see cref="ITextParser"/> <paramref name="tp"/>. The /// <see cref="ITextParser"/> position should be set to /// the first character of the tag following the element name. /// </summary> private bool ParseAttributes(ITextParser tp) { const char kDoubleQuote = '"'; const char kSingleQuote = '\''; const string kDoubleQuoteEntity = ""; if (tp.Peek() == '>') { tp.MoveAhead(); return(true); } var sb = new StringBuilder(); // Copy current input character void Copy() { sb.Append(tp.Peek()); tp.MoveAhead(); } // Copy input characters until fence character or end of tag void CopyTo(char fence) { while (!tp.EndOfText) { var c = tp.Peek(); if (c == fence || c == '>') { break; } if (c != kDoubleQuote) { sb.Append(c); } else { sb.Append(kDoubleQuoteEntity); } tp.MoveAhead(); } } // Copy attributes var startPos = tp.Position; while (!tp.EndOfText) { var c = tp.Peek(); if (c == '>' || c == '<') { break; } switch (c) { case '=': Copy(); c = tp.Peek(); if (c == kDoubleQuote) { // Copy double-quoted value Copy(); CopyTo(kDoubleQuote); sb.Append(kDoubleQuote); tp.MoveAhead(); } else if (c == kSingleQuote) { // Copy single-quoted value, but with double-quotes sb.Append(kDoubleQuote); tp.MoveAhead(); CopyTo(kSingleQuote); sb.Append(kDoubleQuote); tp.MoveAhead(); } else { // Copy unqouted value adding double-quotes sb.Append(kDoubleQuote); CopyTo(' '); sb.Append(kDoubleQuote); } break; default: Copy(); break; } } if (tp.Peek() != '>') { return(false); } if (tp.CharAt(tp.Position - 1) == '/') { IsSelfClosingTag = true; sb.Length = sb.Length - 1; } AttributesPart = sb.ToString(); if (AttributesPart.IndexOf('&') != -1) { AttributesPart = ResolveEntities(AttributesPart); } tp.MoveAhead(); return(true); }