protected override void ShallowCopyFrom(XObject copyFrom) { base.ShallowCopyFrom(copyFrom); XClosingTag copyFromAtt = (XClosingTag)copyFrom; //immutable types name = copyFromAtt.name; }
public override State PushChar(char c, IParseContext context, ref string rollback) { XClosingTag ct = context.Nodes.Peek() as XClosingTag; if (ct == null) { Debug.Assert(context.CurrentStateLength == 1, "IncompleteNode must not be an XClosingTag when CurrentStateLength is 1"); Debug.Assert(context.Nodes.Peek() is XElement); ct = new XClosingTag(context.LocationMinus(3)); //3 = </ and the current char context.Nodes.Push(ct); } //if tag closed if (c == '>') { context.Nodes.Pop(); if (ct.IsNamed) { ct.End(context.Location); // walk up tree of parents looking for matching tag int popCount = 0; bool found = false; foreach (XObject node in context.Nodes) { popCount++; XElement element = node as XElement; if (element != null && element.Name == ct.Name) { found = true; break; } } if (!found) { popCount = 0; } //clear the stack of intermediate unclosed tags while (popCount > 1) { XElement el = context.Nodes.Pop() as XElement; if (el != null) { context.LogError(string.Format( "Unclosed tag '{0}' at line {1}, column {2}.", el.Name.FullName, el.Region.BeginLine, el.Region.BeginColumn), ct.Region); } popCount--; } //close the start tag, if we found it if (popCount > 0) { if (context.BuildTree) { ((XElement)context.Nodes.Pop()).Close(ct); } else { context.Nodes.Pop(); } } else { context.LogError( "Closing tag '" + ct.Name.FullName + "' does not match any currently open tag."); } } else { context.LogError("Closing tag ended prematurely."); } return(Parent); } if (c == '<') { context.LogError("Unexpected '<' in tag."); rollback = string.Empty; return(Parent); } if (!ct.IsNamed && (char.IsLetter(c) || c == '_')) { rollback = string.Empty; return(NameState); } rollback = string.Empty; //note: MalformedTagState logs an error, so skip this //context.LogError ("Unexpected character '" + c + "' in closing tag."); return(MalformedTagState); }