#if !(NET20)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Vanara.Collections
{
public static partial class TreeExtension
{
private const char defaultSeparator = '\\';
/// Parses and adds the entry to the hierarchy, creating any parent entries as required.
/// The tree.
/// The delimiter separated list of child node values.
/// The index of the character in at which to start reading node values.
/// The separator character used as the delimiter.
public static void AddEntry(this Tree tree, string entry, int startIndex = 0, char separator = defaultSeparator)
{
AddEntry((TreeNode)tree, entry, startIndex, separator);
}
///
/// Gets the child, potentially many levels down, by supplying a backslash ('\') separated list of children's string representations of their .
///
/// The tree.
/// The backslash ('\') separated list of children's string representations.
/// The requested , or null if not found.
public static TreeNode NodeFromPath(this Tree tree, string childPath)
{
childPath = childPath?.TrimStart(defaultSeparator);
if (string.IsNullOrEmpty(childPath)) return tree;
try
{
return childPath.Split(defaultSeparator).Aggregate((TreeNode)tree, (current, key) => current.Children.First(p => string.Equals(key, p.Value, StringComparison.InvariantCulture)));
}
catch
{
return null;
}
}
private static void AddEntry(this TreeNode tree, string entry, int startIndex, char separator)
{
if (string.IsNullOrEmpty(entry)) throw new ArgumentNullException(nameof(entry));
if (startIndex >= entry.Length)
return;
var endIndex = entry.IndexOf(separator, startIndex);
if (endIndex == -1)
endIndex = entry.Length;
var key = entry.Substring(startIndex, endIndex - startIndex);
if (string.IsNullOrEmpty(key))
return;
// Now add the rest to the new item's children
var item = tree.Children.Contains(key) ? tree.Children.GetNode(key) : tree.Children.Add(key);
AddEntry(item, entry, endIndex + 1);
/*while (startIndex < entry.Length)
{
var endIndex = entry.IndexOf(pathDesignator, startIndex);
if (endIndex == -1) endIndex = entry.Length;
var key = entry.Substring(startIndex, endIndex - startIndex);
if (string.IsNullOrEmpty(key)) return;
node = enumChildren(node).FirstOrDefault(n => getPath(n) == key) ?? addChild(node, key);
startIndex = endIndex + 1;
}*/
}
}
public static partial class TupleExtension
{
public static void Add(this IList> list, T1 item1, T2 item2)
{
list.Add(new Tuple(item1, item2));
}
}
/// A hierarchical tree containing nodes of type .
/// The type of the node.
public class Tree : TreeNode where T : IComparable
{
/// Gets the zero-based depth of the tree node in the .
/// The zero-based depth of the tree node in the .
public override int Level => 0;
/// Gets the parent tree node of the current tree node.
/// A that represents the parent of the current tree node.
public override TreeNode Parent => null;
/// Gets the root node of the tree. If this node has no parent, then it will be returned.
/// The root node.
public override TreeNode RootNode => this;
/// Creates a tree from a list of paired values.
/// A list of paired values.
/// A tree of leafs constructed from the list.
public static Tree FromParentChildPairs(ICollection> input)
{
if (input == null) throw new ArgumentNullException(nameof(input));
var ret = new Tree();
var uniq1 = input.Select(i => i.Item1).Distinct();
var uniq2 = input.Select(i => i.Item2).Distinct();
var roots = uniq1.Except(uniq2).ToArray();
if (roots.Length == 1)
{
ret.Value = roots[0];
roots = input.Where(i => Equals(i.Item1, ret.Value)).Select(i => i.Item2).ToArray();
}
ret.Children.AddRange(roots);
var groups = input.GroupBy(i => i.Item1).ToArray();
foreach (var n in ret.Children)
n.AddChildren(groups);
return ret;
}
/// Gets the child specified by the list of nodes.
/// The child values starting with the top-most child key.
/// The requested , or null if not found.
public TreeNode GetChild(params T[] childKeys)
{
try
{
return childKeys.Aggregate((TreeNode)this, (current, key) => current.Children.First(n => Equals(n.Value, key)));
}
catch
{
return null;
}
}
}
///
///
///
public class TreeNode : IDisposable where T : IComparable
{
/// Initializes a new instance of the class.
public TreeNode() { Children = new TreeNodeList(this); }
/// Initializes a new instance of the class.
/// The value to assign to the property.
/// Optionally nest this node to an existing parent .
public TreeNode(T value, TreeNode parent = null) : this()
{
Value = value;
Parent = parent;
}
/// Gets the children (leafs) of the current node.
/// The children nodes.
public TreeNodeList Children { get; internal set; }
/// Gets the parent tree node of the current tree node.
/// A that represents the parent of the current tree node.
public virtual TreeNode Parent { get; internal set; }
/// Gets the root node of the tree. If this node has no parent, then it will be returned.
/// The root node.
public virtual TreeNode RootNode
{
get
{
var r = this;
while (r.Parent != null) r = r.Parent;
return r;
}
}
/// Gets or sets the value tied to this .
/// The value.
public virtual T Value { get; set; }
/// Gets the zero-based depth of the tree node in the .
/// The zero-based depth of the tree node in the .
public virtual int Level => Parent?.Level + 1 ?? 0;
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public virtual void Dispose()
{
Children.Dispose();
(Value as IDisposable)?.Dispose();
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => $"{Value}";
internal void AddChildren(IGrouping>[] groups)
{
foreach (var child in groups.Where(g => Equals(g.Key, Value)))
foreach (var pair in child)
Children.Add(pair.Item2).AddChildren(groups);
}
/// A list of leaf nodes for a . Exposed through the property.
/// The type held by a node.
///
///
public class TreeNodeList : IList>, IDisposable
{
private readonly TreeNode host;
private readonly IList> list;
// Prevent construction
private TreeNodeList() { }
/// Initializes a new instance of the class.
/// The host node.
internal TreeNodeList(TreeNode hostNode)
{
host = hostNode;
list = new List>();
}
/// Gets the values for all nodes in this list.
/// The node values.
public IEnumerable Values => list.Select(n => n.Value);
///
/// Gets the number of elements contained in the .
///
public int Count => list.Count;
///
/// Gets a value indicating whether the is read-only.
///
bool ICollection>.IsReadOnly => false;
/// Gets or sets the at the specified index. This will remove the tree node at this position
/// The .
/// The index.
///
public TreeNode this[int index]
{
get => list[index];
set
{
value.Parent = host;
list[index] = value;
}
}
/// Adds the specified item.
/// The item.
///
public virtual TreeNode Add(T item)
{
var ret = MakeNode(item);
list.Add(ret);
return ret;
}
/// Adds an item to the .
/// The object to add to the .
public void Add(TreeNode item)
{
item.Parent = host;
}
/// Adds the range.
/// The items.
public void AddRange(IEnumerable items)
{
foreach (var item in items)
Add(item);
}
/// Adds the range.
/// The items.
public void AddRange(params T[] items)
{
AddRange(items.AsEnumerable());
}
/// Removes all items from the .
public virtual void Clear()
{
foreach (var n in list)
n.Dispose();
list.Clear();
}
///
/// Determines whether the contains a specific value.
///
/// The object to locate in the .
///
/// true if is found in the ; otherwise, false.
///
public bool Contains(TreeNode item) => list.Contains(item);
/// Determines whether [contains] [the specified item].
/// The item.
/// true if [contains] [the specified item]; otherwise, false.
public bool Contains(T item) => IndexOf(item) != -1;
///
/// Copies the elements of the to an , starting at a particular index.
///
/// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.
/// The zero-based index in at which copying begins.
public void CopyTo(TreeNode[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
/// Copies to.
/// The array.
/// Index of the array.
public void CopyTo(T[] array, int arrayIndex)
{
list.Select(n => n.Value).ToArray().CopyTo(array, arrayIndex);
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public virtual void Dispose()
{
Clear();
}
/// Returns an enumerator that iterates through the collection.
///
/// A that can be used to iterate through the collection.
///
public IEnumerator> GetEnumerator() => list.GetEnumerator();
/// Gets the node.
/// The item.
///
public TreeNode GetNode(T item) => list.First(n => Equals(n.Value, item));
/// Indexes the of.
/// The item.
///
public virtual int IndexOf(T item) => ((List>)list).FindIndex(n => Equals(n.Value, item));
/// Determines the index of a specific item in the .
/// The object to locate in the .
/// The index of if found in the list; otherwise, -1.
public int IndexOf(TreeNode item) => list.IndexOf(item);
/// Inserts the specified index.
/// The index.
/// The item.
///
public virtual TreeNode Insert(int index, T item)
{
var n = MakeNode(item);
Insert(index, n);
return n;
}
/// Inserts an item to the at the specified index.
/// The zero-based index at which should be inserted.
/// The object to insert into the .
public void Insert(int index, TreeNode item)
{
list.Insert(index, item);
}
///
/// Removes the first occurrence of a specific object from the .
///
/// The object to remove from the .
///
/// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original .
///
public bool Remove(TreeNode item) => list.Remove(item);
/// Removes the specified item.
/// The item.
///
public bool Remove(T item)
{
var i = IndexOf(item);
if (i == -1) return false;
list.RemoveAt(i);
return true;
}
/// Removes the item at the specified index.
/// The zero-based index of the item to remove.
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => $"Count:{list.Count}";
/// Returns an enumerator that iterates through a collection.
///
/// An object that can be used to iterate through the collection.
///
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal void InternalAdd(TreeNode item)
{
list.Add(item);
}
protected virtual TreeNode MakeNode(T item) => new TreeNode(item, host);
}
}
}
#endif