| // 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 | |
| } |