private void ResolveWrongClosingTag(DothtmlElementNode element) { Debug.Assert(element.IsClosingTag); var startElement = ElementHierarchy.Peek() as DothtmlElementNode; Debug.Assert(startElement != null); Debug.Assert(startElement.FullTagName != element.FullTagName); while (startElement != null && !startElement.FullTagName.Equals(element.FullTagName, StringComparison.OrdinalIgnoreCase)) { ElementHierarchy.Pop(); if (HtmlWriter.SelfClosingTags.Contains(startElement.FullTagName)) { // automatic immediate close of the tag (for <img src="">) ElementHierarchy.Peek().Content.AddRange(startElement.Content); startElement.Content.Clear(); } else if (AutomaticClosingTags.Contains(startElement.FullTagName)) { // elements than can contain itself like <p> are closed on the first occurance of element with the same name var sameElementIndex = startElement.Content.FindIndex(a => (a as DothtmlElementNode)?.FullTagName == startElement.FullTagName); if (sameElementIndex >= 0) { var count = startElement.Content.Count - sameElementIndex; ElementHierarchy.Peek().Content.AddRange(startElement.Content.Skip(sameElementIndex)); startElement.Content.RemoveRange(sameElementIndex, count); } } // otherwise just pop the element startElement = ElementHierarchy.Peek() as DothtmlElementNode; } }
private void ResolveWrongClosingTag(DothtmlElementNode element) { Debug.Assert(element.IsClosingTag); var startElement = ElementHierarchy.Peek() as DothtmlElementNode; Debug.Assert(startElement != null); Debug.Assert(startElement.FullTagName != element.FullTagName); while (startElement != null && !startElement.FullTagName.Equals(element.FullTagName, StringComparison.OrdinalIgnoreCase)) { ElementHierarchy.Pop(); if (HtmlWriter.IsSelfClosing(startElement.FullTagName)) { // automatic immediate close of the tag (for <img src="">) ElementHierarchy.Peek().Content.AddRange(startElement.Content); startElement.Content.Clear(); startElement.AddWarning("End tag is missing, the element is implicitly self-closed."); } else if (AutomaticClosingTags.Contains(startElement.FullTagName)) { // elements than can contain itself like <p> are closed on the first occurance of element with the same name var sameElementIndex = startElement.Content.FindIndex(a => (a as DothtmlElementNode)?.FullTagName == startElement.FullTagName); if (sameElementIndex < 0) { startElement.AddWarning($"End tag is missing, the element is implicitly closed with its parent tag or by the end of file."); } else if (sameElementIndex >= 0) { startElement.AddWarning($"End tag is missing, the element is implicitly closed by following <{startElement.Content[sameElementIndex].As<DothtmlElementNode>()?.FullTagName}> tag."); startElement.Content[sameElementIndex].AddWarning($"Previous <{startElement.FullTagName}> is implicitly closed here."); var count = startElement.Content.Count - sameElementIndex; ElementHierarchy.Peek().Content.AddRange(startElement.Content.Skip(sameElementIndex)); startElement.Content.RemoveRange(sameElementIndex, count); } } else { startElement.AddWarning($"End tag is missing, the element is implicitly closed by </{element.FullTagName}>."); element.AddWarning($"Element <{startElement.FullTagName}> is implicitly closed here."); } // otherwise just pop the element startElement = ElementHierarchy.Peek() as DothtmlElementNode; } }
public DothtmlRootNode Parse(List <DothtmlToken> tokens) { Root = null; Tokens = tokens; CurrentIndex = 0; ElementHierarchy.Clear(); // read file var root = new DothtmlRootNode(); root.Tokens.Add(Tokens); ElementHierarchy.Push(root); // read content var doNotAppend = false; while (Peek() is DothtmlToken token) { if (token.Type == DothtmlTokenType.DirectiveStart) { // directive root.Directives.Add(ReadDirective()); doNotAppend = true; } else if (token.Type == DothtmlTokenType.OpenTag) { // element - check element hierarchy var element = ReadElement(); if (ElementHierarchy.Any()) { element.ParentNode = ElementHierarchy.Peek() as DothtmlElementNode; } if (!element.IsSelfClosingTag) { if (!element.IsClosingTag) { // open tag CurrentElementContent.Add(element); ElementHierarchy.Push(element); } else { // close tag if (ElementHierarchy.Count <= 1) { element.AddWarning($"The closing tag '</{element.FullTagName}>' doesn't have a matching opening tag!"); CurrentElementContent.Add(element); } else { var beginTag = (DothtmlElementNode)ElementHierarchy.Peek(); var beginTagName = beginTag.FullTagName; if (!beginTagName.Equals(element.FullTagName, StringComparison.OrdinalIgnoreCase)) { element.AddWarning($"The closing tag '</{element.FullTagName}>' doesn't have a matching opening tag!"); ResolveWrongClosingTag(element); if (ElementHierarchy.Peek() is DothtmlElementNode newBeginTag && beginTagName != newBeginTag.FullTagName) { newBeginTag.CorrespondingEndTag = element; ElementHierarchy.Pop(); } else { CurrentElementContent.Add(element); } } else { ElementHierarchy.Pop(); beginTag.CorrespondingEndTag = element; } } } }