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