public void CopyAttributes(Node n) { for (int i = 0, len = n.attributes.Count; i < len; i++) { var a = (Attribute)n.attributes[i]; var na = this.AddAttribute(a.Name, a.Value, a.QuoteChar, false); na.DtdType = a.DtdType; } }
/// <summary> /// Reads the next node from the stream. /// </summary> /// <returns>true if the next node was read successfully; false if there are no more nodes to read.</returns> public override bool Read() { if (m_current == null) { OpenInput(); } var start = this.m_state; if (m_node.Simulated) { // return the next node m_node.Simulated = false; this.m_node = Top(); this.m_state = this.m_node.CurrentState; return true; } var foundnode = false; while (!foundnode) { switch (this.m_state) { case State.Initial: this.m_state = State.Markup; this.m_current.ReadChar(); goto case State.Markup; case State.Eof: if (this.m_current.Parent != null) { this.m_current.Close(); this.m_current = this.m_current.Parent; } else { return false; } break; case State.EndTag: if (string.Equals(this.m_endTag, this.m_node.Name, StringComparison.OrdinalIgnoreCase)) { Pop(); // we're done! this.m_state = State.Markup; goto case State.Markup; } Pop(); // close one element foundnode = true; // return another end element. break; case State.Markup: if (this.m_node.IsEmpty) { Pop(); } var n = this.m_node; foundnode = ParseMarkup(); break; case State.PartialTag: Pop(); // remove text node. this.m_state = State.Markup; foundnode = ParseTag(this.m_partial); break; case State.PseudoStartTag: foundnode = ParseStartTag('<'); break; case State.AutoClose: Pop(); // close next node. if (this.m_stack.Count <= this.m_poptodepth) { this.m_state = State.Markup; if (this.m_newnode != null) { Push(this.m_newnode); // now we're ready to start the new node. this.m_newnode = null; this.m_state = State.Markup; } else if (this.m_node.NodeType == XmlNodeType.Document) { this.m_state = State.Eof; goto case State.Eof; } } foundnode = true; break; case State.CData: foundnode = ParseCData(); break; case State.Attr: goto case State.AttrValue; case State.AttrValue: this.m_state = State.Markup; goto case State.Markup; case State.Text: Pop(); goto case State.Markup; case State.PartialText: if (ParseText(this.m_current.Lastchar, false)) { this.m_node.NodeType = XmlNodeType.Whitespace; } foundnode = true; break; } if (foundnode && this.m_node.NodeType == XmlNodeType.Whitespace && this.m_whitespaceHandling == WhitespaceHandling.None) { // strip out whitespace (caller is probably pretty printing the XML). foundnode = false; } if (!foundnode && this.m_state == State.Eof && this.m_stack.Count > 1) { this.m_poptodepth = 1; this.m_state = State.AutoClose; this.m_node = Top(); return true; } } if (!m_foundRoot && (this.NodeType == XmlNodeType.Element || this.NodeType == XmlNodeType.Text || this.NodeType == XmlNodeType.CDATA)) { m_foundRoot = true; if (this.IsHtml && (this.NodeType != XmlNodeType.Element || !string.Equals(this.LocalName, "html", StringComparison.OrdinalIgnoreCase))) { // Simulate an HTML root element! this.m_node.CurrentState = this.m_state; var root = Push("html", XmlNodeType.Element, null); SwapTopNodes(); // make html the outer element. this.m_node = root; root.Simulated = true; root.IsEmpty = false; this.m_state = State.Markup; //this.state = State.PseudoStartTag; //this.startTag = name; } return true; } return true; }
private void Pop() { if (this.m_stack.Count > 1) { this.m_node = (Node)this.m_stack.Pop(); } }
private Node Push(Node n) { // we have to do a deep clone of the Node object because // it is reused in the stack. var n2 = Push(n.Name, n.NodeType, n.Value); n2.DtdType = n.DtdType; n2.IsEmpty = n.IsEmpty; n2.Space = n.Space; n2.XmlLang = n.XmlLang; n2.CurrentState = n.CurrentState; n2.CopyAttributes(n); this.m_node = n2; return n2; }
private Node Push(string name, XmlNodeType nt, string value) { var result = (Node)this.m_stack.Push(); if (result == null) { result = new Node(); this.m_stack[this.m_stack.Count - 1] = result; } result.Reset(name, nt, value); this.m_node = result; return result; }
private void Init() { this.m_state = State.Initial; this.m_stack = new HWStack(10); this.m_node = Push(null, XmlNodeType.Document, null); this.m_node.IsEmpty = false; this.m_sb = new StringBuilder(); this.m_name = new StringBuilder(); this.m_poptodepth = 0; this.m_current = null; this.m_partial = '\0'; this.m_endTag = null; this.m_a = null; this.m_apos = 0; this.m_newnode = null; this.m_rootCount = 0; this.m_foundRoot = false; this.unknownNamespaces.Clear(); }
private void ValidateContent(Node node) { if (node.NodeType == XmlNodeType.Element) { if (!VerifyName(node.Name)) { Pop(); Push(null, XmlNodeType.Text, "<" + node.Name + ">"); return; } } if (this.m_dtd != null) { // See if this element is allowed inside the current element. // If it isn't, then auto-close elements until we find one // that it is allowed to be in. var name = node.Name.ToUpperInvariant(); // DTD is in upper case var i = 0; var top = this.m_stack.Count - 2; if (node.DtdType != null) { // it is a known element, let's see if it's allowed in the // current context. for (i = top; i > 0; i--) { var n = (Node)this.m_stack[i]; if (n.IsEmpty) { continue; // we'll have to pop this one } var f = n.DtdType; if (f != null) { if ((i == 2) && string.Equals(f.Name, "BODY", StringComparison.OrdinalIgnoreCase)) // NOTE (steveb): never close the BODY tag too early { break; } else if (string.Equals(f.Name, this.m_dtd.Name, StringComparison.OrdinalIgnoreCase)) { break; // can't pop the root element. } else if (f.CanContain(name, this.m_dtd)) { break; } else if (!f.EndTagOptional) { // If the end tag is not optional then we can't // auto-close it. We'll just have to live with the // junk we've found and move on. break; } } else { // Since we don't understand this tag anyway, // we might as well allow this content! break; } } } if (i == 0) { // Tag was not found or is not allowed anywhere, ignore it and // continue on. return; } else if (i < top) { var n = (Node)this.m_stack[top]; if (i == top - 1 && string.Equals(name, n.Name, StringComparison.OrdinalIgnoreCase)) { // e.g. p not allowed inside p, not an interesting error. } else { #if DEBUG var closing = ""; for (var k = top; k >= i + 1; k--) { if (closing != "") { closing += ","; } var n2 = (Node)this.m_stack[k]; closing += "<" + n2.Name + ">"; } Log("Element '{0}' not allowed inside '{1}', closing {2}.", name, n.Name, closing); #endif } this.m_state = State.AutoClose; this.m_newnode = node; Pop(); // save this new node until we pop the others this.m_poptodepth = i + 1; } } }
private static void ValidateAttribute(Node node, Attribute a) { var e = node.DtdType; if (e != null) { var ad = e.FindAttribute(a.Name); if (ad != null) { a.DtdType = ad; } } }
private void Validate(Node node) { if (this.m_dtd != null) { var e = this.m_dtd.FindElement(node.Name); if (e != null) { node.DtdType = e; if (e.ContentModel.DeclaredContent == DeclaredContent.EMPTY) { node.IsEmpty = true; } } } }
private bool ParseEndTag() { this.m_state = State.EndTag; this.m_current.ReadChar(); // consume '/' char. var name = this.ScanName(tagterm); var ch = this.m_current.SkipWhitespace(); if (ch != '>') { Log("Expected empty start tag '/>' sequence instead of '{0}'", ch); this.m_current.ScanToEnd(null, "Recovering", ">"); } this.m_current.ReadChar(); // consume '>' this.m_endTag = name; // Make sure there's a matching start tag for it. var caseInsensitive = (this.m_folding == CaseFolding.None); this.m_node = (Node)this.m_stack[this.m_stack.Count - 1]; for (var i = this.m_stack.Count - 1; i > 0; i--) { var n = (Node)this.m_stack[i]; if (string.Equals(n.Name, name, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) { this.m_endTag = n.Name; return true; } } Log("No matching start tag for '</{0}>'", name); this.m_state = State.Markup; return false; }