public void CopyAttributes( Node n ) { var len = n._attributes.Count; for( var i = 0; i < len; i++ ) { var a = (Attribute) n._attributes[ i ]; var na = AddAttribute( a.Name, a.Value, a.QuoteChar, false ); na.DtdType = a.DtdType; } }
private void ValidateContent( Node node ) { if( node.NodeType == XmlNodeType.Element ) { if( !VerifyName( node.Name ) ) { Pop(); Push( null, XmlNodeType.Text, "<" + node.Name + ">" ); return; } } if( _dtd == null ) return; // 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 = _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) _stack[ i ]; if( n.IsEmpty ) continue; // we'll have to pop this one var f = n.DtdType; if( // Since we don't understand this tag anyway, // we might as well allow this content! f == null || // NOTE (steveb): never close the BODY tag too early ( i == 2 && f.Name.EqualsIgnoreCase( "BODY" ) ) || // can't pop the root element. f.Name.EqualsIgnoreCase( _dtd.Name ) || f.CanContain( name, _dtd ) || // 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. !f.EndTagOptional ) break; } } if( i == 0 ) { // Tag was not found or is not allowed anywhere, ignore it and // continue on. return; } if( i >= top ) return; var t = (Node) _stack[ top ]; if( i == top - 1 && name.EqualsIgnoreCase( t.Name ) ) { // 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) _stack[ k ]; closing += "<" + n2.Name + ">"; } Log( "Element '{0}' not allowed inside '{1}', closing {2}.", name, t.Name, closing ); #endif } _state = State.AutoClose; _newNode = node; Pop(); // save this new node until we pop the others _popToDepth = i + 1; }
/// <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( _current == null ) { OpenInput(); } if( _node.Simulated ) { // return the next node _node.Simulated = false; _node = Top(); _state = _node.CurrentState; return true; } var foundnode = false; while( !foundnode ) { switch( _state ) { case State.Initial: _state = State.Markup; _current.ReadChar(); goto case State.Markup; case State.Eof: if( _current.Parent != null ) { _current.Close(); _current = _current.Parent; } else { return false; } break; case State.EndTag: if( _endTag.EqualsIgnoreCase( _node.Name ) ) { Pop(); // we're done! _state = State.Markup; goto case State.Markup; } Pop(); // close one element foundnode = true;// return another end element. break; case State.Markup: if( _node.IsEmpty ) { Pop(); } foundnode = ParseMarkup(); break; case State.PartialTag: Pop(); // remove text node. _state = State.Markup; foundnode = ParseTag( _partial ); break; case State.PseudoStartTag: foundnode = ParseStartTag( '<' ); break; case State.AutoClose: Pop(); // close next node. if( _stack.Count <= _popToDepth ) { _state = State.Markup; if( _newNode != null ) { Push( _newNode ); // now we're ready to start the new node. _newNode = null; _state = State.Markup; } else if( _node.NodeType == XmlNodeType.Document ) { _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: _state = State.Markup; goto case State.Markup; case State.Text: Pop(); goto case State.Markup; case State.PartialText: if( ParseText( _current.Lastchar, false ) ) { _node.NodeType = XmlNodeType.Whitespace; } foundnode = true; break; } if( foundnode && _node.NodeType == XmlNodeType.Whitespace && _whitespaceHandling == WhitespaceHandling.None ) { // strip out whitespace (caller is probably pretty printing the XML). foundnode = false; } if( foundnode || _state != State.Eof || _stack.Count <= 1 ) continue; _popToDepth = 1; _state = State.AutoClose; _node = Top(); return true; } if( !_foundRoot && ( NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Text || NodeType == XmlNodeType.CDATA ) ) { _foundRoot = true; if( IsHtml && ( NodeType != XmlNodeType.Element || !LocalName.EqualsIgnoreCase( "html" ) ) ) { // Simulate an HTML root element! _node.CurrentState = _state; var root = Push( "html", XmlNodeType.Element, null ); SwapTopNodes(); // make html the outer element. _node = root; root.Simulated = true; root.IsEmpty = false; _state = State.Markup; } return true; } return true; }
private void 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 ); _node = n2; return; }
private void Validate( Node node ) { if( _dtd == null ) return; var e = _dtd.FindElement( node.Name ); if( e == null ) return; node.DtdType = e; if( e.ContentModel.DeclaredContent == DeclaredContent.EMPTY ) node.IsEmpty = true; }
private Node Push( string name, XmlNodeType nt, string value ) { var result = (Node) _stack.Push(); if( result == null ) { result = new Node(); _stack[ _stack.Count - 1 ] = result; } result.Reset( name, nt, value ); _node = result; return result; }
private void Pop() { if( _stack.Count > 1 ) { _node = (Node) _stack.Pop(); } }
private bool ParseEndTag() { _state = State.EndTag; _current.ReadChar(); // consume '/' char. var name = ScanName( Tagterm ); var ch = _current.SkipWhitespace(); if( ch != '>' ) { Log( "Expected empty start tag '/>' sequence instead of '{0}'", ch ); _current.ScanToEnd( null, "Recovering", ">" ); } _current.ReadChar(); // consume '>' _endTag = name; // Make sure there's a matching start tag for it. var caseInsensitive = ( _folding == CaseFolding.None ); _node = (Node) _stack[ _stack.Count - 1 ]; for( var i = _stack.Count - 1; i > 0; i-- ) { var n = (Node) _stack[ i ]; if( !n.Name.Equals( name, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal ) ) continue; _endTag = n.Name; return true; } Log( "No matching start tag for '</{0}>'", name ); _state = State.Markup; return false; }
private void Init() { _state = State.Initial; _stack = new HwStack( 10 ); _node = Push( null, XmlNodeType.Document, null ); _node.IsEmpty = false; _sb = new StringBuilder(); _name = new StringBuilder(); _popToDepth = 0; _current = null; _partial = '\0'; _endTag = null; _attribute = null; _attributePos = 0; _newNode = null; _rootCount = 0; _foundRoot = false; _unknownNamespaces.Clear(); }
private static void ValidateAttribute( Node node, Attribute a ) { var e = node.DtdType; if( e == null ) return; var ad = e.FindAttribute( a.Name ); if( ad != null ) { a.DtdType = ad; } }