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