#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