// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html import ( "fmt" ) // checkTreeConsistency checks that a node and its descendants are all // consistent in their parent/child/sibling relationships. func checkTreeConsistency(n *Node) error { return checkTreeConsistency1(n, 0) } func checkTreeConsistency1(n *Node, depth int) error { if depth == 1e4 { return fmt.Errorf("html: tree looks like it contains a cycle") } if err := checkNodeConsistency(n); err != nil { return err } for c := n.FirstChild; c != nil; c = c.NextSibling { if err := checkTreeConsistency1(c, depth+1); err != nil { return err } } return nil } // checkNodeConsistency checks that a node's parent/child/sibling relationships // are consistent. func checkNodeConsistency(n *Node) error { if n == nil { return nil } nParent := 0 for p := n.Parent; p != nil; p = p.Parent { nParent++ if nParent == 1e4 { return fmt.Errorf("html: parent list looks like an infinite loop") } } nForward := 0 for c := n.FirstChild; c != nil; c = c.NextSibling { nForward++ if nForward == 1e6 { return fmt.Errorf("html: forward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } nBackward := 0 for c := n.LastChild; c != nil; c = c.PrevSibling { nBackward++ if nBackward == 1e6 { return fmt.Errorf("html: backward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } if n.Parent != nil { if n.Parent == n { return fmt.Errorf("html: inconsistent parent relationship") } if n.Parent == n.FirstChild { return fmt.Errorf("html: inconsistent parent/first relationship") } if n.Parent == n.LastChild { return fmt.Errorf("html: inconsistent parent/last relationship") } if n.Parent == n.PrevSibling { return fmt.Errorf("html: inconsistent parent/prev relationship") } if n.Parent == n.NextSibling { return fmt.Errorf("html: inconsistent parent/next relationship") } parentHasNAsAChild := false for c := n.Parent.FirstChild; c != nil; c = c.NextSibling { if c == n { parentHasNAsAChild = true break } } if !parentHasNAsAChild { return fmt.Errorf("html: inconsistent parent/child relationship") } } if n.PrevSibling != nil && n.PrevSibling.NextSibling != n { return fmt.Errorf("html: inconsistent prev/next relationship") } if n.NextSibling != nil && n.NextSibling.PrevSibling != n { return fmt.Errorf("html: inconsistent next/prev relationship") } if (n.FirstChild == nil) != (n.LastChild == nil) { return fmt.Errorf("html: inconsistent first/last relationship") } if n.FirstChild != nil && n.FirstChild == n.LastChild { // We have a sole child. if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil { return fmt.Errorf("html: inconsistent sole child's sibling relationship") } } seen := map[*Node]bool{} var last *Node for c := n.FirstChild; c != nil; c = c.NextSibling { if seen[c] { return fmt.Errorf("html: inconsistent repeated child") } seen[c] = true last = c } if last != n.LastChild { return fmt.Errorf("html: inconsistent last relationship") } var first *Node for c := n.LastChild; c != nil; c = c.PrevSibling { if !seen[c] { return fmt.Errorf("html: inconsistent missing child") } delete(seen, c) first = c } if first != n.FirstChild { return fmt.Errorf("html: inconsistent first relationship") } if len(seen) != 0 { return fmt.Errorf("html: inconsistent forwards/backwards child list") } return nil }