diff --git a/SharpNBT/CompoundTag.cs b/SharpNBT/CompoundTag.cs
index 4d604c6..b84541f 100644
--- a/SharpNBT/CompoundTag.cs
+++ b/SharpNBT/CompoundTag.cs
@@ -21,6 +21,8 @@ namespace SharpNBT
/// The name of the tag, or if tag has no name.
public CompoundTag([CanBeNull] string name) : base(TagType.Compound, name)
{
+ NamedChildren = true;
+ RequiredType = null;
}
///
@@ -28,8 +30,9 @@ namespace SharpNBT
///
/// The name of the tag, or if tag has no name.
/// A collection objects that are children of this object.
- public CompoundTag([CanBeNull] string name, [NotNull] IEnumerable values) : base(TagType.Compound, name, values)
+ public CompoundTag([CanBeNull] string name, [NotNull] IEnumerable values) : this(name)
{
+ AddRange(values);
}
/// Returns a string that represents the current object.
diff --git a/SharpNBT/ListTag.cs b/SharpNBT/ListTag.cs
index 632b908..65bfa63 100644
--- a/SharpNBT/ListTag.cs
+++ b/SharpNBT/ListTag.cs
@@ -27,6 +27,8 @@ namespace SharpNBT
/// A constant describing the NBT type for children in this tag.
public ListTag([CanBeNull] string name, TagType childType) : base(TagType.List, name)
{
+ RequiredType = childType;
+ NamedChildren = false;
ChildType = childType;
}
@@ -36,9 +38,8 @@ namespace SharpNBT
/// The name of the tag, or if tag has no name.
/// A constant describing the NBT type for children in this tag.
/// A collection of values to include in this tag.
- public ListTag([CanBeNull] string name, TagType childType, [NotNull][ItemNotNull] IEnumerable children) : base(TagType.List, name)
+ public ListTag([CanBeNull] string name, TagType childType, [NotNull][ItemNotNull] IEnumerable children) : this(name, childType)
{
- ChildType = childType;
AddRange(children);
}
diff --git a/SharpNBT/Tag.cs b/SharpNBT/Tag.cs
index bfe8af8..52abac0 100644
--- a/SharpNBT/Tag.cs
+++ b/SharpNBT/Tag.cs
@@ -14,7 +14,7 @@ namespace SharpNBT
/// Abstract base class that all NBT tags inherit from.
///
[PublicAPI][DataContract][KnownType("GetKnownTypes")]
- public abstract class Tag
+ public abstract class Tag : IEquatable
{
private static IEnumerable GetKnownTypes()
{
@@ -130,6 +130,48 @@ namespace SharpNBT
stream.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}
+
+ /// Indicates whether the current object is equal to another object of the same type.
+ /// An object to compare with this object.
+ ///
+ /// if the current object is equal to the parameter; otherwise, .
+ ///
+ public bool Equals(Tag other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Type == other.Type && Name == other.Name;
+ }
+
+ /// Determines whether the specified object is equal to the current object.
+ /// The object to compare with the current object.
+ ///
+ /// if the specified object is equal to the current object; otherwise, .
+ ///
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((Tag)obj);
+ }
+
+ /// Serves as the default hash function.
+ /// A hash code for the current object.
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ // ReSharper disable NonReadonlyMemberInGetHashCode
+ return ((int)Type * 373) ^ (Name != null ? Name.GetHashCode() : 0);
+ // ReSharper restore NonReadonlyMemberInGetHashCode
+ }
+ }
+
+ public static bool operator ==(Tag left, Tag right) => Equals(left, right);
+
+ public static bool operator !=(Tag left, Tag right) => !Equals(left, right);
}
///
@@ -137,7 +179,7 @@ namespace SharpNBT
///
/// The type of the value the tag represents.
[PublicAPI][DataContract]
- public abstract class Tag : Tag
+ public abstract class Tag : Tag, IEquatable>
{
///
/// Gets or sets the value of the tag.
@@ -163,5 +205,47 @@ namespace SharpNBT
buffer.Append(indent);
buffer.AppendLine(ToString());
}
+
+ /// Indicates whether the current object is equal to another object of the same type.
+ /// An object to compare with this object.
+ ///
+ /// if the current object is equal to the parameter; otherwise, .
+ ///
+ public bool Equals(Tag other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other) && EqualityComparer.Default.Equals(Value, other.Value);
+ }
+
+ /// Determines whether the specified object is equal to the current object.
+ /// The object to compare with the current object.
+ ///
+ /// if the specified object is equal to the current object; otherwise, .
+ ///
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((Tag)obj);
+ }
+
+ /// Serves as the default hash function.
+ /// A hash code for the current object.
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ // ReSharper disable NonReadonlyMemberInGetHashCode
+ return (base.GetHashCode() * 421) ^ EqualityComparer.Default.GetHashCode(Value);
+ // ReSharper restore NonReadonlyMemberInGetHashCode
+ }
+ }
+
+ public static bool operator ==(Tag left, Tag right) => Equals(left, right);
+
+ public static bool operator !=(Tag left, Tag right) => !Equals(left, right);
}
}
\ No newline at end of file
diff --git a/SharpNBT/TagContainer.cs b/SharpNBT/TagContainer.cs
index 8981410..9e6ec09 100644
--- a/SharpNBT/TagContainer.cs
+++ b/SharpNBT/TagContainer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using JetBrains.Annotations;
using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
@@ -10,8 +11,11 @@ namespace SharpNBT
/// Base class for tags that contain a collection of other objects and can be enumerated.
///
[PublicAPI][DataContract]
- public abstract class TagContainer : EnumerableTag
+ public abstract class TagContainer : EnumerableTag
{
+ protected bool NamedChildren;
+ protected TagType? RequiredType;
+
///
/// Initializes a new instance of the .
///
@@ -38,7 +42,7 @@ namespace SharpNBT
[SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")]
public sealed override void Add(Tag item)
{
- base.Add(item ?? throw new ArgumentNullException(nameof(item)));
+ base.Add(AssertConventions(item));
item.Parent = this;
}
@@ -52,7 +56,7 @@ namespace SharpNBT
[SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")]
public sealed override void Insert(int index, Tag item)
{
- base.Insert(index, item ?? throw new ArgumentNullException(nameof(item)));
+ base.Insert(index, AssertConventions(item));
item.Parent = this;
}
@@ -68,7 +72,7 @@ namespace SharpNBT
get => base[index];
set
{
- base[index] = value ?? throw new ArgumentNullException(nameof(value));
+ base[index] = AssertConventions(value);
value.Parent = this;
}
}
@@ -109,5 +113,30 @@ namespace SharpNBT
this[index].Parent = null;
base.RemoveAt(index);
}
+
+ ///
+ /// Performs routine checks to ensure that the given complies with the NBT standard for this collection type.
+ ///
+ /// A instance to validate.
+ /// Returns the instance.
+ ///
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected Tag AssertConventions([CanBeNull] Tag tag)
+ {
+ if (tag is null)
+ throw new ArgumentNullException(nameof(tag), "Child tag in collection cannot be null");
+
+ if (NamedChildren && tag.Name is null)
+ throw new FormatException("Children of this collection type must be named.");
+ if (!NamedChildren && tag.Name != null)
+ throw new FormatException("Children of this collection type cannot be named.");
+
+ if (RequiredType.HasValue && RequiredType.Value != tag.Type)
+ throw new ArrayTypeMismatchException("Incorrect tag type added to this collection.");
+
+ return tag;
+ }
}
}
\ No newline at end of file