mirror of
https://github.com/ViaVersion/ViaNBT.git
synced 2025-02-16 01:11:20 +01:00
4.0.0: Refactor NBTIO, replace clone with copy
This commit is contained in:
parent
0439d7af76
commit
1d5b6e43f2
@ -9,9 +9,8 @@ This project is derived from an earlier version of [OpenNBT](https://github.com/
|
||||
* Add primitive getter methods to number types
|
||||
* Don't wrap values given in Tag#setValue / Tag constructors
|
||||
* Abstract NumberTag class for easier number handling
|
||||
* Always read/write CompoundTags in NBTIO
|
||||
* Don't use reflection when creating tag instances
|
||||
* Directly use value in clone()
|
||||
* Directly use value in copy(), also replacing clone()
|
||||
* Implement tag specific equals() methods
|
||||
* Update to Java 8
|
||||
|
||||
@ -32,7 +31,7 @@ This project also includes code from [adventure](https://github.com/KyoriPowered
|
||||
<dependency>
|
||||
<groupId>com.viaversion</groupId>
|
||||
<artifactId>nbt</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>4.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@ -44,7 +43,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.viaversion:nbt:3.0.0")
|
||||
implementation("com.viaversion:nbt:4.0.0")
|
||||
}
|
||||
```
|
||||
|
||||
|
2
pom.xml
2
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.viaversion</groupId>
|
||||
<artifactId>nbt</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>4.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ViaNBT</name>
|
||||
|
@ -1,515 +0,0 @@
|
||||
package com.github.steveice10.opennbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* A class containing methods for reading/writing NBT tags.
|
||||
*/
|
||||
public final class NBTIO {
|
||||
|
||||
/**
|
||||
* Reads the compressed, big endian root CompoundTag from the given file.
|
||||
*
|
||||
* @param path Path of the file.
|
||||
* @return The read compound tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readFile(String path) throws IOException {
|
||||
return readFile(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the compressed, big endian root CompoundTag from the given file.
|
||||
*
|
||||
* @param file File to read from.
|
||||
* @return The read compound tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readFile(File file) throws IOException {
|
||||
return readFile(file, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the root CompoundTag from the given file.
|
||||
*
|
||||
* @param path Path of the file.
|
||||
* @param compressed Whether the NBT file is compressed.
|
||||
* @param littleEndian Whether the NBT file is little endian.
|
||||
* @return The read compound tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readFile(String path, boolean compressed, boolean littleEndian) throws IOException {
|
||||
return readFile(new File(path), compressed, littleEndian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the root CompoundTag from the given file.
|
||||
*
|
||||
* @param file File to read from.
|
||||
* @param compressed Whether the NBT file is compressed.
|
||||
* @param littleEndian Whether the NBT file is little endian.
|
||||
* @return The read compound tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readFile(File file, boolean compressed, boolean littleEndian) throws IOException {
|
||||
InputStream in = Files.newInputStream(file.toPath());
|
||||
try {
|
||||
if (compressed) {
|
||||
in = new GZIPInputStream(in);
|
||||
}
|
||||
|
||||
return readTag(in, littleEndian);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given root CompoundTag to the given file, compressed and in big endian.
|
||||
*
|
||||
* @param tag Tag to write.
|
||||
* @param path Path to write to.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeFile(CompoundTag tag, String path) throws IOException {
|
||||
writeFile(tag, new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given root CompoundTag to the given file, compressed and in big endian.
|
||||
*
|
||||
* @param tag Tag to write.
|
||||
* @param file File to write to.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeFile(CompoundTag tag, File file) throws IOException {
|
||||
writeFile(tag, file, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given root CompoundTag to the given file.
|
||||
*
|
||||
* @param tag Tag to write.
|
||||
* @param path Path to write to.
|
||||
* @param compressed Whether the NBT file should be compressed.
|
||||
* @param littleEndian Whether to write little endian NBT.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeFile(CompoundTag tag, String path, boolean compressed, boolean littleEndian) throws IOException {
|
||||
writeFile(tag, new File(path), compressed, littleEndian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given root CompoundTag to the given file.
|
||||
*
|
||||
* @param tag Tag to write.
|
||||
* @param file File to write to.
|
||||
* @param compressed Whether the NBT file should be compressed.
|
||||
* @param littleEndian Whether to write little endian NBT.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeFile(CompoundTag tag, File file, boolean compressed, boolean littleEndian) throws IOException {
|
||||
if (!file.exists()) {
|
||||
if (file.getParentFile() != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
OutputStream out = Files.newOutputStream(file.toPath());
|
||||
try {
|
||||
if (compressed) {
|
||||
out = new GZIPOutputStream(out);
|
||||
}
|
||||
|
||||
writeTag(out, tag, littleEndian);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a big endian NBT tag.
|
||||
*
|
||||
* @param in Input stream to read from.
|
||||
* @return The read tag, or null if the tag is an end tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readTag(InputStream in) throws IOException {
|
||||
return readTag(in, TagLimiter.noop());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a big endian NBT tag.
|
||||
*
|
||||
* @param in Input stream to read from.
|
||||
* @return The read tag, or null if the tag is an end tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readTag(InputStream in, TagLimiter tagLimiter) throws IOException {
|
||||
return readTag((DataInput) new DataInputStream(in), tagLimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an NBT tag.
|
||||
*
|
||||
* @param in Input stream to read from.
|
||||
* @param littleEndian Whether to read little endian NBT.
|
||||
* @return The read tag, or null if the tag is an end tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readTag(InputStream in, boolean littleEndian) throws IOException {
|
||||
return readTag((DataInput) (littleEndian ? new LittleEndianDataInputStream(in) : new DataInputStream(in)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an NBT tag.
|
||||
*
|
||||
* @param in Data input to read from.
|
||||
* @return The read tag, or null if the tag is an end tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readTag(DataInput in) throws IOException {
|
||||
return readTag(in, TagLimiter.noop());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an NBT tag.
|
||||
*
|
||||
* @param in Data input to read from.
|
||||
* @param tagLimiter taglimiter
|
||||
* @return The read tag, or null if the tag is an end tag.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static CompoundTag readTag(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
int id = in.readByte();
|
||||
if (id != CompoundTag.ID) {
|
||||
throw new IOException(String.format("Expected root tag to be a CompoundTag, was %s", id));
|
||||
}
|
||||
|
||||
// Empty name
|
||||
in.skipBytes(in.readUnsignedShort());
|
||||
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.read(in, tagLimiter);
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an NBT tag in big endian.
|
||||
*
|
||||
* @param out Output stream to write to.
|
||||
* @param tag Tag to write.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeTag(OutputStream out, CompoundTag tag) throws IOException {
|
||||
writeTag(out, tag, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an NBT tag.
|
||||
*
|
||||
* @param out Output stream to write to.
|
||||
* @param tag Tag to write.
|
||||
* @param littleEndian Whether to write little endian NBT.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeTag(OutputStream out, CompoundTag tag, boolean littleEndian) throws IOException {
|
||||
writeTag((DataOutput) (littleEndian ? new LittleEndianDataOutputStream(out) : new DataOutputStream(out)), tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an NBT tag.
|
||||
*
|
||||
* @param out Data output to write to.
|
||||
* @param tag Tag to write.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public static void writeTag(DataOutput out, CompoundTag tag) throws IOException {
|
||||
out.writeByte(CompoundTag.ID);
|
||||
out.writeUTF(""); // Empty name
|
||||
tag.write(out);
|
||||
}
|
||||
|
||||
private static final class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
|
||||
|
||||
private LittleEndianDataInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return this.in.read(b, 0, b.length);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return this.in.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
this.readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||
if (len < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else {
|
||||
int read;
|
||||
for (int pos = 0; pos < len; pos += read) {
|
||||
read = this.in.read(b, off + pos, len - pos);
|
||||
if (read < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(int n) throws IOException {
|
||||
int total = 0;
|
||||
int skipped = 0;
|
||||
while (total < n && (skipped = (int) this.in.skip(n - total)) > 0) {
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean() throws IOException {
|
||||
int val = this.in.read();
|
||||
if (val < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
int val = this.in.read();
|
||||
if (val < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (byte) val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
int val = this.in.read();
|
||||
if (val < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
int b1 = this.in.read();
|
||||
int b2 = this.in.read();
|
||||
if ((b1 | b2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (short) (b1 | (b2 << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
int b1 = this.in.read();
|
||||
int b2 = this.in.read();
|
||||
if ((b1 | b2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b1 | (b2 << 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
int b1 = this.in.read();
|
||||
int b2 = this.in.read();
|
||||
if ((b1 | b2) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return (char) (b1 | (b2 << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
int b1 = this.in.read();
|
||||
int b2 = this.in.read();
|
||||
int b3 = this.in.read();
|
||||
int b4 = this.in.read();
|
||||
if ((b1 | b2 | b3 | b4) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
long b1 = this.in.read();
|
||||
long b2 = this.in.read();
|
||||
long b3 = this.in.read();
|
||||
long b4 = this.in.read();
|
||||
long b5 = this.in.read();
|
||||
long b6 = this.in.read();
|
||||
long b7 = this.in.read();
|
||||
long b8 = this.in.read();
|
||||
if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(this.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(this.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
throw new UnsupportedOperationException("Use readUTF.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
byte[] bytes = new byte[this.readUnsignedShort()];
|
||||
this.readFully(bytes);
|
||||
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
|
||||
|
||||
private LittleEndianDataOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
this.out.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int off, int len) throws IOException {
|
||||
this.out.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
this.out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean b) throws IOException {
|
||||
this.out.write(b ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int b) throws IOException {
|
||||
this.out.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(int s) throws IOException {
|
||||
this.out.write(s & 0xFF);
|
||||
this.out.write((s >>> 8) & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChar(int c) throws IOException {
|
||||
this.out.write(c & 0xFF);
|
||||
this.out.write((c >>> 8) & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(int i) throws IOException {
|
||||
this.out.write(i & 0xFF);
|
||||
this.out.write((i >>> 8) & 0xFF);
|
||||
this.out.write((i >>> 16) & 0xFF);
|
||||
this.out.write((i >>> 24) & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(long l) throws IOException {
|
||||
this.out.write((int) (l & 0xFF));
|
||||
this.out.write((int) ((l >>> 8) & 0xFF));
|
||||
this.out.write((int) ((l >>> 16) & 0xFF));
|
||||
this.out.write((int) ((l >>> 24) & 0xFF));
|
||||
this.out.write((int) ((l >>> 32) & 0xFF));
|
||||
this.out.write((int) ((l >>> 40) & 0xFF));
|
||||
this.out.write((int) ((l >>> 48) & 0xFF));
|
||||
this.out.write((int) ((l >>> 56) & 0xFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(float f) throws IOException {
|
||||
this.writeInt(Float.floatToIntBits(f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double d) throws IOException {
|
||||
this.writeLong(Double.doubleToLongBits(d));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(String s) throws IOException {
|
||||
int len = s.length();
|
||||
for (int index = 0; index < len; index++) {
|
||||
this.out.write((byte) s.charAt(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChars(String s) throws IOException {
|
||||
int len = s.length();
|
||||
for (int index = 0; index < len; index++) {
|
||||
char c = s.charAt(index);
|
||||
this.out.write(c & 0xFF);
|
||||
this.out.write((c >>> 8) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(String s) throws IOException {
|
||||
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
this.writeShort(bytes.length);
|
||||
this.write(bytes);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* A registry mapping tags and value types to converters.
|
||||
*/
|
||||
public class ConverterRegistry {
|
||||
public final class ConverterRegistry {
|
||||
private static final Int2ObjectMap<TagConverter<? extends Tag, ?>> TAG_TO_CONVERTER = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<Class<?>, TagConverter<? extends Tag, ?>> TYPE_TO_CONVERTER = new HashMap<>();
|
||||
|
||||
|
@ -13,15 +13,18 @@ import com.github.steveice10.opennbt.tag.builtin.LongTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ShortTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A registry containing different tag classes.
|
||||
*/
|
||||
public final class TagRegistry {
|
||||
public static final int END = 0;
|
||||
private static final int HIGHEST_ID = LongArrayTag.ID;
|
||||
private static final RegisteredTagType[] TAGS = new RegisteredTagType[HIGHEST_ID + 1];
|
||||
private static final Object2IntMap<Class<? extends Tag>> TAG_TO_ID = new Object2IntOpenHashMap<>();
|
||||
@ -29,18 +32,18 @@ public final class TagRegistry {
|
||||
static {
|
||||
TAG_TO_ID.defaultReturnValue(-1);
|
||||
|
||||
register(ByteTag.ID, ByteTag.class, ByteTag::new);
|
||||
register(ShortTag.ID, ShortTag.class, ShortTag::new);
|
||||
register(IntTag.ID, IntTag.class, IntTag::new);
|
||||
register(LongTag.ID, LongTag.class, LongTag::new);
|
||||
register(FloatTag.ID, FloatTag.class, FloatTag::new);
|
||||
register(DoubleTag.ID, DoubleTag.class, DoubleTag::new);
|
||||
register(ByteArrayTag.ID, ByteArrayTag.class, ByteArrayTag::new);
|
||||
register(StringTag.ID, StringTag.class, StringTag::new);
|
||||
register(ListTag.ID, ListTag.class, ListTag::new);
|
||||
register(CompoundTag.ID, CompoundTag.class, CompoundTag::new);
|
||||
register(IntArrayTag.ID, IntArrayTag.class, IntArrayTag::new);
|
||||
register(LongArrayTag.ID, LongArrayTag.class, LongArrayTag::new);
|
||||
register(ByteTag.ID, ByteTag.class, (in, tagLimiter, nestingLevel) -> ByteTag.read(in, tagLimiter));
|
||||
register(ShortTag.ID, ShortTag.class, (in, tagLimiter, nestingLevel) -> ShortTag.read(in, tagLimiter));
|
||||
register(IntTag.ID, IntTag.class, (in, tagLimiter, nestingLevel) -> IntTag.read(in, tagLimiter));
|
||||
register(LongTag.ID, LongTag.class, (in, tagLimiter, nestingLevel) -> LongTag.read(in, tagLimiter));
|
||||
register(FloatTag.ID, FloatTag.class, (in, tagLimiter, nestingLevel) -> FloatTag.read(in, tagLimiter));
|
||||
register(DoubleTag.ID, DoubleTag.class, (in, tagLimiter, nestingLevel) -> DoubleTag.read(in, tagLimiter));
|
||||
register(ByteArrayTag.ID, ByteArrayTag.class, (in, tagLimiter, nestingLevel) -> ByteArrayTag.read(in, tagLimiter));
|
||||
register(StringTag.ID, StringTag.class, (in, tagLimiter, nestingLevel) -> StringTag.read(in, tagLimiter));
|
||||
register(ListTag.ID, ListTag.class, ListTag::read);
|
||||
register(CompoundTag.ID, CompoundTag.class, CompoundTag::read);
|
||||
register(IntArrayTag.ID, IntArrayTag.class, (in, tagLimiter, nestingLevel) -> IntArrayTag.read(in, tagLimiter));
|
||||
register(LongArrayTag.ID, LongArrayTag.class, (in, tagLimiter, nestingLevel) -> LongArrayTag.read(in, tagLimiter));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,7 +53,7 @@ public final class TagRegistry {
|
||||
* @param tag Tag class to register.
|
||||
* @throws IllegalArgumentException if the id is unexpectedly out of bounds, or if the id or tag have already been registered
|
||||
*/
|
||||
public static void register(int id, Class<? extends Tag> tag, Supplier<? extends Tag> supplier) {
|
||||
public static <T extends Tag> void register(int id, Class<T> tag, TagSupplier<T> supplier) {
|
||||
if (id < 0 || id > HIGHEST_ID) {
|
||||
throw new IllegalArgumentException("Tag ID must be between 0 and " + HIGHEST_ID);
|
||||
}
|
||||
@ -65,16 +68,6 @@ public final class TagRegistry {
|
||||
TAG_TO_ID.put(tag, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a tag class.
|
||||
*
|
||||
* @param id ID of the tag to unregister.
|
||||
*/
|
||||
public static void unregister(int id) {
|
||||
TAG_TO_ID.removeInt(getClassFor(id));
|
||||
TAGS[id] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag class with the given id.
|
||||
*
|
||||
@ -103,23 +96,28 @@ public final class TagRegistry {
|
||||
* @return The created tag.
|
||||
* @throws IllegalArgumentException if no tags is registered over the provided id
|
||||
*/
|
||||
public static Tag createInstance(int id) {
|
||||
Supplier<? extends Tag> supplier = id > 0 && id < TAGS.length ? TAGS[id].supplier : null;
|
||||
public static Tag read(int id, DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
TagSupplier<?> supplier = id > 0 && id < TAGS.length ? TAGS[id].supplier : null;
|
||||
if (supplier == null) {
|
||||
throw new IllegalArgumentException("Could not find tag with ID \"" + id + "\".");
|
||||
}
|
||||
|
||||
return supplier.get();
|
||||
return supplier.create(in, tagLimiter, nestingLevel);
|
||||
}
|
||||
|
||||
private static final class RegisteredTagType {
|
||||
|
||||
private final Class<? extends Tag> type;
|
||||
private final Supplier<? extends Tag> supplier;
|
||||
private final TagSupplier<? extends Tag> supplier;
|
||||
|
||||
private RegisteredTagType(final Class<? extends Tag> type, final Supplier<? extends Tag> supplier) {
|
||||
private <T extends Tag> RegisteredTagType(final Class<T> type, final TagSupplier<T> supplier) {
|
||||
this.type = type;
|
||||
this.supplier = supplier;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TagSupplier<T extends Tag> {
|
||||
|
||||
T create(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException;
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,20 @@ public class ByteArrayTag extends NumberArrayTag {
|
||||
* @param value The value of the tag.
|
||||
*/
|
||||
public ByteArrayTag(byte[] value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("value cannot be null");
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ByteArrayTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
byte[] value = new byte[in.readInt()];
|
||||
tagLimiter.countBytes(value.length);
|
||||
in.readFully(value);
|
||||
return new ByteArrayTag(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue() {
|
||||
return this.value;
|
||||
@ -87,14 +98,6 @@ public class ByteArrayTag extends NumberArrayTag {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
this.value = new byte[in.readInt()];
|
||||
tagLimiter.countBytes(this.value.length);
|
||||
in.readFully(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeInt(this.value.length);
|
||||
@ -115,7 +118,7 @@ public class ByteArrayTag extends NumberArrayTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ByteArrayTag clone() {
|
||||
public ByteArrayTag copy() {
|
||||
return new ByteArrayTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ public class ByteTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ByteTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countByte();
|
||||
return new ByteTag(in.readByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asByte()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class ByteTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countByte();
|
||||
this.value = in.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeByte(this.value);
|
||||
@ -76,7 +75,7 @@ public class ByteTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ByteTag clone() {
|
||||
public ByteTag copy() {
|
||||
return new ByteTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import com.github.steveice10.opennbt.tag.TagRegistry;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@ -49,11 +48,43 @@ public class CompoundTag extends Tag implements Iterable<Entry<String, Tag>> {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static CompoundTag read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.checkLevel(nestingLevel);
|
||||
int newNestingLevel = nestingLevel + 1;
|
||||
int id;
|
||||
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
while (true) {
|
||||
tagLimiter.countByte();
|
||||
id = in.readByte();
|
||||
if (id == TagRegistry.END) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = in.readUTF();
|
||||
tagLimiter.countBytes(2 * name.length());
|
||||
|
||||
Tag tag;
|
||||
try {
|
||||
tag = TagRegistry.read(id, in, tagLimiter, newNestingLevel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Failed to create tag.", e);
|
||||
}
|
||||
compoundTag.value.put(name, tag);
|
||||
}
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Tag> getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asRawString() {
|
||||
return this.value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this tag.
|
||||
*
|
||||
@ -218,32 +249,6 @@ public class CompoundTag extends Tag implements Iterable<Entry<String, Tag>> {
|
||||
return this.value.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
try {
|
||||
tagLimiter.checkLevel(nestingLevel);
|
||||
int newNestingLevel = nestingLevel + 1;
|
||||
int id;
|
||||
while (true) {
|
||||
tagLimiter.countByte();
|
||||
id = in.readByte();
|
||||
if (id == 0) {
|
||||
// End tag
|
||||
break;
|
||||
}
|
||||
|
||||
String name = in.readUTF();
|
||||
tagLimiter.countBytes(2 * name.length());
|
||||
|
||||
Tag tag = TagRegistry.createInstance(id);
|
||||
tag.read(in, tagLimiter, newNestingLevel);
|
||||
this.value.put(name, tag);
|
||||
}
|
||||
} catch (EOFException ignored) {
|
||||
throw new IOException("Closing tag was not found!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
for (Entry<String, Tag> entry : this.value.entrySet()) {
|
||||
@ -271,10 +276,10 @@ public class CompoundTag extends Tag implements Iterable<Entry<String, Tag>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CompoundTag clone() {
|
||||
public CompoundTag copy() {
|
||||
LinkedHashMap<String, Tag> newMap = new LinkedHashMap<>();
|
||||
for (Entry<String, Tag> entry : this.value.entrySet()) {
|
||||
newMap.put(entry.getKey(), entry.getValue().clone());
|
||||
newMap.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
|
||||
return new CompoundTag(newMap);
|
||||
|
@ -28,6 +28,11 @@ public class DoubleTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static DoubleTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countDouble();
|
||||
return new DoubleTag(in.readDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asDouble()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class DoubleTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countDouble();
|
||||
this.value = in.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeDouble(this.value);
|
||||
@ -76,7 +75,7 @@ public class DoubleTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DoubleTag clone() {
|
||||
public DoubleTag copy() {
|
||||
return new DoubleTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ public class FloatTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static FloatTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countFloat();
|
||||
return new FloatTag(in.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asFloat()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class FloatTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countFloat();
|
||||
this.value = in.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeFloat(this.value);
|
||||
@ -76,7 +75,7 @@ public class FloatTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FloatTag clone() {
|
||||
public FloatTag copy() {
|
||||
return new FloatTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,16 @@ public class IntArrayTag extends NumberArrayTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static IntArrayTag read(final DataInput in, final TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
int[] value = new int[in.readInt()];
|
||||
tagLimiter.countBytes(Integer.BYTES * value.length);
|
||||
for (int index = 0; index < value.length; index++) {
|
||||
value[index] = in.readInt();
|
||||
}
|
||||
return new IntArrayTag(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getValue() {
|
||||
return this.value;
|
||||
@ -89,16 +99,6 @@ public class IntArrayTag extends NumberArrayTag {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(final DataInput in, final TagLimiter tagLimiter, final int nestingLevel) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
this.value = new int[in.readInt()];
|
||||
tagLimiter.countBytes(4 * this.value.length);
|
||||
for (int index = 0; index < this.value.length; index++) {
|
||||
this.value[index] = in.readInt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final DataOutput out) throws IOException {
|
||||
out.writeInt(this.value.length);
|
||||
@ -121,7 +121,7 @@ public class IntArrayTag extends NumberArrayTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IntArrayTag clone() {
|
||||
public IntArrayTag copy() {
|
||||
return new IntArrayTag(this.value.clone());
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ public class IntTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static IntTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
return new IntTag(in.readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asInt()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class IntTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
this.value = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeInt(this.value);
|
||||
@ -76,7 +75,7 @@ public class IntTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IntTag clone() {
|
||||
public IntTag copy() {
|
||||
return new IntTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class ListTag extends Tag implements Iterable<Tag> {
|
||||
public static final int ID = 9;
|
||||
private final List<Tag> value;
|
||||
private List<Tag> value;
|
||||
private Class<? extends Tag> type;
|
||||
|
||||
/**
|
||||
@ -28,6 +28,7 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
|
||||
/**
|
||||
* Creates an empty list tag and type.
|
||||
*
|
||||
* @param type Tag type of the list.
|
||||
*/
|
||||
public ListTag(@Nullable Class<? extends Tag> type) {
|
||||
@ -43,15 +44,47 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
* @throws IllegalArgumentException If all tags in the list are not of the same type.
|
||||
*/
|
||||
public ListTag(List<Tag> value) throws IllegalArgumentException {
|
||||
this.value = new ArrayList<>(value.size());
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
public static ListTag read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.checkLevel(nestingLevel);
|
||||
tagLimiter.countBytes(Byte.BYTES + Integer.BYTES);
|
||||
|
||||
int id = in.readByte();
|
||||
Class<? extends Tag> type = null;
|
||||
if (id != TagRegistry.END) {
|
||||
type = TagRegistry.getClassFor(id);
|
||||
if (type == null) {
|
||||
throw new IOException("Unknown tag ID in ListTag: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
ListTag listTag = new ListTag(type);
|
||||
int count = in.readInt();
|
||||
int newNestingLevel = nestingLevel + 1;
|
||||
for (int index = 0; index < count; index++) {
|
||||
Tag tag;
|
||||
try {
|
||||
tag = TagRegistry.read(id, in, tagLimiter, newNestingLevel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Failed to create tag.", e);
|
||||
}
|
||||
listTag.add(tag);
|
||||
}
|
||||
return listTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asRawString() {
|
||||
return this.value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this tag.
|
||||
* The list tag's type will be set to that of the first tag being added, or null if the given list is empty.
|
||||
@ -60,15 +93,14 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
* @throws IllegalArgumentException If all tags in the list are not of the same type.
|
||||
*/
|
||||
public void setValue(List<Tag> value) throws IllegalArgumentException {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("value cannot be null");
|
||||
}
|
||||
|
||||
this.type = null;
|
||||
this.value.clear();
|
||||
|
||||
for (Tag tag : value) {
|
||||
this.add(tag);
|
||||
this.value = new ArrayList<>(value);
|
||||
if (!value.isEmpty()) {
|
||||
this.type = value.get(0).getClass();
|
||||
for (int i = 1; i < value.size(); i++) {
|
||||
this.checkType(value.get(i));
|
||||
}
|
||||
} else {
|
||||
this.type = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,20 +122,22 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
* @throws IllegalArgumentException If the tag's type differs from the list tag's type.
|
||||
*/
|
||||
public boolean add(Tag tag) throws IllegalArgumentException {
|
||||
if (tag == null) {
|
||||
throw new NullPointerException("tag cannot be null");
|
||||
}
|
||||
|
||||
// If empty list, use this as tag type.
|
||||
// If currently empty, use this as the tag type
|
||||
if (this.type == null) {
|
||||
this.type = tag.getClass();
|
||||
} else if (tag.getClass() != this.type) {
|
||||
throw new IllegalArgumentException("Tag type " + tag.getClass().getSimpleName() + " differs from list type " + this.type.getSimpleName());
|
||||
this.checkType(tag);
|
||||
}
|
||||
|
||||
return this.value.add(tag);
|
||||
}
|
||||
|
||||
private void checkType(Tag tag) throws IllegalArgumentException {
|
||||
if (tag.getClass() != this.type) {
|
||||
throw new IllegalArgumentException("Tag type " + tag.getClass().getSimpleName() + " differs from list type " + this.type.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tag from this list tag.
|
||||
*
|
||||
@ -143,39 +177,10 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
return this.value.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
this.type = null;
|
||||
|
||||
tagLimiter.checkLevel(nestingLevel);
|
||||
tagLimiter.countBytes(1 + 4);
|
||||
int id = in.readByte();
|
||||
if (id != 0) {
|
||||
this.type = TagRegistry.getClassFor(id);
|
||||
if (this.type == null) {
|
||||
throw new IOException("Unknown tag ID in ListTag: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
int count = in.readInt();
|
||||
int newNestingLevel = nestingLevel + 1;
|
||||
for (int index = 0; index < count; index++) {
|
||||
Tag tag;
|
||||
try {
|
||||
tag = TagRegistry.createInstance(id);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Failed to create tag.", e);
|
||||
}
|
||||
|
||||
tag.read(in, tagLimiter, newNestingLevel);
|
||||
this.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
if (this.type == null) {
|
||||
out.writeByte(0);
|
||||
out.writeByte(TagRegistry.END);
|
||||
} else {
|
||||
int id = TagRegistry.getIdFor(this.type);
|
||||
if (id == -1) {
|
||||
@ -192,10 +197,10 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ListTag clone() {
|
||||
public ListTag copy() {
|
||||
List<Tag> newList = new ArrayList<>();
|
||||
for (Tag value : this.value) {
|
||||
newList.add(value.clone());
|
||||
newList.add(value.copy());
|
||||
}
|
||||
|
||||
return new ListTag(newList);
|
||||
|
@ -33,6 +33,16 @@ public class LongArrayTag extends NumberArrayTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static LongArrayTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
long[] value = new long[in.readInt()];
|
||||
tagLimiter.countBytes(Long.BYTES * value.length);
|
||||
for (int index = 0; index < value.length; index++) {
|
||||
value[index] = in.readLong();
|
||||
}
|
||||
return new LongArrayTag(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] getValue() {
|
||||
return this.value;
|
||||
@ -89,16 +99,6 @@ public class LongArrayTag extends NumberArrayTag {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countInt();
|
||||
this.value = new long[in.readInt()];
|
||||
tagLimiter.countBytes(8 * this.value.length);
|
||||
for (int index = 0; index < this.value.length; index++) {
|
||||
this.value[index] = in.readLong();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeInt(this.value.length);
|
||||
@ -121,7 +121,7 @@ public class LongArrayTag extends NumberArrayTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LongArrayTag clone() {
|
||||
public LongArrayTag copy() {
|
||||
return new LongArrayTag(this.value.clone());
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ public class LongTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static LongTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countLong();
|
||||
return new LongTag(in.readLong());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asLong()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class LongTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countLong();
|
||||
this.value = in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeLong(this.value);
|
||||
@ -76,7 +75,7 @@ public class LongTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LongTag clone() {
|
||||
public LongTag copy() {
|
||||
return new LongTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ public class ShortTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ShortTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
tagLimiter.countShort();
|
||||
return new ShortTag(in.readShort());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #asShort()}
|
||||
*/
|
||||
@ -51,12 +56,6 @@ public class ShortTag extends NumberTag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
tagLimiter.countShort();
|
||||
this.value = in.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeShort(this.value);
|
||||
@ -76,7 +75,7 @@ public class ShortTag extends NumberTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ShortTag clone() {
|
||||
public ShortTag copy() {
|
||||
return new ShortTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,12 @@ public class StringTag extends Tag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static StringTag read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
final String value = in.readUTF();
|
||||
tagLimiter.countBytes(2 * value.length()); // More or less, ignoring the length reading
|
||||
return new StringTag(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
@ -53,12 +59,6 @@ public class StringTag extends Tag {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException {
|
||||
this.value = in.readUTF();
|
||||
tagLimiter.countBytes(2 * value.length()); // More or less, ignoring the length reading
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
out.writeUTF(this.value);
|
||||
@ -78,7 +78,7 @@ public class StringTag extends Tag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final StringTag clone() {
|
||||
public StringTag copy() {
|
||||
return new StringTag(this.value);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
package com.github.steveice10.opennbt.tag.builtin;
|
||||
|
||||
import com.github.steveice10.opennbt.stringified.SNBT;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
@ -11,24 +9,22 @@ import java.io.IOException;
|
||||
* <p>
|
||||
* Tags should also have setter methods specific to their value types.
|
||||
*/
|
||||
public abstract class Tag implements Cloneable {
|
||||
public abstract class Tag {
|
||||
|
||||
/**
|
||||
* Gets the value of this tag.
|
||||
* Returns the value of this tag.
|
||||
*
|
||||
* @return The value of this tag.
|
||||
* @return value of this tag
|
||||
*/
|
||||
public abstract Object getValue();
|
||||
|
||||
/**
|
||||
* Returns the raw string representation of the value of this tag.
|
||||
* For SNBT, use {@link SNBT#serialize(Tag)} or {@link #toString()}.
|
||||
* For SNBT, use {@link SNBT#serialize(Tag)}.
|
||||
*
|
||||
* @return raw string representation of the value of this tag
|
||||
*/
|
||||
public String asRawString() {
|
||||
return this.getValue().toString();
|
||||
}
|
||||
public abstract String asRawString();
|
||||
|
||||
/**
|
||||
* Returns the unchecked value of this tag.
|
||||
@ -40,54 +36,27 @@ public abstract class Tag implements Cloneable {
|
||||
return (T) getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this tag from an input stream.
|
||||
*
|
||||
* @param in Stream to write to.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public final void read(DataInput in) throws IOException {
|
||||
this.read(in, TagLimiter.noop(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this tag from an input stream.
|
||||
*
|
||||
* @param in Stream to write to.
|
||||
* @param tagLimiter taglimiter
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public final void read(DataInput in, TagLimiter tagLimiter) throws IOException {
|
||||
this.read(in, tagLimiter, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this tag from an input stream.
|
||||
*
|
||||
* @param in Stream to write to.
|
||||
* @param tagLimiter taglimiter
|
||||
* @param nestingLevel current level of nesting
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
*/
|
||||
public abstract void read(DataInput in, TagLimiter tagLimiter, int nestingLevel) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes this tag to an output stream.
|
||||
*
|
||||
* @param out Stream to write to.
|
||||
* @throws java.io.IOException If an I/O error occurs.
|
||||
* @param out data output to write to
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public abstract void write(DataOutput out) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the NBT tag id of this tag type, used in I/O.
|
||||
*
|
||||
* @return Id of the tag this class represents
|
||||
* @return ID of the tag this class represents
|
||||
*/
|
||||
public abstract int getTagId();
|
||||
|
||||
@Override
|
||||
public abstract Tag clone();
|
||||
/**
|
||||
* Returns a copy of this tag.
|
||||
*
|
||||
* @return a copy of this tag
|
||||
*/
|
||||
public abstract Tag copy();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -0,0 +1,86 @@
|
||||
package com.github.steveice10.opennbt.tag.io;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.TagRegistry;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Utility to read and write NBT tags.
|
||||
*/
|
||||
public final class NBTIO {
|
||||
|
||||
private NBTIO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NBT tag reader. If a tag limiter is set, the reader is reusable, but not thread-safe.
|
||||
*
|
||||
* @return NBT tag reader
|
||||
*/
|
||||
public static TagReader<Tag> reader() {
|
||||
return new TagReader<>(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NBT tag reader to read an expected tag type. If a tag limiter is set, the reader is reusable, but not thread-safe.
|
||||
*
|
||||
* @param expectedTagType the expected tag type, or null if any is accepted
|
||||
* @param <T> the expected tag type
|
||||
* @return NBT tag reader
|
||||
*/
|
||||
public static <T extends Tag> TagReader<T> reader(final Class<T> expectedTagType) {
|
||||
return new TagReader<>(expectedTagType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reusable NBT tag writer.
|
||||
*
|
||||
* @return reusable NBT tag writer
|
||||
*/
|
||||
public static TagWriter writer() {
|
||||
return new TagWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a named NBT tag from a data input.
|
||||
*
|
||||
* @param in input stream to read from
|
||||
* @param tagLimiter tag limiter to use
|
||||
* @param named whether the tag is named
|
||||
* @param expectedTagType the expected tag type, or null if any is accepted
|
||||
* @return the read tag
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static <T extends Tag> T readTag(final DataInput in, final TagLimiter tagLimiter, final boolean named, @Nullable final Class<T> expectedTagType) throws IOException {
|
||||
final int id = in.readByte();
|
||||
if (expectedTagType != null && expectedTagType != TagRegistry.getClassFor(id)) {
|
||||
throw new IOException("Expected tag type " + expectedTagType.getSimpleName() + " but got " + TagRegistry.getClassFor(id).getSimpleName());
|
||||
}
|
||||
|
||||
if (named) {
|
||||
in.skipBytes(in.readUnsignedShort()); // Skip name
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return (T) TagRegistry.read(id, in, tagLimiter, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a named NBT tag to a data output.
|
||||
*
|
||||
* @param out output stream to write to
|
||||
* @param tag tag to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static void writeTag(final DataOutput out, final Tag tag, final boolean named) throws IOException {
|
||||
out.writeByte(tag.getTagId());
|
||||
if (named) {
|
||||
out.writeUTF(""); // Empty name
|
||||
}
|
||||
tag.write(out);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.github.steveice10.opennbt.tag.io;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* NBT tag reader.
|
||||
*
|
||||
* @param <T> the expected tag type
|
||||
* @see NBTIO#reader()
|
||||
*/
|
||||
public final class TagReader<T extends Tag> {
|
||||
private final Class<T> expectedTagType;
|
||||
private TagLimiter tagLimiter = TagLimiter.noop();
|
||||
private boolean named;
|
||||
|
||||
TagReader(@Nullable final Class<T> expectedTagType) {
|
||||
this.expectedTagType = expectedTagType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tag limiter to use per read tag, making the reader no longer thread-safe.
|
||||
*
|
||||
* @param tagLimiter the tag limiter to use
|
||||
* @return self
|
||||
*/
|
||||
public TagReader<T> tagLimiter(final TagLimiter tagLimiter) {
|
||||
this.tagLimiter = tagLimiter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this reader to read a named tag.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public TagReader<T> named() {
|
||||
this.named = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the tag from the given data output.
|
||||
*
|
||||
* @param in data input to read from
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public T read(final DataInput in) throws IOException {
|
||||
this.tagLimiter.reset();
|
||||
return NBTIO.readTag(in, this.tagLimiter, this.named, this.expectedTagType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a tag from the given input stream.
|
||||
*
|
||||
* @param in input stream to read from
|
||||
* @return the read tag
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public T read(final InputStream in) throws IOException {
|
||||
final DataInput dataInput = new DataInputStream(in);
|
||||
return this.read(dataInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a tag from the given path. At least so far, the standard format is always named, so make sure to call {@link #named()}.
|
||||
*
|
||||
* @param path path to read from
|
||||
* @param compressed whether the file is compressed
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public T read(final Path path, final boolean compressed) throws IOException {
|
||||
InputStream in = new FastBufferedInputStream(Files.newInputStream(path));
|
||||
try {
|
||||
if (compressed) {
|
||||
in = new GZIPInputStream(in);
|
||||
}
|
||||
return this.read(in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.github.steveice10.opennbt.tag.io;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* Reusable NBT tag writer.
|
||||
*
|
||||
* @see NBTIO#writer()
|
||||
*/
|
||||
public final class TagWriter {
|
||||
private boolean named;
|
||||
|
||||
/**
|
||||
* Sets this writer to write a named tag.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public TagWriter named() {
|
||||
this.named = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the tag to the given data output.
|
||||
*
|
||||
* @param out output stream to write to
|
||||
* @param tag tag to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void write(final DataOutput out, final Tag tag) throws IOException {
|
||||
NBTIO.writeTag(out, tag, this.named);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the tag to the given output stream.
|
||||
*
|
||||
* @param out output stream to write to
|
||||
* @param tag tag to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void write(final OutputStream out, final Tag tag) throws IOException {
|
||||
NBTIO.writeTag(new DataOutputStream(out), tag, this.named);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the tag to the given path. At least so far, the standard format is always named, so make sure to call {@link #named()}.
|
||||
*
|
||||
* @param path path to write to
|
||||
* @param tag tag to write
|
||||
* @param compressed whether to compress the file
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void write(final Path path, final Tag tag, final boolean compressed) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.createFile(path);
|
||||
}
|
||||
|
||||
OutputStream out = new FastBufferedOutputStream(Files.newOutputStream(path));
|
||||
try {
|
||||
if (compressed) {
|
||||
out = new GZIPOutputStream(out);
|
||||
}
|
||||
this.write(out, tag);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -26,4 +26,8 @@ final class NoopTagLimiter implements TagLimiter {
|
||||
public int bytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
}
|
||||
|
@ -82,4 +82,9 @@ public interface TagLimiter {
|
||||
* @return currently read bytes
|
||||
*/
|
||||
int bytes();
|
||||
|
||||
/**
|
||||
* Resets the current byte count.
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
@ -40,4 +40,9 @@ final class TagLimiterImpl implements TagLimiter {
|
||||
public int bytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.bytes = 0;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user