Skip to content

Commit 97d8a52

Browse files
author
Oren (electricessence)
committed
Fixes.
1 parent 05cd2a2 commit 97d8a52

5 files changed

Lines changed: 85 additions & 31 deletions

File tree

INode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Open.Hierarchy
44
{
5-
public interface INode<TNode> : ICollection<TNode>, IChild<TNode>, IParent<TNode>, IHaveRoot<TNode>
5+
public interface INode<TNode> : IList<TNode>, IChild<TNode>, IParent<TNode>, IHaveRoot<TNode>
66
where TNode : INode<TNode>
77
{
88
}

Node.Clone.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public Node<T> Clone(
3131
newParentForClone?.Add(clone);
3232

3333
foreach (var child in _children)
34-
clone.Add(child.Clone(clone, onNodeCloned));
34+
child.Clone(clone, onNodeCloned);
3535

3636
clone.Unmapped = Unmapped;
3737

Node.Factory.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ public Node<T> GetBlankNode()
4646
/// Gets a blank node.
4747
/// </summary>
4848
/// <returns>A blank node.</returns>
49-
public Node<T> GetNodeWithValue(T value)
49+
public Node<T> GetNodeWithValue(T value, bool asUnmapped = false)
5050
{
5151
var n = GetBlankNode();
5252
n.Value = value;
53+
n.Unmapped = asUnmapped;
5354
return n;
5455
}
5556

@@ -95,16 +96,12 @@ static void PrepareForPool(Node<T> n)
9596
public Node<T> Map<TRoot>(TRoot root)
9697
where TRoot : T
9798
{
98-
AssertIsAlive();
99-
10099
var current = GetBlankNode();
101100
current.Value = root;
102101

103102
// Mapping is deferred and occurs on demand.
104103
// If values or children change in the node, mapping is disregarded.
105104
current.Unmapped = true;
106-
107-
108105
return current;
109106
}
110107

Node.cs

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ IReadOnlyList<Node<T>> EnsureChildrenMapped()
6464
if (_value is IParent<T> p)
6565
{
6666
foreach (var child in p.Children)
67-
_children.Add(_factory.Map(child));
67+
{
68+
var childNode = _factory.Map(child);
69+
childNode.Parent = this;
70+
_children.Add(childNode);
71+
}
72+
6873
}
6974
Unmapped = false;
7075
}
@@ -81,50 +86,98 @@ IReadOnlyList<Node<T>> EnsureChildrenMapped()
8186

8287
// WARNING: Care must be taken not to have duplicate nodes anywhere in the tree but having duplicate values are allowed.
8388

89+
#region IList<Node<T>> Implementation
90+
91+
public int IndexOf(Node<T> child)
92+
{
93+
EnsureChildrenMapped();
94+
return _children.IndexOf(child);
95+
}
96+
97+
void AssertOkToJoinFamily(Node<T> child)
98+
{
99+
if (child.Parent == null) return;
100+
if (child.Parent == this)
101+
throw new InvalidOperationException("Provided node already belongs to this parent.");
102+
throw new InvalidOperationException("Provided node belongs to another parent.");
103+
}
104+
105+
public void Insert(int index, Node<T> child)
106+
{
107+
if (child == null) throw new ArgumentNullException(nameof(child));
108+
Contract.EndContractBlock();
109+
110+
AssertNotRecycled();
111+
AssertOkToJoinFamily(child);
112+
113+
EnsureChildrenMapped(); // Adding to potentially existing nodes.
114+
child.Parent = this;
115+
_children.Add(child);
116+
}
117+
118+
// ReSharper disable once UnusedMethodReturnValue.Global
119+
public Node<T> RemoveAt(int index)
120+
{
121+
if (index < 0 && index >= Count) throw new ArgumentOutOfRangeException(nameof(index));
122+
Contract.EndContractBlock();
123+
124+
AssertNotRecycled();
125+
var child = this[index];
126+
_children.Remove(child);
127+
128+
Unmapped = false;
129+
child.Parent = null; // Need to be very careful about retaining parent references as it may cause a 'leak' per-se.
130+
return child;
131+
}
132+
133+
void IList<Node<T>>.RemoveAt(int index)
134+
=> RemoveAt(index);
135+
136+
public Node<T> this[int index]
137+
{
138+
get => EnsureChildrenMapped()[index];
139+
set => Replace(EnsureChildrenMapped()[index], value);
140+
}
141+
84142
#region ICollection<Node<T>> Implementation
85143
/// <inheritdoc />
86144
public bool IsReadOnly => false;
87145

88146
/// <inheritdoc />
89-
public bool Contains(Node<T> node)
147+
public bool Contains(Node<T> child)
90148
{
91-
if (node == null) throw new ArgumentNullException(nameof(node));
149+
if (child == null) throw new ArgumentNullException(nameof(child));
92150
Contract.EndContractBlock();
93151

94-
return _children.Contains(node);
152+
return _children.Contains(child);
95153
}
96154

97155
/// <inheritdoc />
98-
public bool Remove(Node<T> node)
156+
public bool Remove(Node<T> child)
99157
{
100-
if (node == null) throw new ArgumentNullException(nameof(node));
158+
if (child == null) throw new ArgumentNullException(nameof(child));
101159
Contract.EndContractBlock();
102160

103-
if (!_children.Remove(node)) return false;
161+
if (!_children.Remove(child)) return false;
104162

105163
AssertNotRecycled();
106164
Unmapped = false;
107-
node.Parent = null; // Need to be very careful about retaining parent references as it may cause a 'leak' per-se.
165+
child.Parent = null; // Need to be very careful about retaining parent references as it may cause a 'leak' per-se.
108166
return true;
109167
}
110168

111169
/// <inheritdoc />
112-
public void Add(Node<T> node)
170+
public void Add(Node<T> child)
113171
{
114-
if (node == null) throw new ArgumentNullException(nameof(node));
172+
if (child == null) throw new ArgumentNullException(nameof(child));
115173
Contract.EndContractBlock();
116174

117-
if (node.Parent != null)
118-
{
119-
if (node.Parent == this)
120-
throw new InvalidOperationException("Adding a child node more than once.");
121-
throw new InvalidOperationException("Adding a node that belongs to another parent.");
122-
}
123-
124175
AssertNotRecycled();
176+
AssertOkToJoinFamily(child);
177+
125178
EnsureChildrenMapped(); // Adding to potentially existing nodes.
126-
node.Parent = this;
127-
_children.Add(node);
179+
child.Parent = this;
180+
_children.Add(child);
128181
}
129182

130183
/// <inheritdoc />
@@ -140,7 +193,7 @@ public void Clear()
140193
}
141194

142195
/// <inheritdoc />
143-
public int Count => _children.Count;
196+
public int Count => EnsureChildrenMapped().Count;
144197

145198
/// <inheritdoc />
146199
public IEnumerator<Node<T>> GetEnumerator()
@@ -157,15 +210,17 @@ public void CopyTo(Node<T>[] array, int arrayIndex)
157210
_children.CopyTo(array, arrayIndex);
158211
}
159212
#endregion
213+
#endregion
160214

161215
/// <summary>
162216
/// Gets a new node with the provided value and adds it as a child.
163217
/// </summary>
164218
/// <param name="value">The value of the new child.</param>
165-
public void AddValue(T value)
219+
/// <param name="asUnmapped">If true adds the node if it has been prepared for mapping but not yet checked if the value is an IParent. If value is not an IParent, then this flag does nothing.</param>
220+
public void AddValue(T value, bool asUnmapped = false)
166221
{
167222
AssertNotRecycled();
168-
Add(_factory.GetNodeWithValue(value));
223+
Add(_factory.GetNodeWithValue(value, asUnmapped));
169224
}
170225

171226
/// <summary>
@@ -278,6 +333,7 @@ public void RecycleChildren()
278333
_children.Clear();
279334
}
280335

336+
281337
}
282338

283339
}

Open.Hierarchy.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ Part of the "Open" set of libraries.</Description>
1414
<RepositoryUrl>https://github.com/electricessence/Open.Hierarchy/</RepositoryUrl>
1515
<RepositoryType>git</RepositoryType>
1616
<PackageTags>dotnet, dotnetcore, cs, tree, node, hierarchy, parent, child, root</PackageTags>
17-
<Version>1.4.4</Version>
18-
<PackageReleaseNotes>Added useful node creation methods.</PackageReleaseNotes>
17+
<Version>1.5.1</Version>
18+
<PackageReleaseNotes>Improved API. Implemented IList&lt;Node&lt;T&gt;&gt; for all nodes.
19+
Validated mapping optimization.</PackageReleaseNotes>
1920
</PropertyGroup>
2021

2122
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

0 commit comments

Comments
 (0)