|
|
@@ -1,19 +1,25 @@
|
|
|
using System;
|
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Text;
|
|
|
|
|
|
namespace SharpGLTF.Collections
|
|
|
{
|
|
|
+ /// <summary>
|
|
|
+ /// An Specialisation of <see cref="List{T}"/>, which interconnects the list items with the parent of the collection.
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="T"></typeparam>
|
|
|
+ /// <typeparam name="TParent"></typeparam>
|
|
|
[System.Diagnostics.DebuggerDisplay("{Count}")]
|
|
|
- sealed class ChildrenCollection<T, TParent> : IList<T>, IReadOnlyList<T>
|
|
|
- where T : class, IChildOf<TParent>
|
|
|
+ sealed class ChildrenList<T, TParent> : IList<T>, IReadOnlyList<T>
|
|
|
+ where T : class, IChildOfList<TParent>
|
|
|
where TParent : class
|
|
|
{
|
|
|
#region lifecycle
|
|
|
|
|
|
- public ChildrenCollection(TParent parent)
|
|
|
+ public ChildrenList(TParent parent)
|
|
|
{
|
|
|
Guard.NotNull(parent, nameof(parent));
|
|
|
_Parent = parent;
|
|
|
@@ -33,6 +39,7 @@ namespace SharpGLTF.Collections
|
|
|
|
|
|
#region properties
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public T this[int index]
|
|
|
{
|
|
|
get
|
|
|
@@ -44,36 +51,34 @@ namespace SharpGLTF.Collections
|
|
|
set
|
|
|
{
|
|
|
// new value must be an orphan
|
|
|
- Guard.NotNull(value, nameof(value));
|
|
|
- Guard.MustBeNull(value.LogicalParent, nameof(value.LogicalParent));
|
|
|
+ _VerifyIsOrphan(value);
|
|
|
|
|
|
if (_Collection == null) throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
|
|
if (_Collection[index] == value) return; // nothing to do
|
|
|
|
|
|
- // orphan the current child
|
|
|
+ // make the current child orphan.
|
|
|
if (_Collection[index] != null)
|
|
|
{
|
|
|
- _Collection[index]._SetLogicalParent(null, -1);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[index].LogicalParent == null);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[index].LogicalIndex == -1);
|
|
|
+ _Collection[index].SetLogicalParent(null, -1);
|
|
|
+ _AssertItem(_Collection[index], -1);
|
|
|
}
|
|
|
|
|
|
- _Collection[index] = null;
|
|
|
-
|
|
|
- // adopt the new child
|
|
|
+ // update the collection with new child
|
|
|
_Collection[index] = value;
|
|
|
+
|
|
|
if (_Collection[index] != null)
|
|
|
{
|
|
|
- _Collection[index]._SetLogicalParent(_Parent, index);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[index].LogicalParent == _Parent);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[index].LogicalIndex == index);
|
|
|
+ _Collection[index].SetLogicalParent(_Parent, index);
|
|
|
+ _AssertItem(_Collection[index], index);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public int Count => _Collection == null ? 0 : _Collection.Count;
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
|
|
|
public bool IsReadOnly => false;
|
|
|
|
|
|
@@ -81,112 +86,131 @@ namespace SharpGLTF.Collections
|
|
|
|
|
|
#region API
|
|
|
|
|
|
- public void Add(T item)
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public bool Contains(T item)
|
|
|
{
|
|
|
- // new value must be an orphan
|
|
|
- Guard.NotNull(item, nameof(item));
|
|
|
- Guard.MustBeNull(item.LogicalParent, nameof(item.LogicalParent));
|
|
|
- Guard.MustBeEqualTo(-1, item.LogicalIndex, nameof(item.LogicalIndex));
|
|
|
+ return _Collection?.Contains(item) ?? false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public int IndexOf(T item)
|
|
|
+ {
|
|
|
+ return _Collection?.IndexOf(item) ?? -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public void CopyTo(T[] array, int arrayIndex)
|
|
|
+ {
|
|
|
+ if (_Collection == null) return;
|
|
|
+ _Collection.CopyTo(array, arrayIndex);
|
|
|
+ }
|
|
|
|
|
|
- if (_Collection == null) _Collection = new List<T>();
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public void Add(T item)
|
|
|
+ {
|
|
|
+ _VerifyIsOrphan(item);
|
|
|
|
|
|
- item._SetLogicalParent(_Parent, _Collection.Count);
|
|
|
- System.Diagnostics.Debug.Assert(item.LogicalParent == _Parent);
|
|
|
- System.Diagnostics.Debug.Assert(item.LogicalIndex == _Collection.Count);
|
|
|
+ _Collection ??= new List<T>();
|
|
|
|
|
|
+ var idx =_Collection.Count;
|
|
|
_Collection.Add(item);
|
|
|
+ item.SetLogicalParent(_Parent, idx);
|
|
|
+ _AssertItem(item, idx);
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public void Clear()
|
|
|
{
|
|
|
if (_Collection == null) return;
|
|
|
|
|
|
- foreach (var item in _Collection)
|
|
|
+ foreach (var item in _Collection) // orphan all children
|
|
|
{
|
|
|
- item._SetLogicalParent(null, -1);
|
|
|
- System.Diagnostics.Debug.Assert(item.LogicalParent == null);
|
|
|
- System.Diagnostics.Debug.Assert(item.LogicalIndex == -1);
|
|
|
+ item.SetLogicalParent(null, -1);
|
|
|
+ _AssertItem(item, -1);
|
|
|
}
|
|
|
|
|
|
_Collection = null;
|
|
|
}
|
|
|
|
|
|
- public bool Contains(T item)
|
|
|
- {
|
|
|
- return _Collection == null ? false : _Collection.Contains(item);
|
|
|
- }
|
|
|
-
|
|
|
- public void CopyTo(T[] array, int arrayIndex)
|
|
|
- {
|
|
|
- if (_Collection == null) return;
|
|
|
- _Collection.CopyTo(array, arrayIndex);
|
|
|
- }
|
|
|
-
|
|
|
- public int IndexOf(T item)
|
|
|
- {
|
|
|
- return _Collection == null ? -1 : _Collection.IndexOf(item);
|
|
|
- }
|
|
|
-
|
|
|
+ /// <inheritdoc/>
|
|
|
public void Insert(int index, T item)
|
|
|
{
|
|
|
- // new value must be an orphan
|
|
|
- Guard.NotNull(item, nameof(item));
|
|
|
- Guard.MustBeNull(item.LogicalParent, nameof(item.LogicalParent));
|
|
|
- Guard.MustBeEqualTo(-1, item.LogicalIndex, nameof(item.LogicalIndex));
|
|
|
-
|
|
|
- if (_Collection == null) _Collection = new List<T>();
|
|
|
+ _VerifyIsOrphan(item);
|
|
|
|
|
|
+ _Collection ??= new List<T>();
|
|
|
_Collection.Insert(index, item);
|
|
|
|
|
|
// fix indices of upper items
|
|
|
for (int i = index; i < _Collection.Count; ++i)
|
|
|
{
|
|
|
- _Collection[i]._SetLogicalParent(_Parent, i);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[i].LogicalParent == _Parent);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[i].LogicalIndex == i);
|
|
|
+ item = _Collection[i];
|
|
|
+ if (item == null) continue;
|
|
|
+ item.SetLogicalParent(_Parent, i);
|
|
|
+ _AssertItem(item, i);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public bool Remove(T item)
|
|
|
{
|
|
|
var idx = IndexOf(item);
|
|
|
-
|
|
|
if (idx < 0) return false;
|
|
|
-
|
|
|
RemoveAt(idx);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public void RemoveAt(int index)
|
|
|
{
|
|
|
if (_Collection == null) throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
if (index < 0 || index >= _Collection.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
|
|
- // orphan the current child
|
|
|
- if (_Collection[index] != null) { _Collection[index]._SetLogicalParent(null, -1); }
|
|
|
-
|
|
|
+ var item = _Collection[index];
|
|
|
_Collection.RemoveAt(index);
|
|
|
|
|
|
+ // orphan the current child
|
|
|
+ item?.SetLogicalParent(null, -1);
|
|
|
+
|
|
|
// fix indices of upper items
|
|
|
for (int i = index; i < _Collection.Count; ++i)
|
|
|
{
|
|
|
- _Collection[i]._SetLogicalParent(_Parent, i);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[i].LogicalParent == _Parent);
|
|
|
- System.Diagnostics.Debug.Assert(_Collection[i].LogicalIndex == i);
|
|
|
+ item = _Collection[i];
|
|
|
+
|
|
|
+ item.SetLogicalParent(_Parent, i);
|
|
|
+ _AssertItem(item, i);
|
|
|
}
|
|
|
|
|
|
if (_Collection.Count == 0) _Collection = null;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
|
{
|
|
|
- return _Collection == null ? Enumerable.Empty<T>().GetEnumerator() : _Collection.GetEnumerator();
|
|
|
+ return _Collection?.GetEnumerator() ?? Enumerable.Empty<T>().GetEnumerator();
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
|
{
|
|
|
- return _Collection == null ? Enumerable.Empty<T>().GetEnumerator() : _Collection.GetEnumerator();
|
|
|
+ return _Collection?.GetEnumerator() ?? Enumerable.Empty<T>().GetEnumerator();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void _VerifyIsOrphan(T item)
|
|
|
+ {
|
|
|
+ Guard.NotNull(item, nameof(item));
|
|
|
+ Guard.MustBeNull(item.LogicalParent, nameof(item.LogicalParent));
|
|
|
+ Guard.MustBeEqualTo(-1, item.LogicalIndex, nameof(item.LogicalIndex));
|
|
|
+ }
|
|
|
+
|
|
|
+ [Conditional("DEBUG")]
|
|
|
+ private void _AssertItem(T item, int index)
|
|
|
+ {
|
|
|
+ System.Diagnostics.Debug.Assert(item.LogicalIndex == index);
|
|
|
+
|
|
|
+ var parent = index >= 0 ? _Parent : null;
|
|
|
+
|
|
|
+ System.Diagnostics.Debug.Assert(item.LogicalParent == parent);
|
|
|
}
|
|
|
|
|
|
#endregion
|