// /** // * File: Parser.cs // * Author: haraldwolff // * // * This file and it's content is copyrighted by the Author and / or copyright holder. // * Any use wihtout proper permission is illegal and may lead to legal actions. // * // * // **/ using System; using System.IO; using System.Collections.Generic; using System.Text; namespace ln.templates.html { public class HtmlReader { public HtmlReader() { } public void Read(string source) => Read(new StringReader(source)); public void Read(Stream sourceStream) { using (StreamReader streamReader = new StreamReader(sourceStream)) Read(streamReader); } public void Read(TextReader textReader) { while (textReader.Peek() != -1) { switch (textReader.Peek()) { case '<': ReadTag(textReader); break; default: ReadText(textReader); break; } } } public void ReadTag(TextReader textReader) { if (textReader.Read() != '<') throw new FormatException("Expected <"); bool closing = false; if (textReader.Peek()=='/') { textReader.Read(); closing = true; } if (textReader.Peek()=='!') { textReader.Read(); string doctype = ReadTagName(textReader); if (!doctype.Equals("DOCTYPE")) throw new FormatException("Expected DOCTYPE"); string type = ReadAttributeName(textReader); DOCTYPE(type); if (textReader.Read() != '>') throw new FormatException("Expected >"); return; } string tagName = ReadTagName(textReader); if (closing) { CloseTag(tagName); } else { OpenTag(tagName); while (TestChar((char)textReader.Peek(), (ch) => !char.IsWhiteSpace(ch) && (ch != '\0') && (ch != '"') && (ch != '\'') && (ch != '>') && (ch != '/') && (ch != '='))) { string attributeName = ReadAttributeName(textReader); string attributeValue = ""; if (textReader.Peek() == '=') { textReader.Read(); attributeValue = ReadAttributeValue(textReader); } Attribute(attributeName, attributeValue); } if (textReader.Peek()=='/') { textReader.Read(); CloseTag(tagName); } } if (textReader.Read() != '>') throw new FormatException("Expected >"); } bool TestChar(char ch, Func condition) => condition(ch); public string ReadToken(TextReader textReader, Func condition, bool clearFinalChar = false) { StringBuilder characters = new StringBuilder(); while (condition((char)textReader.Peek()) && textReader.Peek() != -1) { characters.Append((char)textReader.Read()); } if ((textReader.Peek() != -1) && clearFinalChar) textReader.Read(); return characters.ToString(); } public string ReadTokenLWS(TextReader textReader, Func condition,bool clearFinalChar = false) { string token = ReadToken(textReader, condition, clearFinalChar); ReadToken(textReader, char.IsWhiteSpace); return token; } void ReadLWS(TextReader textReader) => ReadToken(textReader, char.IsWhiteSpace); public string ReadTagName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => char.IsLetterOrDigit(ch)); public string ReadAttributeName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => !char.IsWhiteSpace(ch) && (ch != '\0') && (ch != '"') && (ch != '\'') && (ch != '>') && (ch != '/') && (ch != '=')); public string ReadAttributeValue(TextReader textReader) { switch (textReader.Peek()) { case '"': textReader.Read(); return ReadTokenLWS(textReader, (ch) => ch != '"', true); case '\'': textReader.Read(); return ReadTokenLWS(textReader, (ch)=> ch != '\'', true); default: return ReadTokenLWS(textReader, (ch) => !char.IsWhiteSpace(ch) && (ch != '"') && (ch != '\'') && (ch != '<') && (ch != '>') && (ch != '`')); } } public void ReadText(TextReader textReader) { StringBuilder stringBuilder = new StringBuilder(); while (textReader.Peek() != '<') stringBuilder.Append((char)textReader.Read()); Text(stringBuilder.ToString()); } public virtual void DOCTYPE(string type) { } public virtual void OpenTag(string tagName) { } public virtual void CloseTag(string tagName) { } public virtual void Attribute(string attributeName,string attributeValue) { } public virtual void Text(string text) { } } }