using System; using System.Collections.Generic; using JD.Kernel.StaticHelpers; namespace JD.Collections { /// /// TreeNode /// /// public class TreeNode : IEnumerable> { #region Variables private T m_data; private TreeNode m_parent; private TreeNode m_child; private TreeNode m_sibling; private TreeNode m_lastChild; #endregion #region Constructor /// /// Default constructor. /// public TreeNode() { m_data = default(T); } /// /// Alternative constructor. /// /// Node's data. public TreeNode(T data) { m_data = data; } #endregion #region Properties /// /// Is root. /// public bool IsRoot { get { return m_parent == null; } } /// /// Is leaf. /// public bool IsLeaf { get { return m_child == null; } } /// /// Is isolated. /// public bool IsIsolated { get { return IsRoot && IsLeaf; } } /// /// Data. /// public T Data { get { return m_data; } set { m_data = value; } } /// /// Parent. /// public TreeNode Parent { get { return m_parent; } } #endregion /// /// Children. /// public IEnumerable> Children { get { TreeNode child = m_child; while (child != null) { yield return child; child = child.m_sibling; } } } /// /// Enumerate directly through children data. /// public IEnumerable ChildrenData { get { foreach (TreeNode child in Children) { yield return child.Data; } } } /// /// Descendants. /// public IEnumerable> Descendants { get { foreach (TreeNode child in Children) { yield return child; foreach (TreeNode descendant in child.Descendants) { yield return descendant; } } } } public IEnumerable> AllNodes { get { yield return this; foreach (TreeNode child in Descendants) { yield return child; } } } public IEnumerable AllNodesData { get { yield return m_data; foreach (TreeNode child in Descendants) { yield return child.Data; } } } #region Methods /// /// Adds a new node with data as value. /// /// Data of the new child. /// The created node. public TreeNode Add(T data) { TreeNode newChild = new TreeNode(data); AttachChild(newChild); return newChild; } /// /// Attach as a child. /// /// The TreeNode will be detach from its current parent if it's not a root. /// public void AttachChild(TreeNode newChild) { Throw.IfNull(newChild, "newChild"); Throw.InvalidOperationIf(newChild == this, "Trying to attach a node to itself."); Throw.InvalidOperationIf(IsDescendantOf(newChild), "Try to attach a node to one of its descendants."); if (newChild.Parent != null) { newChild.Detach(); } if (m_child == null) { m_child = newChild; } else { m_lastChild.m_sibling = newChild; } newChild.m_parent = this; m_lastChild = newChild; } /// /// Detach from parent. /// public void Detach() { Throw.InvalidOperationIf(IsRoot, "Unable to detach a root node."); if (m_parent.m_child == this) { //Parent's first child is us: //Set the parent's new child as our sibling. m_parent.m_child = m_sibling; if (m_parent.m_lastChild == this) { m_parent.m_lastChild = null; } } else { //Parent's first child is not us: //Iterate in the children of the parent until a child's sibling is us TreeNode childIter = m_parent.m_child; while (childIter != null && childIter.m_sibling != this) { childIter = childIter.m_sibling; } System.Diagnostics.Debug.Assert(childIter != null, "Unable to find node in parent's child."); if (m_parent.m_lastChild == this) { m_parent.m_lastChild = childIter; } childIter.m_sibling = m_sibling; } m_parent = null; m_sibling = null; } /// /// Is parent of . /// /// /// public bool IsParentOf(TreeNode node) { Throw.IfNull(node, "node"); return node.Parent == this; } /// /// Is ancestor of . /// /// /// public bool IsAncestorOf(TreeNode node) { Throw.IfNull(node, "node"); return node.IsDescendantOf(this); } /// /// Is decendant of . /// /// /// public bool IsDescendantOf(TreeNode node) { Throw.IfNull(node, "node"); TreeNode parent = m_parent; while(parent != null && parent != node) { parent = parent.Parent; } return parent != null; } /// /// Perform for all children. /// /// public void ForEachChildren(Action> action) { Throw.IfNull(action, "action"); TreeNode child = m_child; while(child != null) { //Cache the next child in case the child pointer gets //invalidated in the action. This happens if, for example, you //delete an item in the sibling chain while iterating. In this //case, the pointer to m_sibling might be deleted, resulting in //loosing items. TreeNode nextChild = child.m_sibling; action(child); child = nextChild; } } /// /// Perform for all ancestor. /// /// public void ForEachAncestor(Action> action) { Throw.IfNull(action, "action"); TreeNode parent = m_parent; while(parent != null) { //Cache the next parent. See reason in ForEachChildren TreeNode nextParent = parent.Parent; action(parent); parent = nextParent; } } /// /// Perform for all descendant. /// /// Action application is done before calling the child. /// DO NOT MODIFY THE HIERARCHY STRUCTURE FROM THE GIVEN ACTION. /// public void ForEachDescendant(Action> action) { Throw.IfNull(action, "action"); TreeNode child = m_child; while(child != null) { action(child); child.ForEachDescendant(action); child = child.m_sibling; } } #endregion #region IEnumerable Members public IEnumerator> GetEnumerator() { return AllNodes.GetEnumerator(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return AllNodes.GetEnumerator(); } #endregion } }