Compound and List tags now assign/unassign parent on insertion/deletion

This commit is contained in:
Eric Freed 2023-08-27 16:37:14 -04:00
parent 11f3c4d70c
commit bcd41b37f0
4 changed files with 68 additions and 26 deletions

View File

@ -306,7 +306,7 @@ public class TagWriter : TagIO
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteTypeAndName(Tag tag)
{
if (tag.Parent is ListTag)
if (tag.Parent is ListTag || string.IsNullOrEmpty(tag.Name))
return;
BaseStream.WriteByte((byte) tag.Type);

View File

@ -39,12 +39,12 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
{
foreach (var value in values)
{
dict.Add(value.Name!, AssertName(value));
dict.Add(value.Name!, ValidateChild(value));
}
}
/// <inheritdoc />
void ICollection<KeyValuePair<string, Tag>>.Add(KeyValuePair<string, Tag> item) => dict.Add(item.Key, item.Value);
void ICollection<KeyValuePair<string, Tag>>.Add(KeyValuePair<string, Tag> item) => Add(item.Value);
/// <inheritdoc />
bool ICollection<KeyValuePair<string, Tag>>.Contains(KeyValuePair<string, Tag> item) => dict.Contains(item);
@ -63,10 +63,23 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
bool ICollection<Tag>.IsReadOnly => false;
/// <inheritdoc />
bool ICollection<KeyValuePair<string, Tag>>.Remove(KeyValuePair<string, Tag> item) => dict.Remove(item.Key);
bool ICollection<KeyValuePair<string, Tag>>.Remove(KeyValuePair<string, Tag> item)
{
if (dict.Remove(item.Key))
{
item.Value.Parent = null;
return true;
}
return false;
}
/// <inheritdoc cref="ICollection{T}.Clear"/>
public void Clear() => dict.Clear();
public void Clear()
{
foreach (var child in dict.Values)
child.Parent = null;
dict.Clear();
}
/// <inheritdoc cref="ICollection{T}.Clear"/>
public int Count => dict.Count;
@ -88,10 +101,10 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
}
/// <inheritdoc />
public void Add(string key, Tag value) => dict.Add(key, AssertName(value));
public void Add(string key, Tag value) => dict.Add(key, ValidateChild(value));
/// <inheritdoc cref="ICollection{T}.Add"/>
public void Add(Tag value) => dict.Add(value.Name!, AssertName(value));
public void Add(Tag value) => dict.Add(value.Name!, ValidateChild(value));
/// <inheritdoc />
public bool ContainsKey(string key) => dict.ContainsKey(key);
@ -100,10 +113,24 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
public bool Contains(Tag tag) => !string.IsNullOrEmpty(tag.Name) && dict.ContainsKey(tag.Name);
/// <inheritdoc />
public bool Remove(string key) => dict.Remove(key);
public bool Remove(string key)
{
if (dict.TryGetValue(key, out var tag))
tag.Parent = null;
return dict.Remove(key);
}
/// <inheritdoc />
public bool Remove(Tag item) => !string.IsNullOrWhiteSpace(item.Name) && dict.Remove(item.Name);
public bool Remove(Tag item)
{
if (!string.IsNullOrWhiteSpace(item.Name) && dict.Remove(item.Name))
{
item.Parent = null;
return true;
}
return false;
}
/// <inheritdoc />
public bool TryGetValue(string key, out Tag value) => dict.TryGetValue(key, out value!);
@ -131,7 +158,7 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
public Tag this[string name]
{
get => dict[name];
set => dict[name] = value;
set => dict[name] = ValidateChild(value);
}
public TTag Get<TTag>(string name) where TTag : Tag
@ -249,10 +276,11 @@ public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Tag AssertName(Tag tag)
private Tag ValidateChild(Tag tag)
{
if (string.IsNullOrWhiteSpace(tag.Name))
throw new FormatException(Strings.ChildrenMustBeNamed);
tag.Parent = this;
return tag;
}
}

View File

@ -20,7 +20,7 @@ public class ListTag : Tag, IList<Tag>
/// <summary>
/// Gets the NBT type of this tag's children.
/// </summary>
public TagType ChildType { get; private set; }
public TagType ChildType { get; }
/// <summary>
/// Creates a new instance of the <see cref="ListTag"/> class.
@ -54,7 +54,7 @@ public class ListTag : Tag, IList<Tag>
public ListTag(string? name, TagType childType, IEnumerable<Tag> children) : this(name, childType)
{
foreach (var item in children)
list.Add(AssertType(item));
list.Add(ValidateChild(item));
}
/// <inheritdoc />
@ -64,12 +64,12 @@ public class ListTag : Tag, IList<Tag>
IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
/// <inheritdoc />
public void Add(Tag item) => list.Add(AssertType(item));
public void Add(Tag item) => list.Add(ValidateChild(item));
public void AddRange(IEnumerable<Tag> items)
{
foreach (var item in items)
list.Add(AssertType(item));
list.Add(ValidateChild(item));
}
/// <inheritdoc />
@ -82,7 +82,16 @@ public class ListTag : Tag, IList<Tag>
public void CopyTo(Tag[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
/// <inheritdoc />
public bool Remove(Tag item) => list.Remove(item);
public bool Remove(Tag item)
{
if (list.Remove(item))
{
item.Parent = null;
return true;
}
return false;
}
/// <inheritdoc />
public int Count => list.Count;
@ -94,16 +103,21 @@ public class ListTag : Tag, IList<Tag>
public int IndexOf(Tag item) => list.IndexOf(item);
/// <inheritdoc />
public void Insert(int index, Tag item) => list.Insert(index, AssertType(item));
public void Insert(int index, Tag item) => list.Insert(index, ValidateChild(item));
/// <inheritdoc />
public void RemoveAt(int index) => list.RemoveAt(index);
public void RemoveAt(int index)
{
var item = list[index];
item.Parent = null;
list.RemoveAt(index);
}
/// <inheritdoc />
public Tag this[int index]
{
get => list[index];
set => list[index] = AssertType(value);
set => list[index] = ValidateChild(value);
}
/// <inheritdoc />
@ -173,14 +187,13 @@ public class ListTag : Tag, IList<Tag>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Tag AssertType(Tag tag)
private Tag ValidateChild(Tag tag)
{
if (tag.Type != ChildType)
throw new ArrayTypeMismatchException(Strings.ChildWrongType);
tag.Parent = this;
return tag;
}
private readonly List<Tag> list;
}

View File

@ -31,7 +31,7 @@ public abstract class Tag : IEquatable<Tag>, ICloneable
/// <summary>
/// Gets the parent <see cref="Tag"/> this object is a child of.
/// </summary>
[Obsolete("Parent property will be removed in a future version.")]
[Obsolete("Parent property may be removed in a future version.")]
public Tag? Parent { get; internal set; }
/// <summary>
@ -181,7 +181,7 @@ public abstract class Tag : IEquatable<Tag>, ICloneable
// Serialize then deserialize to make a deep-copy
using var stream = new MemoryStream();
// Might as well not worry about swapping bits, just use native endian
// Use native endian
var opts = BitConverter.IsLittleEndian ? FormatOptions.LittleEndian : FormatOptions.BigEndian;
using var writer = new TagWriter(stream, opts, true);
using var reader = new TagReader(stream, opts, true);
@ -192,6 +192,7 @@ public abstract class Tag : IEquatable<Tag>, ICloneable
return reader.ReadTag(!string.IsNullOrWhiteSpace(Name));
}
/// <summary>
/// Tests for equality of this object with another <see cref="Tag"/> instance.
/// </summary>