Reimplemented CompoundTag as a Dictionary
This commit is contained in:
parent
b50d5ebbd6
commit
a4ecca89a8
|
@ -16,7 +16,7 @@ namespace SharpNBT;
|
||||||
public class TagBuilder
|
public class TagBuilder
|
||||||
{
|
{
|
||||||
private readonly CompoundTag root;
|
private readonly CompoundTag root;
|
||||||
private readonly Stack<TagContainer> tree;
|
private readonly Stack<ICollection<Tag>> tree;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the zero-based depth of the current node, indicating how deeply nested it is within other tags.
|
/// 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)
|
public TagBuilder(string? name = null)
|
||||||
{
|
{
|
||||||
root = new CompoundTag(name);
|
root = new CompoundTag(name);
|
||||||
tree = new Stack<TagContainer>();
|
tree = new Stack<ICollection<Tag>>();
|
||||||
tree.Push(root);
|
tree.Push(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,9 +426,9 @@ public class TagBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the top-level tag for this context.
|
/// Gets the top-level tag for this context.
|
||||||
/// </summary>
|
/// </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;
|
Tag = tag;
|
||||||
closeHandler = handler;
|
closeHandler = handler;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using JetBrains.Annotations;
|
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.
|
/// closing <see cref="EndTag"/> does not require to be explicitly added, it will be added automatically during serialization.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[PublicAPI][Serializable]
|
[PublicAPI][Serializable]
|
||||||
public class CompoundTag : TagContainer
|
public class CompoundTag : Tag, IDictionary<string, Tag>, ICollection<Tag>
|
||||||
{
|
{
|
||||||
|
private readonly Dictionary<string, Tag> dict;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="CompoundTag"/> class.
|
/// Creates a new instance of the <see cref="CompoundTag"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the tag, or <see langword="null"/> if tag has no name.</param>
|
/// <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)
|
public CompoundTag(string? name) : base(TagType.Compound, name)
|
||||||
{
|
{
|
||||||
NamedChildren = true;
|
dict = new Dictionary<string, Tag>();
|
||||||
RequiredType = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// <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)
|
public CompoundTag(string? name, IEnumerable<Tag> values) : this(name)
|
||||||
{
|
{
|
||||||
AddRange(values);
|
foreach (var value in values)
|
||||||
|
{
|
||||||
|
dict.Add(value.Name!, AssertName(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -44,6 +50,111 @@ public class CompoundTag : TagContainer
|
||||||
/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext" />) for this serialization.</param>
|
/// <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)
|
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>
|
/// <summary>Returns a string that represents the current object.</summary>
|
||||||
|
@ -71,16 +182,16 @@ public class CompoundTag : TagContainer
|
||||||
/// Searches the children of this tag, returning the first child with the specified <paramref name="name"/>.
|
/// Searches the children of this tag, returning the first child with the specified <paramref name="name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the tag to search for.</param>
|
/// <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>
|
/// <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)
|
if (string.CompareOrdinal(name, tag.Name) == 0)
|
||||||
return tag;
|
return tag;
|
||||||
|
|
||||||
if (deep && tag is CompoundTag child)
|
if (recursive && tag is CompoundTag child)
|
||||||
{
|
{
|
||||||
var result = child.Find(name, true);
|
var result = child.Find(name, true);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
|
@ -91,12 +202,6 @@ public class CompoundTag : TagContainer
|
||||||
return null;
|
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)"/>
|
/// <inheritdoc cref="Tag.PrettyPrinted(StringBuilder,int,string)"/>
|
||||||
protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent)
|
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 + ToString());
|
||||||
buffer.AppendLine(space + "{");
|
buffer.AppendLine(space + "{");
|
||||||
foreach (var tag in this)
|
foreach (var tag in dict.Values)
|
||||||
tag.PrettyPrinted(buffer, level + 1, indent);
|
tag.PrettyPrinted(buffer, level + 1, indent);
|
||||||
buffer.AppendLine(space + "}");
|
buffer.AppendLine(space + "}");
|
||||||
}
|
}
|
||||||
|
@ -118,13 +223,18 @@ public class CompoundTag : TagContainer
|
||||||
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
|
/// <seealso href="https://minecraft.fandom.com/wiki/NBT_format#SNBT_format"/>
|
||||||
public override string Stringify()
|
public override string Stringify()
|
||||||
{
|
{
|
||||||
var strings = new string[Count];
|
var sb = new StringBuilder();
|
||||||
for (var i = 0; i < strings.Length; i++)
|
sb.Append($"{StringifyName}:{{");
|
||||||
strings[i] = this[i].Stringify();
|
|
||||||
|
|
||||||
// TODO: Use StringBuilder
|
var i = 0;
|
||||||
|
foreach (var value in dict.Values)
|
||||||
return $"{StringifyName}:{{{string.Join(',', strings)}}}";
|
{
|
||||||
|
if (i++ > 0)
|
||||||
|
sb.Append(',');
|
||||||
|
sb.Append(value);
|
||||||
|
}
|
||||||
|
sb.Append('}');
|
||||||
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -138,4 +248,12 @@ public class CompoundTag : TagContainer
|
||||||
var str = Stringify();
|
var str = Stringify();
|
||||||
return topLevel ? $"{{{str}}}" : str;
|
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