using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Xml.Serialization; namespace RainmeterStudio.Core.Model { public class Tree : IList> { public T Data { get; set; } public ObservableCollection> Children { get; private set; } public Tree() { Children = new ObservableCollection>(); Data = default(T); } public Tree(T data) { Children = new ObservableCollection>(); Data = data; } public int IndexOf(Tree item) { return Children.IndexOf(item); } public void Insert(int index, Tree item) { Children.Insert(index, item); } public void RemoveAt(int index) { Children.RemoveAt(index); } public Tree this[int index] { get { return Children[index]; } set { Children[index] = value; } } public void Add(Tree item) { Children.Add(item); } public void Clear() { Children.Clear(); } public bool Contains(Tree item) { return Children.Contains(item); } public void CopyTo(Tree[] array, int arrayIndex) { Children.CopyTo(array, arrayIndex); } public int Count { get { return Children.Count; } } public bool IsReadOnly { get { return false; } } public bool Remove(Tree item) { return Children.Remove(item); } public IEnumerator> GetEnumerator() { return Children.GetEnumerator(); } public int IndexOf(T item) { return Children.IndexOf(new Tree(item)); } public void Insert(int index, T item) { Children.Insert(index, new Tree(item)); } public void Add(T item) { Children.Add(new Tree(item)); } public bool Contains(T item) { return Children.Contains(new Tree(item)); } public void CopyTo(T[] array, int arrayIndex) { foreach (var node in Children) array[arrayIndex++] = node.Data; } public bool Remove(T item) { return Children.Remove(new Tree(item)); } public override bool Equals(object obj) { Tree other = obj as Tree; // Types are different, so not equal if (other == null) return false; // Compare data if (!object.Equals(Data, other.Data)) return false; // Compare children array return Children.SequenceEqual(other.Children); } public override int GetHashCode() { int hash = ((Data == null) ? 0 : Data.GetHashCode()); foreach (var c in Children) hash = hash * 7 + c.GetHashCode(); return hash; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Children.GetEnumerator(); } public void TreeExpand(bool p) { throw new NotImplementedException(); } } /// /// Extension methods for trees /// public static class TreeExtensions { /// /// Tree traversal orders /// public enum TreeTraversalOrder { BreadthFirst, DepthFirst, DepthFirstPreOrder = DepthFirst, DepthFirstPostOrder } /// /// Traverses a tree /// /// Tree data type /// Root node of tree /// Traversal order /// An enumeration of the nodes in the specified traverse order public static IEnumerable> Traverse(this Tree root, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst) { if (order == TreeTraversalOrder.BreadthFirst) return TraverseBF(root); else return TraverseDF(root, order); } private static IEnumerable> TraverseDF(this Tree root, TreeTraversalOrder order) { // Preorder - return root first if (order == TreeTraversalOrder.DepthFirstPreOrder) yield return root; // Return children foreach (var child in root.Children) foreach (var node in TraverseDF(child, order)) yield return node; // Postorder - return root last if (order == TreeTraversalOrder.DepthFirstPostOrder) yield return root; } private static IEnumerable> TraverseBF(this Tree root) { // Create a queue containing the root Queue> queue = new Queue>(); queue.Enqueue(root); // While there are elements in the queue while (queue.Count > 0) { // Return next node in tree var node = queue.Dequeue(); yield return node; // Enqueue node's children foreach (var child in node.Children) queue.Enqueue(child); } } /// /// Applies an action to every node of the tree /// /// Tree data type /// Root node of tree /// Action to apply /// Traversal order public static void Apply(this Tree root, Action> action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst) { // Safety check if (action == null) return; // Apply action foreach (var node in Traverse(root, order)) action(node); } /// /// Applies an action to every node of the tree /// /// Tree data type /// Root node of tree /// Action to apply /// Traversal order public static void ApplyToData(this Tree root, Action action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst) where T : class { // Safety check if (action == null) return; // Apply action foreach (var node in Traverse(root, order)) action(node.Data); } /// /// Rebuilds the tree by applying the specified transform function /// /// Data type of tree /// Data type of rebuilt tree /// The root node /// The transform function /// The transformed tree public static Tree Transform(this Tree root, Func, Tree> transformFunction) { // Safety check if (transformFunction == null) throw new ArgumentNullException("Transform function cannot be null."); // Build root Tree resRoot = transformFunction(root); // Add children foreach (var node in root.Children) resRoot.Children.Add(Transform(node, transformFunction)); // Return return resRoot; } /// /// Rebuilds the tree by applying the specified transform function /// /// Data type of tree /// Data type of rebuilt tree /// The root node /// The transform function /// The transformed tree public static Tree TransformData(this Tree root, Func transformFunction) { // Safety check if (transformFunction == null) throw new ArgumentNullException("Transform function cannot be null."); // Build root Tree resRoot = new Tree(transformFunction(root.Data)); // Add children foreach (var node in root.Children) resRoot.Children.Add(TransformData(node, transformFunction)); // Return return resRoot; } } }