using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; namespace Inspectron.Settings { [Serializable] public class Tree { /// /// Constructor of empty tree public Tree() : this(default(T)) { } /// /// Constructor for tree with one node /// Value associated with this tree public Tree(T value) { m_value = value; m_children = new ChildCollection(this); } /// /// Gets or sets tree's parent. Is null if this is a root node. public Tree Parent { get { return m_parent; } set { if (m_parent != value) { if (m_parent != null) m_parent.Children.Remove(this); m_parent = value; if (m_parent != null) m_parent.Children.Add(this); } } } /// /// Gets the list of children nodes. Is the same as 'this' because this Tree implements IList. public IList> Children { get { return m_children; } } /// /// Gets or sets the value associated with the tree public T Value { get { return m_value; } set { m_value = value; } } /// /// Tests for equality /// Other object /// True iff object is a tree with the same structure and values as this tree public override bool Equals(object obj) { if (obj == null) return false; Tree other = obj as Tree; if (other == null) return false; if (!m_value.Equals(other.m_value)) return false; if (m_children.Count != other.m_children.Count) return false; for (int i = 0; i < m_children.Count; i++) if (!(m_children[i]).Equals(other.m_children[i])) return false; return true; } /// /// Tests for similarity /// Other tree /// True iff other has the same structure as this tree /// Same structure means same node structure public bool Similar(Tree other) { if (this == other) return true; if (other == null) return false; if (m_children.Count != other.m_children.Count) return false; for (int i = 0; i < m_children.Count; i++) if (!(m_children[i]).Similar(other.m_children[i])) return false; return true; } /// /// Returns hash code for tree /// Hash code public override int GetHashCode() { int result = 0; foreach (Tree tree in PreOrder) result ^= tree.Value.GetHashCode(); return result; } /// /// Converts tree to string of the form "(Value(Child1),...,(ChildN))" /// String representation of tree public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append('('); Stringify(builder); builder.Append(')'); return builder.ToString(); } private void Stringify(StringBuilder builder) { builder.Append(m_value.ToString()); if (!IsLeaf) { builder.Append('('); bool firstTime = true; foreach (Tree t in m_children) { if (firstTime) firstTime = false; else builder.Append(','); t.Stringify(builder); } builder.Append(')'); } } /// /// Tests if this tree is a descendant of another /// Possible ancestor /// True iff this tree is a descendant of the other /// A tree is considered a descendant of itself public bool IsDescendantOf(Tree ancestor) { Tree descendant = this; while (descendant != null) { if (ancestor == descendant) return true; descendant = descendant.Parent; } return false; } /// /// Gets whether a tree is a leaf (no children) public bool IsLeaf { get { return m_children.Count == 0; } } /// /// Gets level, or depth, in tree public int Level { get { int result = 0; Tree ancestor = m_parent; while (ancestor != null) { result++; ancestor = ancestor.Parent; } return result; } } /// /// Gets number of descendants, including the tree itself public int DescendantCount { get { int n = 0; foreach (Tree tree in PreOrder) n++; return n; } } /// /// Gets an enumeration of all the nodes of the tree in pre-order (depth first). /// For example, the root node is first, followed by its first child (and its /// children and so on) and then the second child of the root (and its children /// and so on) etc. public IEnumerable> PreOrder { get { Stack> nodes = new Stack>(); nodes.Push(this); while (nodes.Count > 0) { Tree node = nodes.Pop(); yield return node; // push children in reverse order for (int i = node.m_children.Count - 1; i >= 0; i--) nodes.Push(node.m_children[i]); } } } /// /// Gets an enumeration of all the nodes of the tree in post-order. This means that for /// each node, the children are visited first (starting with the first child) and then /// the parent node is enumerated. public IEnumerable> PostOrder { get { // push each non-leaf node twice to represent nodes whose children haven't been visited // rather than storing a bit on each node. Stack> nodes = new Stack>(); nodes.Push(this); if (!IsLeaf) nodes.Push(this); while (nodes.Count > 1) { Tree node = nodes.Pop(); if (node != nodes.Peek()) { yield return node; } else { // push children in reverse order for (int i = node.m_children.Count - 1; i >= 0; i--) { Tree child = node.m_children[i]; nodes.Push(child); if (!child.IsLeaf) nodes.Push(child); } } } yield return nodes.Pop(); } } /// /// Gets an enumeration of all the nodes in a breadth-first order. This means that the /// root is enumerated first (level 0), followed by all of its children (level 1), /// followed by all of their children (level 2), and so on. public IEnumerable> LevelOrder { get { Queue> nodes = new Queue>(); nodes.Enqueue(this); while (nodes.Count > 0) { Tree node = nodes.Dequeue(); yield return node; // queue children foreach (Tree child in node.m_children) nodes.Enqueue(child); } } } private class ChildCollection : Collection> { public ChildCollection(Tree parent) { m_parent = parent; } protected override void InsertItem(int index, Tree item) { item.m_parent = m_parent; base.InsertItem(index, item); } protected override void RemoveItem(int index) { Items[index].m_parent = null; base.RemoveItem(index); } protected override void SetItem(int index, Tree item) { Items[index].m_parent = null; item.m_parent = m_parent; base.SetItem(index, item); } protected override void ClearItems() { foreach (Tree subTree in Items) subTree.m_parent = null; base.ClearItems(); } private readonly Tree m_parent; } private T m_value; private Tree m_parent; private readonly ChildCollection m_children; } }