|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace General.Hierarchy
{
/// <summary>
/// Represents a single node within a tree.
///
/// when added to a Tree, the node becomes searchable from within the tree, and new nodes added/updated in the
/// tree will be reflected in the node.
/// Changing the parent node value will update the children of the new parent.
///
/// The TreeNode does not need to be added to a tree to form a hierarchy. New nodes can be created and set as the
/// parent of any existing node.
///
/// The hierarchy enumerations on the node will navigate through the hierarchy generated by the linked nodes
/// and their children, - these methods do not require the TreeNode to be in a Tree.
///
///
///
///
/// </summary>
/// <typeparam name="V">
///
/// </typeparam>
[Serializable]
public class TreeNode<V>
{
#region Fields
/// <summary>
/// the owning Tree
/// </summary>
protected Tree<V> _owner = null;
/// <summary>
/// the owning parent.
/// </summary>
protected TreeNode<V> _parent = null;
/// <summary>
/// local cache of child objects:
/// </summary>
private List<TreeNode<V>> _children = new List<TreeNode<V>>();
/// <summary>
/// handler for the parent object node added event.
/// </summary>
private Tree<V>.NodeEvent _nodeAddedHandler = null;
#endregion
#region Constructor
/// <summary>
/// default parameterless constructor.
/// </summary>
public TreeNode()
{
_nodeAddedHandler = new Tree<V>.NodeEvent(_owner_NodeAdded);
}
#endregion
#region Properties
/// <summary>
/// owner property.
/// </summary>
public Tree<V> Owner { get { return _owner; } }
/// <summary>
/// parent property.
/// </summary>
public TreeNode<V> Parent
{
get { return _parent; }
set
{
if (_parent == null)
{
_parent = value;
if (!_parent._children.Contains(this))
_parent._children.Add(this);
}
else
{
// change the parent of this node:
_parent._children.Remove(this);
_parent = value;
if (!_parent._children.Contains(this))
_parent._children.Add(this);
}
}
}
/// <summary>
/// value property.
/// </summary>
public V Value { get; set; }
/// <summary>
/// the index within the parent tree collection.
/// </summary>
public int Index { get; set; }
/// <summary>
/// query the children of this node.
/// </summary>
public IEnumerable<TreeNode<V>> Children
{
get
{
return (from c in _children
select c);
}
}
/// <summary>
/// query this nodes siblings.
/// </summary>
public IEnumerable<TreeNode<V>> Siblings
{
get
{
if (_parent == null)
yield break;
else
{
foreach (var sibling in _parent.Children)
if (sibling != this)
yield return sibling;
}
}
}
/// <summary>
/// returns the parent of this node, it's parent and so on until the top of the tree is reached.
/// </summary>
public IEnumerable<TreeNode<V>> PathToTop
{
get {
var node = this._parent;
while (node != null)
{
yield return node;
node = node._parent;
}
}
}
/// <summary>
/// returns all the descendents of the current node in hierarchy order.
/// </summary>
public IEnumerable<TreeNode<V>> Descendents
{
get
{
foreach (var node in _children)
{
yield return node;
foreach (var descendent in node.Descendents)
yield return descendent;
}
}
}
/// <summary>
/// hierarchy path for this node.
/// </summary>
public String Path
{
get
{
if (_parent == null)
return this.Value.ToString();
else
return _parent.Path + "\\" + this.Value.ToString();
}
}
#endregion
#region Collection Modification Methods
/// <summary>
/// set the owning tree. the tree must already contain the node.
/// </summary>
/// <param name="owner"></param>
public void SetOwner(Tree<V> owner)
{
if (_owner != null && _owner != owner)
throw new ApplicationException("This node already belongs to a different tree!");
if (!owner.Contains(this))
throw new ApplicationException("Owner does not contain this node!");
// set the owner and index:
this._owner = owner;
this.Index = owner.IndexOf(this);
// attach an event-handler to the tree's node-added event to keep the children up to date.
this._owner.NodeAdded += _nodeAddedHandler;
}
/// <summary>
/// detach this node from it's owning tree.
/// </summary>
public void Detach()
{
RemoveFromTree(_owner);
}
/// <summary>
/// remove this node, and all it's parents from the tree. the node cannot have children.
/// </summary>
/// <param name="owner"></param>
public void RemoveFromTree(Tree<V> owner)
{
if (_children.Count > 0)
throw new ApplicationException("Cannot remove a node with children!");
if (_owner == null)
throw new ApplicationException("This node does not have an owner to remove");
if (Parent != null)
Parent.RemoveFromTree(owner);
if (_owner != null && _owner == owner)
{
_owner.Remove(this);
_owner = null;
}
}
/// <summary>
/// add this node to the specified tree. the parent node must already be
/// in the collection.
/// </summary>
/// <param name="tree"></param>
public void AddToTree(Tree<V> tree)
{
if (_owner != null && _owner != tree)
throw new ApplicationException("This node already belongs to a different tree!");
if (!tree.Contains(this))
tree.Add(this);
}
/// <summary>
/// update this nodes' internal list of children from the Tree's GetChildren method.
/// </summary>
public void UpdateChildren()
{
if (_owner != null)
{
_children.Clear();
foreach (var node in _owner.GetChildren(this))
{
_children.Add(node);
}
}
else
throw new ApplicationException("Tree node must have an Owner!");
}
#endregion
#region Handlers
/// <summary>
/// Handle the "NodeAdded" event from the owning Tree object;
/// if the added node is a parent to this node, add the new node to this node's children collection.
/// </summary>
/// <param name="sender">this will be the Tree control</param>
/// <param name="node">
/// the newly added node.
/// </param>
void _owner_NodeAdded(object sender, TreeNode<V> node)
{
if (node.Parent == this)
{
if (!_children.Contains(node))
_children.Add(node);
}
}
#endregion
/// <summary>
/// a node is unique from it's index....
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
// generate the hash from the parent and value and index
if (_parent != null)
{
return (_parent.Value.ToString() + Value.ToString()).GetHashCode() + Index;
}
else
return Value.ToString().GetHashCode() + Index;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Wrote his first computer game in Microsoft Basic, on a Dragon 32 at age 7. It wasn't very good.
Has been working as a consultant and developer for the last 15 years,
Discovered C# shortly after it was created, and hasn't looked back.
Feels weird talking about himself in the third person.