public override bool Read() { if (current == null) { OpenInput(); } State start = this.state; if (node.Simulated) { // return the next node node.Simulated = false; this.node = Top(); this.state = this.node.CurrentState; return true; } bool foundnode = false; while (! foundnode) { switch (this.state) { case State.Initial: this.state = State.Markup; this.current.ReadChar(); goto case State.Markup; case State.Eof: if (this.current.Parent != null) { this.current.Close(); this.current = this.current.Parent; } else { return false; } break; case State.EndTag: if (this.endTag == (object)this.node.Name) { Pop(); // we're done! this.state = State.Markup; goto case State.Markup; } Pop(); // close one element foundnode = true;// return another end element. break; case State.Markup: if (this.node.IsEmpty) { Pop(); } Node n = this.node; foundnode = ParseMarkup(); break; case State.PartialTag: Pop(); // remove text node. this.state = State.Markup; foundnode = ParseTag(this.partial); break; case State.PseudoStartTag: foundnode = ParseStartTag('<'); break; case State.AutoClose: Pop(); // close next node. if (this.stack.Count <= this.poptodepth) { this.state = State.Markup; if (this.newnode != null) { Push(this.newnode); // now we're ready to start the new node. this.newnode = null; this.state = State.Markup; } else if (this.node.NodeType == XmlNodeType.Document) { this.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.state = State.Markup; goto case State.Markup; case State.Text: Pop(); goto case State.Markup; case State.PartialText: if (ParseText(this.current.Lastchar, false)) { this.node.NodeType = XmlNodeType.Whitespace; } foundnode = true; break; } if (foundnode && this.node.NodeType == XmlNodeType.Whitespace && this.whitespaceHandling == WhitespaceHandling.None) { // strip out whitespace (caller is probably pretty printing the XML). foundnode = false; } if (!foundnode && this.state == State.Eof && this.stack.Count>1) { this.poptodepth = 1; state = State.AutoClose; this.node = Top(); return true; } } if (!foundRoot && (this.NodeType == XmlNodeType.Element || this.NodeType == XmlNodeType.Text || this.NodeType == XmlNodeType.CDATA)) { foundRoot = true; if (this.IsHtml && (this.NodeType != XmlNodeType.Element || string.Compare(this.LocalName, "html", true, System.Globalization.CultureInfo.InvariantCulture) != 0)) { // Simulate an HTML root element! this.node.CurrentState = this.state; Node root = Push("html", XmlNodeType.Element, null); SwapTopNodes(); // make html the outer element. this.node = root; root.Simulated = true; root.IsEmpty = false; this.state = State.Markup; //this.state = State.PseudoStartTag; //this.startTag = name; } return true; } return true; }
Node Push(Node n) { // we have to do a deep clone of the Node object because // it is reused in the stack. Node 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.node = n2; return n2; }
void Pop() { if (this.stack.Count > 1) { this.node = (Node)this.stack.Pop(); } }
void Init() { this.state = State.Initial; this.stack = new HWStack(10); this.node = Push(null, XmlNodeType.Document, null); this.node.IsEmpty = false; this.sb = new StringBuilder(); this.name = new StringBuilder(); this.poptodepth = 0; this.current = null; this.partial = '\0'; this.endTag = null; this.a = null; this.apos = 0; this.newnode = null; this.rootCount = 0; this.foundRoot = false; }
Node Push(string name, XmlNodeType nt, string value) { Node result = (Node)this.stack.Push(); if (result == null) { result = new Node(); this.stack[this.stack.Count-1] = result; } result.Reset(name, nt, value); this.node = result; return result; }
public void CopyAttributes(Node n) { for (int i = 0, len = n.attributes.Count; i < len; i++) { Attribute a = (Attribute)n.attributes[i]; Attribute na = this.AddAttribute(a.Name, a.Value, a.QuoteChar, false); na.DtdType = a.DtdType; } }
void ValidateContent(Node node) { if (this.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. string name = this.nametable.Add(node.Name.ToUpper()); // DTD is in upper case int i = 0; int top = this.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--) { Node n = (Node)this.stack[i]; if (n.IsEmpty) continue; // we'll have to pop this one ElementDecl f = n.DtdType; if (f != null) { if (f.Name == this.dtd.Name) break; // can't pop the root element. if (f.CanContain(name, this.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. } else if (i < top) { Node n = (Node)this.stack[top]; if (i == top - 1 && name == n.Name) { // e.g. p not allowed inside p, not an interesting error. } else { string closing = ""; for (int k = top; k >= i+1; k--) { if (closing != "") closing += ","; Node n2 = (Node)this.stack[k]; closing += "<"+n2.Name+">"; } Log("Element '{0}' not allowed inside '{1}', closing {2}.", name, n.Name, closing); } this.state = State.AutoClose; this.newnode = node; Pop(); // save this new node until we pop the others this.poptodepth = i+1; } } }
void ValidateAttribute(Node node, Attribute a) { ElementDecl e = node.DtdType; if (e != null) { AttDef ad = e.FindAttribute(a.Name); if (ad != null) { a.DtdType = ad; } } }
void Validate(Node node) { if (this.dtd != null) { ElementDecl e = this.dtd.FindElement(node.Name); if (e != null) { node.DtdType = e; if (e.ContentModel.DeclaredContent == DeclaredContent.EMPTY) node.IsEmpty = true; } } }
bool ParseEndTag() { this.state = State.EndTag; this.current.ReadChar(); // consume '/' char. string name = this.ScanName(SgmlReader.tagterm); char ch = this.current.SkipWhitespace(); if (ch != '>') { Log("Expected empty start tag '/>' sequence instead of '{0}'", ch); this.current.ScanToEnd(null, "Recovering", ">"); } this.current.ReadChar(); // consume '>' this.endTag = name; // Make sure there's a matching start tag for it. bool caseInsensitive = (this.folding == CaseFolding.None); this.node = (Node)this.stack[this.stack.Count-1]; for (int i = this.stack.Count-1; i>0; i--) { Node n = (Node)this.stack[i]; if (caseInsensitive && string.Compare(n.Name, name, true) == 0) { this.endTag = n.Name; return true; } else if ((object)n.Name == (object)name) { return true; } } Log("No matching start tag for '</{0}>'", name); this.state = State.Markup; return false; }