Skip to content

Commit 8c425e6

Browse files
author
Oren (electricessence)
committed
Fixes
1 parent 1e46993 commit 8c425e6

7 files changed

Lines changed: 439 additions & 268 deletions

File tree

IChild.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,28 @@ namespace Open.Hierarchy
44
{
55
public interface IChild
66
{
7+
/// <summary>
8+
/// The parent of this child.
9+
/// </summary>
710
object Parent { get; }
811
}
912

1013
public interface IChild<out TParent> : IChild
1114
{
15+
/// <summary>
16+
/// The generic parent of this child.
17+
/// </summary>
1218
new TParent Parent { get; }
1319
}
1420

1521
public static class ChildExtensions
1622
{
23+
/// <summary>
24+
/// Crawls the ancestor lineage and returns them.
25+
/// </summary>
26+
/// <typeparam name="TNode">The node type.</typeparam>
27+
/// <param name="node">The child node to use.</param>
28+
/// <returns>An enumerable of the ancestors.</returns>
1729
public static IEnumerable<TNode> GetAncestors<TNode>(
1830
this TNode node)
1931
where TNode : IChild<TNode>
@@ -26,6 +38,12 @@ public static IEnumerable<TNode> GetAncestors<TNode>(
2638
}
2739
}
2840

41+
/// <summary>
42+
/// Crawls the ancestor lineage returns the first node with no parent.
43+
/// </summary>
44+
/// <typeparam name="TNode">The node type.</typeparam>
45+
/// <param name="node">The child node to start with.</param>
46+
/// <returns>The root node.</returns>
2947
public static TNode GetRoot<TNode>(
3048
this TNode node)
3149
where TNode : IChild<TNode>

IHaveRoot.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22
{
33
public interface IHaveRoot
44
{
5+
/// <summary>
6+
/// The root of this.
7+
/// </summary>
58
object Root { get; }
69
}
710

811
public interface IHaveRoot<out TRoot> : IHaveRoot
912
{
13+
/// <summary>
14+
/// The generic root of this.
15+
/// </summary>
1016
new TRoot Root { get; }
1117
}
1218
}

IParent.cs

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,21 @@
1-
using System.Collections.Generic;
2-
using System.Linq;
1+
using System.Collections.Generic;
32

43
namespace Open.Hierarchy
54
{
65
public interface IParent
76
{
7+
/// <summary>
8+
/// Read only access to the contained children.
9+
/// </summary>
10+
// ReSharper disable once ReturnTypeCanBeEnumerable.Global
811
IReadOnlyList<object> Children { get; }
912
}
1013

1114
public interface IParent<out TChild> : IParent
1215
{
13-
new IReadOnlyList<TChild> Children { get; }
14-
}
15-
16-
public static class ParentExtensions
17-
{
18-
1916
/// <summary>
20-
/// Returns all descendant nodes.
17+
/// Generic read only access to the contained children.
2118
/// </summary>
22-
/// <param name="traversal">The mode by which the tree is traversed.</param>
23-
/// <returns>The enumeration of all the descendant nodes.</returns>
24-
public static IEnumerable<TNode> GetDescendants<TNode>(
25-
this TNode node, TraversalMode traversal = TraversalMode.BreadthFirst)
26-
where TNode : IParent<TNode>
27-
{
28-
// Attempt to be more breadth first.
29-
switch (traversal)
30-
{
31-
case TraversalMode.BreadthFirst:
32-
foreach (var child in node.Children)
33-
yield return child;
34-
35-
var grandchildren = node.Children.SelectMany(c => c.Children);
36-
foreach (var grandchild in grandchildren)
37-
yield return grandchild;
38-
39-
foreach (var descendant in grandchildren
40-
.SelectMany(c => c.GetDescendants()))
41-
yield return descendant;
42-
43-
break;
44-
45-
46-
case TraversalMode.DepthFirst:
47-
48-
foreach (var descendant in node.Children
49-
.SelectMany(c => c.GetDescendants(TraversalMode.DepthFirst)))
50-
yield return descendant;
51-
52-
break;
53-
}
54-
55-
}
56-
57-
/// <summary>
58-
/// Returns all descendant nodes and includes this node.
59-
/// Breadth first returns this node first. Depth first returns this node last.
60-
/// </summary>
61-
/// <param name="traversal">The mode by which the tree is traversed.</param>
62-
/// <returns>The enumeration of all the descendant nodes including this one.</returns>
63-
public static IEnumerable<TNode> GetNodes<TNode>(
64-
this TNode node, TraversalMode traversal = TraversalMode.BreadthFirst)
65-
where TNode : IParent<TNode>
66-
{
67-
if (traversal == TraversalMode.BreadthFirst)
68-
yield return node;
69-
foreach (var descendant in node.GetDescendants(traversal))
70-
yield return descendant;
71-
if (traversal == TraversalMode.DepthFirst)
72-
yield return node;
73-
}
19+
new IReadOnlyList<TChild> Children { get; }
7420
}
75-
}
21+
}

Node.Factory.cs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
using Open.Disposable;
2+
using System;
3+
4+
namespace Open.Hierarchy
5+
{
6+
public sealed partial class Node<T>
7+
{
8+
/// <summary>
9+
/// Used for mapping a tree of evaluations which do not have access to their parent nodes.
10+
/// </summary>
11+
public sealed class Factory : DisposableBase
12+
{
13+
#region Creation, Recycling, and Disposal
14+
public Factory()
15+
{
16+
_pool = new ConcurrentQueueObjectPool<Node<T>>(
17+
() => new Node<T>(), PrepareForPool, null, ushort.MaxValue);
18+
}
19+
20+
protected override void OnDispose(bool calledExplicitly)
21+
{
22+
if (calledExplicitly)
23+
{
24+
DisposeOf(ref _pool);
25+
}
26+
}
27+
28+
ConcurrentQueueObjectPool<Node<T>> _pool;
29+
30+
/// <summary>
31+
/// Gets a blank node.
32+
/// </summary>
33+
/// <returns>A blank node.</returns>
34+
public Node<T> GetBlankNode()
35+
{
36+
AssertIsAlive();
37+
38+
return _pool?.Take();
39+
}
40+
41+
/// <summary>
42+
/// Recycles the node into the object pool, returning the value contained.
43+
/// </summary>
44+
/// <param name="n">The node to be recycled.</param>
45+
/// <returns>The value contained in the node.</returns>
46+
public T Recycle(Node<T> n)
47+
{
48+
AssertIsAlive();
49+
50+
return RecycleInternal(n);
51+
}
52+
53+
internal T RecycleInternal(Node<T> n)
54+
{
55+
var value = n.Value;
56+
var p = _pool;
57+
if (p == null) n.Teardown();
58+
else p.Give(n);
59+
return value;
60+
}
61+
62+
void PrepareForPool(Node<T> n)
63+
=> n.Recycle(this);
64+
#endregion
65+
66+
/// <summary>
67+
/// Clones a node by recreating the tree and copying the values.
68+
/// </summary>
69+
/// <param name="target">The node to replicate.</param>
70+
/// <param name="newParentForClone">
71+
/// If a parent is specified it will use that node as its parent.
72+
/// By default it ends up being detatched.
73+
/// </param>
74+
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
75+
/// <returns>The copy of the tree/branch.</returns>
76+
public Node<T> Clone(
77+
Node<T> target,
78+
Node<T> newParentForClone = null,
79+
Action<Node<T>, Node<T>> onNodeCloned = null)
80+
{
81+
AssertIsAlive();
82+
83+
var clone = _pool.Take();
84+
clone.Value = target.Value;
85+
newParentForClone?.Add(clone);
86+
87+
foreach (var child in target._children)
88+
clone.Add(Clone(child, clone, onNodeCloned));
89+
90+
onNodeCloned?.Invoke(target, clone);
91+
92+
return clone;
93+
}
94+
95+
96+
/// <summary>
97+
/// Clones a node by recreating the tree and copying the values.
98+
/// </summary>
99+
/// <param name="target">The node to replicate.</param>
100+
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
101+
/// <returns>The copy of the tree/branch.</returns>
102+
public Node<T> Clone(
103+
Node<T> target,
104+
Action<Node<T>, Node<T>> onNodeCloned)
105+
{
106+
return Clone(target, null, onNodeCloned);
107+
}
108+
109+
/// <summary>
110+
/// Create's a clone of the entire tree but only returns the clone of this node.
111+
/// </summary>
112+
/// <returns>A clone of this node as part of a newly cloned tree.</returns>
113+
public Node<T> CloneTree(Node<T> target)
114+
{
115+
Node<T> node = null;
116+
Clone(target.Root, (n, clone) =>
117+
{
118+
if (n == target) node = clone;
119+
});
120+
return node;
121+
}
122+
123+
/// <summary>
124+
/// Generates a full hierarchy if the root is an IParent and uses the root as the value of the hierarchy.
125+
/// Essentially building a map of the tree.
126+
/// </summary>
127+
/// <typeparam name="TRoot">The type of the root.</typeparam>
128+
/// <param name="root">The root instance.</param>
129+
/// <returns>The full map of the root.</returns>
130+
public Node<T> Map<TRoot>(TRoot root)
131+
where TRoot : T
132+
{
133+
AssertIsAlive();
134+
135+
var current = _pool.Take();
136+
current.Value = root;
137+
138+
if (!(root is IParent<T> parent)) return current;
139+
foreach (var child in parent.Children)
140+
{
141+
current.Add(Map<T>(child));
142+
}
143+
144+
return current;
145+
}
146+
147+
/// <summary>
148+
/// Generates a full hierarchy if the root is an IParent and uses the root as the value of the hierarchy.
149+
/// Essentially building a map of the tree.
150+
/// </summary>
151+
/// <param name="root">The root instance.</param>
152+
/// <returns>The full map of the root.</returns>
153+
public Node<T> Map(T root)
154+
{
155+
return Map<T>(root);
156+
}
157+
158+
/// <summary>
159+
/// Generates a full hierarchy if the root of the container is an IParent and uses the root as the value of the hierarchy.
160+
/// Essentially building a map of the tree.
161+
/// </summary>
162+
/// <typeparam name="TRoot">The type of the root.</typeparam>
163+
/// <param name="container">The container of the root instance.</param>
164+
/// <returns>The full map of the root.</returns>
165+
public Node<T> Map<TRoot>(IHaveRoot<TRoot> container)
166+
where TRoot : T
167+
{
168+
return Map(container.Root);
169+
}
170+
171+
}
172+
173+
}
174+
175+
}

0 commit comments

Comments
 (0)