From e1673f686c9117b9ea0d4d8fb9800846881be728 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Wed, 31 May 2023 23:22:39 +0200 Subject: [PATCH 1/8] Rename ListTag.iterateType to asList The implementation of TypedListTag fully supports mutation with the necessary validation checks, so it's purpose isn't just iteration (anymore). --- src/main/java/net/querz/nbt/ListTag.java | 2 +- src/test/java/net/querz/nbt/TestListTag.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 7ae340d3..59fcfb26 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -366,7 +366,7 @@ public ListTag getListOrDefault(int index, ListTag def) { return def; } - public List iterateType(Class tagClass) { + public List asList(Class tagClass) { if (type != null && type.tagClass != tagClass) { throw new IllegalArgumentException("Incorrect tagClass "+tagClass.getName()+", list is of type "+type.tagClass.getName()); } diff --git a/src/test/java/net/querz/nbt/TestListTag.java b/src/test/java/net/querz/nbt/TestListTag.java index 705c3731..22e71739 100644 --- a/src/test/java/net/querz/nbt/TestListTag.java +++ b/src/test/java/net/querz/nbt/TestListTag.java @@ -12,7 +12,7 @@ public class TestListTag extends NBTTestCase { @Test public void testIterateEmpty() { ListTag list = new ListTag(); - for (ByteTag t : list.iterateType(ByteTag.class)) { + for (ByteTag t : list.asList(ByteTag.class)) { fail("iteration for an empty list should not have elements, got: "+t.toString()); } } @@ -20,7 +20,7 @@ public void testIterateEmpty() { @Test public void testModifyIteration() { ListTag list = new ListTag(); - List iteration = list.iterateType(ByteTag.class); + List iteration = list.asList(ByteTag.class); ByteTag tag = ByteTag.valueOf((byte) 123); iteration.add(tag); assertEquals(tag, list.get(0)); @@ -29,7 +29,7 @@ public void testModifyIteration() { @Test public void testIterateLaterAddedWrongType() { ListTag list = new ListTag(); - List iteration = list.iterateType(ByteTag.class); + List iteration = list.asList(ByteTag.class); list.add(IntTag.valueOf(123)); assertThrows(ClassCastException.class, () -> iteration.get(0)); } @@ -37,7 +37,7 @@ public void testIterateLaterAddedWrongType() { @Test public void testIterateLaterAddedCorrectType() { ListTag list = new ListTag(); - List iteration = list.iterateType(ByteTag.class); + List iteration = list.asList(ByteTag.class); list.addByte((byte) 123); assertEquals(123, iteration.get(0).asByte()); } From 6a0cb3bea36e03a591de982131cd33bda0d22a95 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Wed, 31 May 2023 22:42:12 +0200 Subject: [PATCH 2/8] Decouple ListTag type from emptiness There's nothing inherently wrong with a typed but empty list, as long as it's properly serialized. Resetting a ListTag's type when it runs empty circumvents pollution checks, meaning a ListTag could (accidentally) change its type, which feels like asking for trouble :) --- src/main/java/net/querz/nbt/ListTag.java | 42 +++++++++--------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 59fcfb26..2b5507b3 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -25,10 +25,7 @@ public ListTag(Type type) { public ListTag(List list, Type type) { Objects.requireNonNull(list); - - if (type == END) { - throw new IllegalArgumentException("ListTag can not be of type END"); - } + assimilateType(type); for (int i = 0; i < list.size(); i++) { Objects.requireNonNull(list.get(i)); @@ -39,7 +36,16 @@ public ListTag(List list, Type type) { } value = list; - this.type = type; + } + + private void assimilateType(Type type) { + if (type == END) { + throw new IllegalArgumentException("ListTag can not contain tags of type END"); + } else if (this.type == null) { + this.type = type; + } else if (this.type != type) { + throw new UnsupportedOperationException(String.format("incompatible tag type, ListTag is of type %s, got %s", this.type, type)); + } } @Override @@ -50,11 +56,9 @@ public Tag get(int index) { @Override public Tag set(int index, Tag tag) { Objects.requireNonNull(tag); + assimilateType(tag.getType()); Tag old = value.get(index); - if (!updateType(tag)) { - throw new UnsupportedOperationException(String.format("trying to set tag of type %s in ListTag of %s", tag.getType(), type)); - } value.set(index, tag); return old; } @@ -62,10 +66,8 @@ public Tag set(int index, Tag tag) { @Override public void add(int index, Tag tag) { Objects.requireNonNull(tag); + assimilateType(tag.getType()); - if (!updateType(tag)) { - throw new UnsupportedOperationException(String.format("trying to add tag of type %s to ListTag of %s", tag.getType(), type)); - } value.add(index, tag); } @@ -113,22 +115,9 @@ public void addLongArray(long[] l) { add(new LongArrayTag(l)); } - private boolean updateType(Tag tag) { - if (type == null && tag.getType() != END) { - type = tag.getType(); - return true; - } else { - return type == tag.getType(); - } - } - @Override public Tag remove(int index) { - Tag old = value.remove(index); - if (value.isEmpty()) { - type = null; - } - return old; + return value.remove(index); } @Override @@ -147,7 +136,7 @@ public boolean isEmpty() { @Override public void write(DataOutput out) throws IOException { - if (type != null) { + if (value.size() > 0) { out.writeByte(type.id); } else { out.writeByte(0); @@ -187,7 +176,6 @@ public int hashCode() { @Override public void clear() { value.clear(); - type = null; } private NumberTag getNumber(int index) { From 9c30043173ba7c462f562bd4137830e4529339a0 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Wed, 31 May 2023 23:22:39 +0200 Subject: [PATCH 3/8] Explicitly write END tag for empty ListTag --- src/main/java/net/querz/nbt/ListTag.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 2b5507b3..aa87c978 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -139,7 +139,7 @@ public void write(DataOutput out) throws IOException { if (value.size() > 0) { out.writeByte(type.id); } else { - out.writeByte(0); + out.writeByte(END.id); } out.writeInt(value.size()); for (Tag tag : value) { From f2f0958645112c5e7cfaafd6652c76565806db2f Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Thu, 1 Jun 2023 00:02:54 +0200 Subject: [PATCH 4/8] Add ListTag.get*Tag methods Git seems to generate a very unfortunate diff for this... --- src/main/java/net/querz/nbt/ListTag.java | 68 +++++++++++++++++++----- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index aa87c978..ef7b61f9 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -177,49 +177,93 @@ public int hashCode() { public void clear() { value.clear(); } + + public Tag getTag(int index) { + return value.get(index); + } + + public ByteTag getByteTag(int index) { + return (ByteTag) getTag(index); + } + + public ShortTag getShortTag(int index) { + return (ShortTag) getTag(index); + } + + public IntTag getIntTag(int index) { + return (IntTag) getTag(index); + } + + public LongTag getLongTag(int index) { + return (LongTag) getTag(index); + } + + public FloatTag getFloatTag(int index) { + return (FloatTag) getTag(index); + } + + public DoubleTag getDoubleTag(int index) { + return (DoubleTag) getTag(index); + } + + public StringTag getStringTag(int index) { + return (StringTag) getTag(index); + } + + public ByteArrayTag getByteArrayTag(int index) { + return (ByteArrayTag) getTag(index); + } + + public IntArrayTag getIntArrayTag(int index) { + return (IntArrayTag) getTag(index); + } + + public LongArrayTag getLongArrayTag(int index) { + return (LongArrayTag) getTag(index); + } - private NumberTag getNumber(int index) { - return (NumberTag) value.get(index); + public NumberTag getNumberTag(int index) { + return (NumberTag) getTag(index); } public byte getByte(int index) { - return getNumber(index).asByte(); + return getNumberTag(index).asByte(); } public short getShort(int index) { - return getNumber(index).asShort(); + return getNumberTag(index).asShort(); } public int getInt(int index) { - return getNumber(index).asInt(); + return getNumberTag(index).asInt(); } public long getLong(int index) { - return getNumber(index).asLong(); + return getNumberTag(index).asLong(); } public float getFloat(int index) { - return getNumber(index).asFloat(); + return getNumberTag(index).asFloat(); } public double getDouble(int index) { - return getNumber(index).asDouble(); + return getNumberTag(index).asDouble(); } public String getString(int index) { - return ((StringTag) value.get(index)).getValue(); + return getStringTag(index).getValue(); } public byte[] getByteArray(int index) { - return ((ByteArrayTag) value.get(index)).getValue(); + return getByteArrayTag(index).getValue(); } public int[] getIntArray(int index) { - return ((IntArrayTag) value.get(index)).getValue(); + return getIntArrayTag(index).getValue(); } public long[] getLongArray(int index) { - return ((LongArrayTag) value.get(index)).getValue(); + return getLongArrayTag(index).getValue(); } public CompoundTag getCompound(int index) { From 67b28fefdad63aaceffc87aa672dcbe2ae28bf95 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Thu, 1 Jun 2023 00:45:28 +0200 Subject: [PATCH 5/8] Add ListTag.get*TagOrNull methods --- src/main/java/net/querz/nbt/ListTag.java | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index ef7b61f9..0c814ddf 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -278,6 +278,66 @@ public boolean getBoolean(int index) { return getByte(index) != 0; } + public Tag getTagOrNull(int index) { + if (index < value.size()) { + return value.get(index); + } else { + return null; + } + } + + public ByteTag getByteTagOrNull(int index) { + return (ByteTag) getTagOrNull(index); + } + + public ShortTag getShortTagOrNull(int index) { + return (ShortTag) getTagOrNull(index); + } + + public IntTag getIntTagOrNull(int index) { + return (IntTag) getTagOrNull(index); + } + + public LongTag getLongTagOrNull(int index) { + return (LongTag) getTagOrNull(index); + } + + public FloatTag getFloatTagOrNull(int index) { + return (FloatTag) getTagOrNull(index); + } + + public DoubleTag getDoubleTagOrNull(int index) { + return (DoubleTag) getTagOrNull(index); + } + + public StringTag getStringTagOrNull(int index) { + return (StringTag) getTagOrNull(index); + } + + public ByteArrayTag getByteArrayTagOrNull(int index) { + return (ByteArrayTag) getTagOrNull(index); + } + + public IntArrayTag getIntArrayTagOrNull(int index) { + return (IntArrayTag) getTagOrNull(index); + } + + public LongArrayTag getLongArrayTagOrNull(int index) { + return (LongArrayTag) getTagOrNull(index); + } + + public NumberTag getNumberTagOrNull(int index) { + return (NumberTag) getTagOrNull(index); + } + + public CompoundTag getCompoundOrNull(int index) { + return (CompoundTag) getTagOrNull(index); + } + + public ListTag getListOrNull(int index) { + return (ListTag) getTagOrNull(index); + } + public byte getByteOrDefault(int index, byte def) { if (index >= 0 && index < value.size()) { Tag tag = value.get(index); From 66770fe96780c6b39e8d478e724eb57441770e26 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Thu, 1 Jun 2023 00:46:34 +0200 Subject: [PATCH 6/8] De-dupe ListTag.get*OrDefault --- src/main/java/net/querz/nbt/ListTag.java | 108 +++++------------------ 1 file changed, 24 insertions(+), 84 deletions(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 0c814ddf..842f223d 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -339,123 +339,63 @@ public ListTag getListOrNull(int index) { } public byte getByteOrDefault(int index, byte def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asByte(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asByte(); } public short getShortOrDefault(int index, short def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asShort(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asShort(); } public int getIntOrDefault(int index, int def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asInt(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asInt(); } public long getLongOrDefault(int index, long def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asLong(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asLong(); } public float getFloatOrDefault(int index, float def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asFloat(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asFloat(); } public double getDoubleOrDefault(int index, double def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag instanceof NumberTag) { - return ((NumberTag) tag).asDouble(); - } - } - return def; + NumberTag tag = getNumberTagOrNull(index); + return tag == null ? def : tag.asDouble(); } public String getStringOrDefault(int index, String def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == STRING) { - return ((StringTag) tag).getValue(); - } - } - return def; + StringTag tag = getStringTagOrNull(index); + return tag == null ? def : tag.getValue(); } public byte[] getByteArrayOrDefault(int index, byte[] def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == BYTE_ARRAY) { - return ((ByteArrayTag) tag).getValue(); - } - } - return def; + ByteArrayTag tag = getByteArrayTagOrNull(index); + return tag == null ? def : tag.getValue(); } public int[] getIntArrayOrDefault(int index, int[] def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == INT_ARRAY) { - return ((IntArrayTag) tag).getValue(); - } - } - return def; + IntArrayTag tag = getIntArrayTagOrNull(index); + return tag == null ? def : tag.getValue(); } public long[] getLongArrayOrDefault(int index, long[] def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == LONG_ARRAY) { - return ((LongArrayTag) tag).getValue(); - } - } - return def; + LongArrayTag tag = getLongArrayTagOrNull(index); + return tag == null ? def : tag.getValue(); } public CompoundTag getCompoundOrDefault(int index, CompoundTag def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == COMPOUND) { - return (CompoundTag) tag; - } - } - return def; + CompoundTag tag = getCompoundOrNull(index); + return tag == null ? def : tag; } public ListTag getListOrDefault(int index, ListTag def) { - if (index >= 0 && index < value.size()) { - Tag tag = value.get(index); - if (tag.getType() == LIST) { - return (ListTag) tag; - } - } - return def; + ListTag tag = getListOrNull(index); + return tag == null ? def : tag; } public List asList(Class tagClass) { From 475019e47507af7261269fc85480f064581175f6 Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Thu, 1 Jun 2023 00:46:48 +0200 Subject: [PATCH 7/8] Add ListTag.getNumber --- src/main/java/net/querz/nbt/ListTag.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 842f223d..416babc0 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -274,6 +274,10 @@ public ListTag getList(int index) { return (ListTag) value.get(index); } + public Number getNumber(int index) { + return getNumberTag(index).asNumber(); + } + public boolean getBoolean(int index) { return getByte(index) != 0; } From 8671fe542052e6596e11cb609527ea8f9fdd74cf Mon Sep 17 00:00:00 2001 From: HoldYourWaffle Date: Thu, 1 Jun 2023 01:15:29 +0200 Subject: [PATCH 8/8] Implement ListTag constructor with addAll De-dupes validation logic. Modifying a passed in list circumvented validation. It's still possible to modify a `ListTag` using the `List`-interface via `asList`. --- src/main/java/net/querz/nbt/ListTag.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/querz/nbt/ListTag.java b/src/main/java/net/querz/nbt/ListTag.java index 416babc0..e459f95c 100644 --- a/src/main/java/net/querz/nbt/ListTag.java +++ b/src/main/java/net/querz/nbt/ListTag.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -12,7 +13,7 @@ public non-sealed class ListTag extends CollectionTag { - private final List value; + private final List value = new ArrayList<>(); private Type type; public ListTag() { @@ -20,22 +21,13 @@ public ListTag() { } public ListTag(Type type) { - this(new ArrayList<>(), type); + this(List.of(), type); } - public ListTag(List list, Type type) { + public ListTag(Collection list, Type type) { Objects.requireNonNull(list); assimilateType(type); - - for (int i = 0; i < list.size(); i++) { - Objects.requireNonNull(list.get(i)); - - if (list.get(i).getType() != type) { - throw new IllegalArgumentException("Incorrect tag type "+list.get(i).getType()+" at index "+i+" (expected "+type+")"); - } - } - - value = list; + addAll(list); } private void assimilateType(Type type) {