Reimplemented CompoundTag as a Dictionary
This commit is contained in:
parent
b50d5ebbd6
commit
a4ecca89a8
|
@ -16,7 +16,7 @@ namespace SharpNBT;
|
|||
public class TagBuilder
|
||||
{
|
||||
private readonly CompoundTag root;
|
||||
private readonly Stack<TagContainer> tree;
|
||||
private readonly Stack<ICollection<Tag>> tree;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the zero-based depth of the current node, indicating how deeply nested it is within other tags.
|
||||
|
@ -32,7 +32,7 @@ public class TagBuilder
|
|||
public TagBuilder(string? name = null)
|
||||
{
|
||||
root = new CompoundTag(name);
|
||||
tree = new Stack<TagContainer>();
|
||||
tree = new Stack<ICollection<Tag>>();
|
||||
tree.Push(root);
|
||||
}
|
||||
|
||||
|
@ -426,9 +426,9 @@ public class TagBuilder
|
|||
/// <summary>
|
||||
/// Gets the top-level tag for this context.
|
||||
/// </summary>
|
||||
public TagContainer Tag { get; }
|
||||
public ICollection<Tag> Tag { get; }
|
||||
|
||||
internal Context(TagContainer tag, CloseHandler handler)
|
||||
internal Context(ICollection<Tag> tag, CloseHandler handler)
|
||||
{
|
||||
Tag = tag;
|
||||
closeHandler = handler;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
|
@ -15,16 +17,17 @@ namespace SharpNBT;
|
|||
/// closing <see cref="EndTag"/> does not require to be explicitly added, it will be added automatically during serialization.
|
||||
/// </remarks>
|
||||
[PublicAPI][Serializable]
|
||||
public class CompoundTag : TagContainer
|
||||
public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
|
||||
{
|
||||
private readonly Dictionary<string, Tag> dict;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="CompoundTag"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the tag, or <see langword="null"/> if tag has no name.</param>
|
||||
public CompoundTag(string? name) : base(TagType.Compound, name)
|
||||
{
|
||||
NamedChildren = true;
|
||||
RequiredType = null;
|
||||
dict = new Dictionary<string, Tag>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -34,7 +37,10 @@ public class CompoundTag : TagContainer
|
|||
/// <param name="values">A collection <see cref="Tag"/> objects that are children of this object.</param>
|
||||
public CompoundTag(string? name, IEnumerable<Tag> values) : this(name)
|
||||
{
|
||||
AddRange(values);
|
||||
foreach (var value in values)
|
||||
{
|
||||
dict.Add(value.Name!, AssertName(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,8 +50,113 @@ public class CompoundTag : TagContainer
|
|||
/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext" />) for this serialization.</param>
|
||||
protected CompoundTag(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
var result = info.GetValue("children", typeof(Dictionary<string, Tag>)) as Dictionary<string, Tag>;
|
||||
dict = result ?? new Dictionary<string, Tag>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("children", dict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void ICollection<KeyValuePair<string, Tag>>.Add(KeyValuePair<string, Tag> item) => dict.Add(item.Key, item.Value);
|
||||
|
||||
/// <inheritdoc />
|
||||
bool ICollection<KeyValuePair<string, Tag>>.Contains(KeyValuePair<string, Tag> item) => dict.Contains(item);
|
||||
|
||||
/// <inheritdoc />
|
||||
void ICollection<KeyValuePair<string, Tag>>.CopyTo(KeyValuePair<string, Tag>[] array, int arrayIndex)
|
||||
{
|
||||
foreach (var kvp in dict)
|
||||
array[arrayIndex++] = kvp;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
bool ICollection<KeyValuePair<string, Tag>>.IsReadOnly => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
bool ICollection<Tag>.IsReadOnly => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
bool ICollection<KeyValuePair<string, Tag>>.Remove(KeyValuePair<string, Tag> item) => dict.Remove(item.Key);
|
||||
|
||||
/// <inheritdoc cref="ICollection{T}.Clear"/>
|
||||
public void Clear() => dict.Clear();
|
||||
|
||||
/// <inheritdoc cref="ICollection{T}.Clear"/>
|
||||
public int Count => dict.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator<KeyValuePair<string, Tag>> IEnumerable<KeyValuePair<string, Tag>>.GetEnumerator() => dict.GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<Tag> GetEnumerator() => dict.Values.GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(Tag[] array, int arrayIndex)
|
||||
{
|
||||
foreach (var value in dict.Values)
|
||||
array[arrayIndex++] = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Add(string key, Tag value) => dict.Add(key, AssertName(value));
|
||||
|
||||
/// <inheritdoc cref="ICollection{T}.Add"/>
|
||||
public void Add(Tag value) => dict.Add(value.Name!, AssertName(value));
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsKey(string key) => dict.ContainsKey(key);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Contains(Tag tag) => !string.IsNullOrEmpty(tag.Name) && dict.ContainsKey(tag.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Remove(string key) => dict.Remove(key);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Remove(Tag item) => !string.IsNullOrWhiteSpace(item.Name) && dict.Remove(item.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetValue(string key, out Tag value) => dict.TryGetValue(key, out value!);
|
||||
|
||||
/// <inheritdoc cref="TryGetValue"/>
|
||||
public bool TryGetValue<TTag>(string key, out TTag value) where TTag : Tag
|
||||
{
|
||||
if (dict.TryGetValue(key, out var tag) && tag is TTag result)
|
||||
{
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ICollection<string> Keys => dict.Keys;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ICollection<Tag> Values => dict.Values;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Tag this[string name]
|
||||
{
|
||||
get => dict[name];
|
||||
set => dict[name] = value;
|
||||
}
|
||||
|
||||
public TTag Get<TTag>(string name) where TTag : Tag
|
||||
{
|
||||
return (TTag)dict[name];
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that represents the current object.</summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
/// <footer><a href="https://docs.microsoft.com/en-us/dotnet/api/System.Object.ToString?view=netcore-5.0">`Object.ToString` on docs.microsoft.com</a></footer>
|
||||
|
@ -71,16 +182,16 @@ public class CompoundTag : TagContainer
|
|||
/// Searches the children of this tag, returning the first child with the specified <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the tag to search for.</param>
|
||||
/// <param name="deep"><see langword="true"/> to recursively search children, otherwise <see langword="false"/> to only search direct descendants.</param>
|
||||
/// <param name="recursive"><see langword="true"/> to recursively search children, otherwise <see langword="false"/> to only search direct descendants.</param>
|
||||
/// <returns>The first tag found with <paramref name="name"/>, otherwise <see langword="null"/> if none was found.</returns>
|
||||
public Tag? Find(string name, bool deep)
|
||||
public Tag? Find(string name, bool recursive = false)
|
||||
{
|
||||
foreach (var tag in this)
|
||||
foreach (var tag in dict.Values)
|
||||
{
|
||||
if (string.CompareOrdinal(name, tag.Name) == 0)
|
||||
return tag;
|
||||
|
||||
if (deep && tag is CompoundTag child)
|
||||
if (recursive && tag is CompoundTag child)
|
||||
{
|
||||
var result = child.Find(name, true);
|
||||
if (result != null)
|
||||
|
@ -91,12 +202,6 @@ public class CompoundTag : TagContainer
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a child tag with the specified <paramref name="name"/>, or <see langword="null"/> if no match was found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the tag to retrieve.</param>
|
||||
public Tag? this[string name] => Find(name, false);
|
||||
|
||||
/// <inheritdoc cref="Tag.PrettyPrinted(StringBuilder,int,string)"/>
|
||||
protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent)
|
||||
{
|
||||
|
@ -106,7 +211,7 @@ public class CompoundTag : TagContainer
|
|||
|
||||
buffer.AppendLine(space + ToString());
|
||||
buffer.AppendLine(space + "{");
|
||||
foreach (var tag in this)
|
||||
foreach (var tag in dict.Values)
|
||||
tag.PrettyPrinted(buffer, level + 1, indent);
|
||||
buffer.AppendLine(space + "}");
|
||||
}
|
||||
|
@ -118,13 +223,18 @@ public class CompoundTag : TagContainer
|
|||
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
|
||||
public override string Stringify()
|
||||
{
|
||||
var strings = new string[Count];
|
||||
for (var i = 0; i < strings.Length; i++)
|
||||
strings[i] = this[i].Stringify();
|
||||
|
||||
// TODO: Use StringBuilder
|
||||
|
||||
return $"{StringifyName}:{{{string.Join(',', strings)}}}";
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"{StringifyName}:{{");
|
||||
|
||||
var i = 0;
|
||||
foreach (var value in dict.Values)
|
||||
{
|
||||
if (i++ > 0)
|
||||
sb.Append(',');
|
||||
sb.Append(value);
|
||||
}
|
||||
sb.Append('}');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -138,4 +248,12 @@ public class CompoundTag : TagContainer
|
|||
var str = Stringify();
|
||||
return topLevel ? $"{{{str}}}" : str;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Tag AssertName(Tag tag)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tag.Name))
|
||||
throw new FormatException(Strings.ChildrenMustBeNamed);
|
||||
return tag;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue