JD2022-TU1/main/tools/framework/JD.Collections/TreeNode.cs

383 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using JD.Kernel.StaticHelpers;
namespace JD.Collections
{
/// <summary>
/// TreeNode
/// </summary>
/// <typeparam name="T"></typeparam>
public class TreeNode<T> : IEnumerable<TreeNode<T>>
{
#region Variables
private T m_data;
private TreeNode<T> m_parent;
private TreeNode<T> m_child;
private TreeNode<T> m_sibling;
private TreeNode<T> m_lastChild;
#endregion
#region Constructor
/// <summary>
/// Default constructor.
/// </summary>
public TreeNode()
{
m_data = default(T);
}
/// <summary>
/// Alternative constructor.
/// </summary>
/// <param name="data">Node's data.</param>
public TreeNode(T data)
{
m_data = data;
}
#endregion
#region Properties
/// <summary>
/// Is root.
/// </summary>
public bool IsRoot
{
get
{
return m_parent == null;
}
}
/// <summary>
/// Is leaf.
/// </summary>
public bool IsLeaf
{
get
{
return m_child == null;
}
}
/// <summary>
/// Is isolated.
/// </summary>
public bool IsIsolated
{
get
{
return IsRoot && IsLeaf;
}
}
/// <summary>
/// Data.
/// </summary>
public T Data
{
get
{
return m_data;
}
set
{
m_data = value;
}
}
/// <summary>
/// Parent.
/// </summary>
public TreeNode<T> Parent
{
get
{
return m_parent;
}
}
#endregion
/// <summary>
/// Children.
/// </summary>
public IEnumerable<TreeNode<T>> Children
{
get
{
TreeNode<T> child = m_child;
while (child != null)
{
yield return child;
child = child.m_sibling;
}
}
}
/// <summary>
/// Enumerate directly through children data.
/// </summary>
public IEnumerable<T> ChildrenData
{
get
{
foreach (TreeNode<T> child in Children)
{
yield return child.Data;
}
}
}
/// <summary>
/// Descendants.
/// </summary>
public IEnumerable<TreeNode<T>> Descendants
{
get
{
foreach (TreeNode<T> child in Children)
{
yield return child;
foreach (TreeNode<T> descendant in child.Descendants)
{
yield return descendant;
}
}
}
}
public IEnumerable<TreeNode<T>> AllNodes
{
get
{
yield return this;
foreach (TreeNode<T> child in Descendants)
{
yield return child;
}
}
}
public IEnumerable<T> AllNodesData
{
get
{
yield return m_data;
foreach (TreeNode<T> child in Descendants)
{
yield return child.Data;
}
}
}
#region Methods
/// <summary>
/// Adds a new node with data as value.
/// </summary>
/// <param name="data">Data of the new child.</param>
/// <returns>The created node.</returns>
public TreeNode<T> Add(T data)
{
TreeNode<T> newChild = new TreeNode<T>(data);
AttachChild(newChild);
return newChild;
}
/// <summary>
/// Attach <paramref name="newChild"/> as a child.
/// </summary>
/// <remarks>The TreeNode <paramref name="newChild"/> will be detach from its current parent if it's not a root.</remarks>
/// <param name="newChild"></param>
public void AttachChild(TreeNode<T> 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;
}
/// <summary>
/// Detach from parent.
/// </summary>
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<T> 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;
}
/// <summary>
/// Is parent of <paramref name="node"/>.
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool IsParentOf(TreeNode<T> node)
{
Throw.IfNull(node, "node");
return node.Parent == this;
}
/// <summary>
/// Is ancestor of <paramref name="node"/>.
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool IsAncestorOf(TreeNode<T> node)
{
Throw.IfNull(node, "node");
return node.IsDescendantOf(this);
}
/// <summary>
/// Is decendant of <paramref name="node"/>.
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool IsDescendantOf(TreeNode<T> node)
{
Throw.IfNull(node, "node");
TreeNode<T> parent = m_parent;
while(parent != null && parent != node)
{
parent = parent.Parent;
}
return parent != null;
}
/// <summary>
/// Perform <paramref name="action"/> for all children.
/// </summary>
/// <param name="action"></param>
public void ForEachChildren(Action<TreeNode<T>> action)
{
Throw.IfNull(action, "action");
TreeNode<T> 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<T> nextChild = child.m_sibling;
action(child);
child = nextChild;
}
}
/// <summary>
/// Perform <paramref name="action"/> for all ancestor.
/// </summary>
/// <param name="action"></param>
public void ForEachAncestor(Action<TreeNode<T>> action)
{
Throw.IfNull(action, "action");
TreeNode<T> parent = m_parent;
while(parent != null)
{
//Cache the next parent. See reason in ForEachChildren
TreeNode<T> nextParent = parent.Parent;
action(parent);
parent = nextParent;
}
}
/// <summary>
/// Perform <paramref name="action"/> for all descendant.
/// </summary>
/// <remarks>Action application is done before calling the child.
/// DO NOT MODIFY THE HIERARCHY STRUCTURE FROM THE GIVEN ACTION.</remarks>
/// <param name="action"></param>
public void ForEachDescendant(Action<TreeNode<T>> action)
{
Throw.IfNull(action, "action");
TreeNode<T> child = m_child;
while(child != null)
{
action(child);
child.ForEachDescendant(action);
child = child.m_sibling;
}
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<TreeNode<T>> GetEnumerator()
{
return AllNodes.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return AllNodes.GetEnumerator();
}
#endregion
}
}