Skip to content

Commit fdc51f0

Browse files
author
Oren (electricessence)
committed
Migrated cloning and added UnMapped property.
1 parent 00a7c8b commit fdc51f0

3 files changed

Lines changed: 88 additions & 75 deletions

File tree

Node.Clone.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Diagnostics.Contracts;
3+
4+
namespace Open.Hierarchy
5+
{
6+
public sealed partial class Node<T>
7+
{
8+
9+
/// <summary>
10+
/// Clones this node by recreating the tree and copying the values.
11+
/// The resultant clone does not belong to any tree (detatched) unless newParentForClone is specified.
12+
/// </summary>
13+
/// <param name="newParentForClone">
14+
/// If a parent is specified it will use that node as its parent.
15+
/// By default it ends up being detatched.
16+
/// </param>
17+
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
18+
/// <returns>The copy of the tree/branch.</returns>
19+
public Node<T> Clone(
20+
Node<T> newParentForClone,
21+
Action<Node<T>, Node<T>> onNodeCloned = null)
22+
{
23+
if (newParentForClone != null && newParentForClone._factory != _factory)
24+
throw new ArgumentException("The node being provided for cloning does not belong to this factory.", nameof(newParentForClone));
25+
Contract.EndContractBlock();
26+
27+
AssertNotRecycled();
28+
29+
var clone = _factory.GetBlankNode();
30+
clone.Value = Value;
31+
newParentForClone?.Add(clone);
32+
33+
foreach (var child in _children)
34+
clone.Add(child.Clone(clone, onNodeCloned));
35+
36+
clone.UnMapped = UnMapped;
37+
38+
onNodeCloned?.Invoke(this, clone);
39+
40+
return clone;
41+
}
42+
43+
/// <summary>
44+
/// Clones a node by recreating the tree and copying the values.
45+
/// The resultant clone is detached (is its own root).
46+
/// </summary>
47+
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
48+
/// <returns>The copy of the tree/branch.</returns>
49+
public Node<T> Clone(
50+
Action<Node<T>, Node<T>> onNodeCloned)
51+
=> Clone(null, onNodeCloned);
52+
53+
/// <summary>
54+
/// Create's a clone of the entire tree but only returns the clone of this node.
55+
/// </summary>
56+
/// <returns>A clone of this node as part of a newly cloned tree.</returns>
57+
public Node<T> CloneTree(Node<T> target)
58+
{
59+
Node<T> node = null;
60+
target.Root.Clone((n, clone) =>
61+
{
62+
if (n == target) node = clone;
63+
});
64+
return node;
65+
}
66+
67+
}
68+
}

Node.Factory.cs

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -75,67 +75,6 @@ static void PrepareForPool(Node<T> n)
7575

7676
#endregion
7777

78-
/// <summary>
79-
/// Clones a node by recreating the tree and copying the values.
80-
/// </summary>
81-
/// <param name="target">The node to replicate.</param>
82-
/// <param name="newParentForClone">
83-
/// If a parent is specified it will use that node as its parent.
84-
/// By default it ends up being detatched.
85-
/// </param>
86-
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
87-
/// <returns>The copy of the tree/branch.</returns>
88-
public Node<T> Clone(
89-
Node<T> target,
90-
Node<T> newParentForClone = null,
91-
Action<Node<T>, Node<T>> onNodeCloned = null)
92-
{
93-
if (target == null) throw new ArgumentNullException(nameof(target));
94-
if (target._factory != this)
95-
throw new ArgumentException("The node being provided for cloning does not belong to this factory.", nameof(target));
96-
if (newParentForClone != null && newParentForClone._factory != this)
97-
throw new ArgumentException("The node being provided for cloning does not belong to this factory.", nameof(newParentForClone));
98-
Contract.EndContractBlock();
99-
100-
AssertIsAlive();
101-
102-
var clone = GetBlankNode();
103-
clone.Value = target.Value;
104-
newParentForClone?.Add(clone);
105-
106-
foreach (var child in target._children)
107-
clone.Add(Clone(child, clone, onNodeCloned));
108-
109-
onNodeCloned?.Invoke(target, clone);
110-
111-
return clone;
112-
}
113-
114-
/// <summary>
115-
/// Clones a node by recreating the tree and copying the values.
116-
/// </summary>
117-
/// <param name="target">The node to replicate.</param>
118-
/// <param name="onNodeCloned">A function that recieves the old node and its clone.</param>
119-
/// <returns>The copy of the tree/branch.</returns>
120-
public Node<T> Clone(
121-
Node<T> target,
122-
Action<Node<T>, Node<T>> onNodeCloned)
123-
=> Clone(target, null, onNodeCloned);
124-
125-
/// <summary>
126-
/// Create's a clone of the entire tree but only returns the clone of this node.
127-
/// </summary>
128-
/// <returns>A clone of this node as part of a newly cloned tree.</returns>
129-
public Node<T> CloneTree(Node<T> target)
130-
{
131-
Node<T> node = null;
132-
Clone(target.Root, (n, clone) =>
133-
{
134-
if (n == target) node = clone;
135-
});
136-
return node;
137-
}
138-
13978
/// <summary>
14079
/// Generates a full hierarchy if the root is an IParent and uses the root as the value of the hierarchy.
14180
/// Essentially building a map of the tree.
@@ -153,7 +92,7 @@ public Node<T> Map<TRoot>(TRoot root)
15392

15493
// Mapping is deferred and occurs on demand.
15594
// If values or children change in the node, mapping is disregarded.
156-
current._needsMapping = true;
95+
current.UnMapped = true;
15796

15897

15998
return current;

Node.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,35 +38,41 @@ public T Value
3838
set
3939
{
4040
AssertNotRecycled();
41-
_needsMapping = false;
41+
UnMapped = false;
4242
_value = value;
4343
}
4444
}
4545

4646
private readonly Factory _factory;
47-
private bool _needsMapping;
47+
48+
/// <summary>
49+
/// Indicates that this node is in a state of deferred mapping.
50+
/// Will be false if not created by calling Factory.Map or if the value has been mapped.
51+
/// Since querying the chidren of this node will cause the value to be mapped, it can be useful to query this value before attempting traversal.
52+
/// </summary>
53+
public bool UnMapped { get; private set; }
4854

4955
IReadOnlyList<Node<T>> EnsureChildrenMapped()
5056
{
51-
if (!_needsMapping)
57+
if (!UnMapped)
5258
return _childrenReadOnly;
5359

5460
// Need to avoid double mapping and this method is primarily called when 'reading' from the node and contention will only occur if mapping is needed.
5561
lock (_children)
5662
{
57-
if (!_needsMapping) return _childrenReadOnly;
63+
if (!UnMapped) return _childrenReadOnly;
5864
if (_value is IParent<T> p)
5965
{
6066
foreach (var child in p.Children)
6167
_children.Add(_factory.Map(child));
6268
}
63-
_needsMapping = false;
69+
UnMapped = false;
6470
}
6571

6672
return _childrenReadOnly;
6773
}
6874

69-
internal Node(Factory factory)
75+
Node(Factory factory)
7076
{
7177
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
7278
_children = new List<Node<T>>();
@@ -97,7 +103,7 @@ public bool Remove(Node<T> node)
97103
if (!_children.Remove(node)) return false;
98104

99105
AssertNotRecycled();
100-
_needsMapping = false;
106+
UnMapped = false;
101107
node.Parent = null; // Need to be very careful about retaining parent references as it may cause a 'leak' per-se.
102108
return true;
103109
}
@@ -127,7 +133,7 @@ public void Clear()
127133
if (_children.Count == 0) return;
128134

129135
AssertNotRecycled();
130-
_needsMapping = false;
136+
UnMapped = false;
131137
foreach (var c in _children)
132138
c.Parent = null;
133139
_children.Clear();
@@ -171,7 +177,7 @@ public void Replace(Node<T> node, Node<T> replacement)
171177
throw new InvalidOperationException("Node being replaced does not belong to this parent.");
172178

173179
AssertNotRecycled();
174-
_needsMapping = false;
180+
UnMapped = false;
175181
_children[i] = replacement;
176182
node.Parent = null;
177183
replacement.Parent = this;
@@ -209,7 +215,7 @@ public Node<T> Root
209215
/// </summary>
210216
public void Teardown()
211217
{
212-
_needsMapping = false;
218+
UnMapped = false;
213219
Value = default;
214220
Detatch(); // If no parent then this does nothing...
215221
TeardownChildren();
@@ -223,7 +229,7 @@ public void TeardownChildren()
223229
if (_children.Count == 0) return;
224230

225231
AssertNotRecycled();
226-
_needsMapping = false;
232+
UnMapped = false;
227233
foreach (var c in _children)
228234
{
229235
c.Parent = null; // Don't initiate a 'Detach' (which does a lookup) since we are clearing here;
@@ -239,7 +245,7 @@ public void TeardownChildren()
239245
public T Recycle()
240246
{
241247
AssertNotRecycled(); // Avoid double entry in the pool.
242-
_needsMapping = false;
248+
UnMapped = false;
243249
var value = Value;
244250
Value = default;
245251
Detatch(); // If no parent then this does nothing...
@@ -254,7 +260,7 @@ public void RecycleChildren()
254260
{
255261
if (_children.Count == 0) return;
256262

257-
_needsMapping = false;
263+
UnMapped = false;
258264
foreach (var c in _children)
259265
{
260266
c.Parent = null; // Don't initiate a 'Detach' (which does a lookup) since we are clearing here;

0 commit comments

Comments
 (0)