mirror of
https://github.com/ViaVersion/ViaNBT.git
synced 2024-11-15 10:25:16 +01:00
3.0.0 Release
- More general refactoring - Add SNBT serializer - Rebrand to ViaNBT, as the project has shifted greatly from the original Package rename will follow at a (much) later date when ViaVersion is ready for another major version bump
This commit is contained in:
parent
fe18bc74e7
commit
63c109efde
50
README.md
50
README.md
@ -1,8 +1,11 @@
|
||||
# OpenNBT
|
||||
OpenNBT is a library for reading and writing NBT files, with some extra custom tags added to allow the storage of more data types.
|
||||
# ViaNBT
|
||||
|
||||
ViaNBT is a library for dealing with [NBT](https://minecraft.wiki/w/NBT_format) and SNBT.
|
||||
|
||||
This project is derived from an earlier version of [OpenNBT](https://github.com/GeyserMC/OpenNBT/) and contains various fundamental improvements and changes to it, including:
|
||||
|
||||
This fork contains various improvements and changes specifically made for use in Via plugins, including but not limited to the following list:
|
||||
* Most notably, move the tag name out the of tags themselves
|
||||
* `SNBT` for string serialization
|
||||
* Add primitive getter methods to number types
|
||||
* Don't wrap values given in Tag#setValue / Tag constructors
|
||||
* Abstract NumberTag class for easier number handling
|
||||
@ -12,8 +15,43 @@ This fork contains various improvements and changes specifically made for use in
|
||||
* Implement tag specific equals() methods
|
||||
* Update to Java 8
|
||||
|
||||
## Building the Source
|
||||
OpenNBT uses Maven to manage dependencies. Simply run 'mvn clean install' in the source's directory.
|
||||
This project also includes code from [adventure](https://github.com/KyoriPowered/adventure) used for SNBT serialization.
|
||||
|
||||
## Dependency
|
||||
|
||||
**Maven:**
|
||||
|
||||
```xml
|
||||
<repository>
|
||||
<id>viaversion-repo</id>
|
||||
<url>https://repo.viaversion.com</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.viaversion</groupId>
|
||||
<artifactId>nbt</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**Gradle:**
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.viaversion.com")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.viaversion:nbt:3.0.0")
|
||||
}
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Run `mvn install` in the source's directory via Maven.
|
||||
|
||||
## License
|
||||
OpenNBT is licensed under the **[MIT license](http://www.opensource.org/licenses/mit-license.html)**.
|
||||
|
||||
ViaNBT is licensed under the **[MIT license](http://www.opensource.org/licenses/mit-license.html)**.
|
||||
|
13
pom.xml
13
pom.xml
@ -4,13 +4,13 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.viaversion</groupId>
|
||||
<artifactId>opennbt</artifactId>
|
||||
<version>2.1.3</version>
|
||||
<artifactId>nbt</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>OpenNBT</name>
|
||||
<name>ViaNBT</name>
|
||||
<description>A library for reading and writing NBT files, written in Java.</description>
|
||||
<url>https://github.com/ViaVersion/OpenNBT</url>
|
||||
<url>https://github.com/ViaVersion/ViaNBT</url>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
@ -52,6 +52,7 @@
|
||||
</issueManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Expected to be bundled -->
|
||||
<dependency>
|
||||
<groupId>it.unimi.dsi</groupId>
|
||||
<artifactId>fastutil</artifactId>
|
||||
@ -61,7 +62,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>24.0.0</version>
|
||||
<version>24.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@ -90,7 +91,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
|
@ -74,12 +74,7 @@ public final class NBTIO {
|
||||
in = new GZIPInputStream(in);
|
||||
}
|
||||
|
||||
CompoundTag tag = readTag(in, littleEndian);
|
||||
if (!(tag instanceof CompoundTag)) {
|
||||
throw new IOException("Root tag is not a CompoundTag!");
|
||||
}
|
||||
|
||||
return tag;
|
||||
return readTag(in, littleEndian);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
@ -131,7 +126,7 @@ public final class NBTIO {
|
||||
*/
|
||||
public static void writeFile(CompoundTag tag, File file, boolean compressed, boolean littleEndian) throws IOException {
|
||||
if (!file.exists()) {
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) {
|
||||
if (file.getParentFile() != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
package com.github.steveice10.opennbt.conversion;
|
||||
|
||||
/**
|
||||
* An exception thrown when an error occurs while registering a converter.
|
||||
*/
|
||||
public class ConverterRegisterException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2022049594558041160L;
|
||||
|
||||
public ConverterRegisterException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConverterRegisterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConverterRegisterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ConverterRegisterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -1,17 +1,18 @@
|
||||
package com.github.steveice10.opennbt.conversion;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.builtin.ByteArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.ByteTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.CompoundTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.DoubleTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.FloatTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.IntArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.IntTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.ListTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.LongArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.LongTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.ShortTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.builtin.StringTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.ByteArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.ByteTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.CompoundTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.DoubleTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.FloatTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.IntArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.IntTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.ListTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.LongArrayTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.LongTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.ShortTagConverter;
|
||||
import com.github.steveice10.opennbt.conversion.converter.StringTagConverter;
|
||||
import com.github.steveice10.opennbt.tag.TagRegistry;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
@ -25,20 +26,19 @@ 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 java.io.Serializable;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A registry mapping tags and value types to converters.
|
||||
*/
|
||||
public class ConverterRegistry {
|
||||
private static final Map<Class<? extends Tag>, TagConverter<? extends Tag, ?>> tagToConverter = new HashMap<Class<? extends Tag>, TagConverter<? extends Tag, ?>>();
|
||||
private static final Map<Class<?>, TagConverter<? extends Tag, ?>> typeToConverter = new HashMap<Class<?>, TagConverter<? extends Tag, ?>>();
|
||||
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<>();
|
||||
|
||||
static {
|
||||
register(ByteTag.class, Byte.class, new ByteTagConverter());
|
||||
@ -63,19 +63,22 @@ public class ConverterRegistry {
|
||||
* @param tag Tag type class to register the converter to.
|
||||
* @param type Value type class to register the converter to.
|
||||
* @param converter Converter to register.
|
||||
* @throws ConverterRegisterException If an error occurs while registering the converter.
|
||||
* @throws IllegalArgumentException if the tag or type are already registered
|
||||
*/
|
||||
public static <T extends Tag, V> void register(Class<T> tag, Class<V> type, TagConverter<T, V> converter) throws ConverterRegisterException {
|
||||
if (tagToConverter.containsKey(tag)) {
|
||||
throw new ConverterRegisterException("Type conversion to tag " + tag.getName() + " is already registered.");
|
||||
public static <T extends Tag, V> void register(Class<T> tag, Class<? extends V> type, TagConverter<T, V> converter) {
|
||||
int tagId = TagRegistry.getIdFor(tag);
|
||||
if (tagId == -1) {
|
||||
throw new IllegalArgumentException("Tag " + tag.getName() + " is not a registered tag.");
|
||||
}
|
||||
if (TAG_TO_CONVERTER.containsKey(tagId)) {
|
||||
throw new IllegalArgumentException("Type conversion to tag " + tag.getName() + " is already registered.");
|
||||
}
|
||||
if (TYPE_TO_CONVERTER.containsKey(type)) {
|
||||
throw new IllegalArgumentException("Tag conversion to type " + type.getName() + " is already registered.");
|
||||
}
|
||||
|
||||
if (typeToConverter.containsKey(type)) {
|
||||
throw new ConverterRegisterException("Tag conversion to type " + type.getName() + " is already registered.");
|
||||
}
|
||||
|
||||
tagToConverter.put(tag, converter);
|
||||
typeToConverter.put(type, converter);
|
||||
TAG_TO_CONVERTER.put(tagId, converter);
|
||||
TYPE_TO_CONVERTER.put(type, converter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,8 +90,8 @@ public class ConverterRegistry {
|
||||
* @param type Value type class to unregister.
|
||||
*/
|
||||
public static <T extends Tag, V> void unregister(Class<T> tag, Class<V> type) {
|
||||
tagToConverter.remove(tag);
|
||||
typeToConverter.remove(type);
|
||||
TAG_TO_CONVERTER.remove(TagRegistry.getIdFor(tag));
|
||||
TYPE_TO_CONVERTER.remove(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,17 +103,17 @@ public class ConverterRegistry {
|
||||
* @return The converted value.
|
||||
* @throws ConversionException If a suitable converter could not be found.
|
||||
*/
|
||||
public static <T extends Tag, V> V convertToValue(T tag) throws ConversionException {
|
||||
public static <T extends Tag, V> @Nullable V convertToValue(@Nullable T tag) throws ConversionException {
|
||||
if (tag == null || tag.getValue() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!tagToConverter.containsKey(tag.getClass())) {
|
||||
TagConverter<T, ? extends V> converter = (TagConverter<T, ? extends V>) TAG_TO_CONVERTER.get(tag.getClass());
|
||||
if (converter == null) {
|
||||
throw new ConversionException("Tag type " + tag.getClass().getName() + " has no converter.");
|
||||
}
|
||||
|
||||
TagConverter<T, ?> converter = (TagConverter<T, ?>) tagToConverter.get(tag.getClass());
|
||||
return (V) converter.convert(tag);
|
||||
return converter.convert(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,56 +125,19 @@ public class ConverterRegistry {
|
||||
* @return The converted tag.
|
||||
* @throws ConversionException If a suitable converter could not be found.
|
||||
*/
|
||||
public static <V, T extends Tag> T convertToTag(V value) throws ConversionException {
|
||||
public static <V, T extends Tag> @Nullable T convertToTag(@Nullable V value) throws ConversionException {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TagConverter<T, V> converter = (TagConverter<T, V>) typeToConverter.get(value.getClass());
|
||||
// No need to check super classes since registering custom tags is not allowed
|
||||
// and all the given ones cannot be extended, super class can't be instantiated
|
||||
Class<?> valueClass = value.getClass();
|
||||
TagConverter<T, ? super V> converter = (TagConverter<T, ? super V>) TYPE_TO_CONVERTER.get(valueClass);
|
||||
if (converter == null) {
|
||||
for (Class<?> clazz : getAllClasses(value.getClass())) {
|
||||
if (typeToConverter.containsKey(clazz)) {
|
||||
try {
|
||||
converter = (TagConverter<T, V>) typeToConverter.get(clazz);
|
||||
break;
|
||||
} catch (ClassCastException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (converter == null) {
|
||||
throw new ConversionException("Value type " + value.getClass().getName() + " has no converter.");
|
||||
throw new ConversionException("Value type " + valueClass.getName() + " has no converter.");
|
||||
}
|
||||
|
||||
return converter.convert(value);
|
||||
}
|
||||
|
||||
private static Set<Class<?>> getAllClasses(Class<?> clazz) {
|
||||
Set<Class<?>> ret = new LinkedHashSet<Class<?>>();
|
||||
Class<?> c = clazz;
|
||||
while (c != null) {
|
||||
ret.add(c);
|
||||
ret.addAll(getAllSuperInterfaces(c));
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
|
||||
// Make sure Serializable is at the end to avoid mix-ups.
|
||||
if (ret.contains(Serializable.class)) {
|
||||
ret.remove(Serializable.class);
|
||||
ret.add(Serializable.class);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Set<Class<?>> getAllSuperInterfaces(Class<?> clazz) {
|
||||
Set<Class<?>> ret = new HashSet<Class<?>>();
|
||||
for (Class<?> c : clazz.getInterfaces()) {
|
||||
ret.add(c);
|
||||
ret.addAll(getAllSuperInterfaces(c));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public interface TagConverter<T extends Tag, V> {
|
||||
* @param tag Tag to convert.
|
||||
* @return The converted value.
|
||||
*/
|
||||
public V convert(T tag);
|
||||
V convert(T tag);
|
||||
|
||||
/**
|
||||
* Converts a value to a tag.
|
||||
@ -23,5 +23,5 @@ public interface TagConverter<T extends Tag, V> {
|
||||
* @param value Value to convert.
|
||||
* @return The converted tag.
|
||||
*/
|
||||
public T convert(V value);
|
||||
T convert(V value);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.ConverterRegistry;
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.DoubleTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.FloatTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.ConverterRegistry;
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ShortTag;
|
@ -1,4 +1,4 @@
|
||||
package com.github.steveice10.opennbt.conversion.builtin;
|
||||
package com.github.steveice10.opennbt.conversion.converter;
|
||||
|
||||
import com.github.steveice10.opennbt.conversion.TagConverter;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2021 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
/**
|
||||
* A character buffer designed to be inspected by a parser.
|
||||
*/
|
||||
final class CharBuffer {
|
||||
private final CharSequence sequence;
|
||||
private int index;
|
||||
|
||||
CharBuffer(final CharSequence sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the character at the current position.
|
||||
*
|
||||
* @return The current character
|
||||
*/
|
||||
public char peek() {
|
||||
return this.sequence.charAt(this.index);
|
||||
}
|
||||
|
||||
public char peek(final int offset) {
|
||||
return this.sequence.charAt(this.index + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current character and advance.
|
||||
*
|
||||
* @return current character
|
||||
*/
|
||||
public char take() {
|
||||
return this.sequence.charAt(this.index++);
|
||||
}
|
||||
|
||||
public boolean advance() {
|
||||
this.index++;
|
||||
return this.hasMore();
|
||||
}
|
||||
|
||||
public boolean hasMore() {
|
||||
return this.index < this.sequence.length();
|
||||
}
|
||||
|
||||
public boolean hasMore(final int offset) {
|
||||
return this.index + offset < this.sequence.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the provided token, and advance the reader index past the {@code until} character.
|
||||
*
|
||||
* @param until Case-insensitive token
|
||||
* @return the string starting at the current position (inclusive) and going until the location of {@code until}, exclusive
|
||||
*/
|
||||
public CharSequence takeUntil(char until) throws StringifiedTagParseException {
|
||||
until = Character.toLowerCase(until);
|
||||
int endIdx = -1;
|
||||
for (int idx = this.index; idx < this.sequence.length(); ++idx) {
|
||||
if (this.sequence.charAt(idx) == Tokens.ESCAPE_MARKER) {
|
||||
idx++;
|
||||
} else if (Character.toLowerCase(this.sequence.charAt(idx)) == until) {
|
||||
endIdx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (endIdx == -1) {
|
||||
throw this.makeError("No occurrence of " + until + " was found");
|
||||
}
|
||||
|
||||
final CharSequence result = this.sequence.subSequence(this.index, endIdx);
|
||||
this.index = endIdx + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the next non-whitespace character is the provided parameter.
|
||||
*
|
||||
* <p>If the assertion is successful, the token will be consumed.</p>
|
||||
*
|
||||
* @param expectedChar expected character
|
||||
* @return this
|
||||
* @throws StringifiedTagParseException if EOF or non-matching value is found
|
||||
*/
|
||||
public CharBuffer expect(final char expectedChar) throws StringifiedTagParseException {
|
||||
this.skipWhitespace();
|
||||
if (!this.hasMore()) {
|
||||
throw this.makeError("Expected character '" + expectedChar + "' but got EOF");
|
||||
}
|
||||
if (this.peek() != expectedChar) {
|
||||
throw this.makeError("Expected character '" + expectedChar + "' but got '" + this.peek() + "'");
|
||||
}
|
||||
this.take();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the next non-whitespace character is {@code token}, advance past it.
|
||||
*
|
||||
* <p>This method always consumes whitespace.</p>
|
||||
*
|
||||
* @param token next non-whitespace character to query
|
||||
* @return if the next non-whitespace character is {@code token}
|
||||
*/
|
||||
public boolean takeIf(final char token) {
|
||||
this.skipWhitespace();
|
||||
if (this.hasMore() && this.peek() == token) {
|
||||
this.advance();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
public CharBuffer skipWhitespace() {
|
||||
while (this.hasMore() && Character.isWhitespace(this.peek())) this.advance();
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringifiedTagParseException makeError(final String message) {
|
||||
return new StringifiedTagParseException(message, this.index);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
|
||||
/**
|
||||
* Serialization of stringifies tags.
|
||||
*/
|
||||
public final class SNBT {
|
||||
|
||||
private SNBT() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a compound tag from a {@link String}.
|
||||
*
|
||||
* @param snbt SNBT input
|
||||
* @return compound tag from the given SNBT input
|
||||
* @throws StringifiedTagParseException if an exception was encountered while reading a compound tag
|
||||
*/
|
||||
public static Tag deserialize(final String snbt) {
|
||||
final CharBuffer buffer = new CharBuffer(snbt);
|
||||
final TagStringReader parser = new TagStringReader(buffer);
|
||||
final Tag tag = parser.tag();
|
||||
if (buffer.skipWhitespace().hasMore()) {
|
||||
throw new StringifiedTagParseException("Input has trailing content", buffer.index());
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static CompoundTag deserializeCompoundTag(final String snbt) {
|
||||
final CharBuffer buffer = new CharBuffer(snbt);
|
||||
final TagStringReader reader = new TagStringReader(buffer);
|
||||
final CompoundTag tag = reader.compound();
|
||||
if (buffer.skipWhitespace().hasMore()) {
|
||||
throw new StringifiedTagParseException("Input has trailing content", buffer.index());
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a tag to SNBT.
|
||||
*
|
||||
* @param tag the compound tag
|
||||
* @return serialized SNBT
|
||||
* @throws IllegalArgumentException if an unknown tag is provided
|
||||
*/
|
||||
public static String serialize(final Tag tag) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final TagStringWriter writer = new TagStringWriter(builder);
|
||||
writer.writeTag(tag);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2020 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
// Specific Via changes:
|
||||
// - Remove buffer field
|
||||
// - Make public
|
||||
// - Make unchecked
|
||||
// - Rename to Stringified
|
||||
|
||||
/**
|
||||
* An exception thrown when parsing a stringified binary tag.
|
||||
*/
|
||||
public final class StringifiedTagParseException extends RuntimeException {
|
||||
private static final long serialVersionUID = -3001637514903912905L;
|
||||
private final int position;
|
||||
|
||||
public StringifiedTagParseException(final String message, final int position) {
|
||||
super(message);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage() + "(at position " + this.position + ")";
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2021 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.DoubleTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.FloatTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
|
||||
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 it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
// Specific Via changes:
|
||||
// - Use ViaNBT tags
|
||||
// - Small byteArray() optimization
|
||||
// - acceptLegacy = true by default
|
||||
final class TagStringReader {
|
||||
private static final int MAX_DEPTH = 512;
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
private static final long[] EMPTY_LONG_ARRAY = new long[0];
|
||||
|
||||
private final CharBuffer buffer;
|
||||
private boolean acceptLegacy = true; // Via - always true
|
||||
private int depth;
|
||||
|
||||
TagStringReader(final CharBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public CompoundTag compound() throws StringifiedTagParseException {
|
||||
this.buffer.expect(Tokens.COMPOUND_BEGIN);
|
||||
final CompoundTag compoundTag = new CompoundTag();
|
||||
if (this.buffer.takeIf(Tokens.COMPOUND_END)) {
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
while (this.buffer.hasMore()) {
|
||||
compoundTag.put(this.key(), this.tag());
|
||||
if (this.separatorOrCompleteWith(Tokens.COMPOUND_END)) {
|
||||
return compoundTag;
|
||||
}
|
||||
}
|
||||
throw this.buffer.makeError("Unterminated compound tag!");
|
||||
}
|
||||
|
||||
public ListTag list() throws StringifiedTagParseException {
|
||||
final ListTag listTag = new ListTag();
|
||||
this.buffer.expect(Tokens.ARRAY_BEGIN);
|
||||
final boolean prefixedIndex = this.acceptLegacy && this.buffer.peek() == '0' && this.buffer.peek(1) == ':';
|
||||
if (!prefixedIndex && this.buffer.takeIf(Tokens.ARRAY_END)) {
|
||||
return listTag;
|
||||
}
|
||||
while (this.buffer.hasMore()) {
|
||||
if (prefixedIndex) {
|
||||
this.buffer.takeUntil(':');
|
||||
}
|
||||
|
||||
final Tag next = this.tag();
|
||||
listTag.add(next);
|
||||
if (this.separatorOrCompleteWith(Tokens.ARRAY_END)) {
|
||||
return listTag;
|
||||
}
|
||||
}
|
||||
throw this.buffer.makeError("Reached end of file without end of list tag!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to a list tag in syntax, but returning a single array tag rather than a list of tags.
|
||||
*
|
||||
* @return array-typed tag
|
||||
*/
|
||||
public Tag array(char elementType) throws StringifiedTagParseException {
|
||||
this.buffer.expect(Tokens.ARRAY_BEGIN)
|
||||
.expect(elementType)
|
||||
.expect(Tokens.ARRAY_SIGNATURE_SEPARATOR);
|
||||
|
||||
elementType = Character.toLowerCase(elementType);
|
||||
if (elementType == Tokens.TYPE_BYTE) {
|
||||
return new ByteArrayTag(this.byteArray());
|
||||
} else if (elementType == Tokens.TYPE_INT) {
|
||||
return new IntArrayTag(this.intArray());
|
||||
} else if (elementType == Tokens.TYPE_LONG) {
|
||||
return new LongArrayTag(this.longArray());
|
||||
} else {
|
||||
throw this.buffer.makeError("Type " + elementType + " is not a valid element type in an array!");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] byteArray() throws StringifiedTagParseException {
|
||||
if (this.buffer.takeIf(Tokens.ARRAY_END)) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
|
||||
final IntList bytes = new IntArrayList(); // Via - no boxing
|
||||
while (this.buffer.hasMore()) {
|
||||
final CharSequence value = this.buffer.skipWhitespace().takeUntil(Tokens.TYPE_BYTE);
|
||||
try {
|
||||
bytes.add(Byte.parseByte(value.toString())); // Via
|
||||
} catch (final NumberFormatException ex) {
|
||||
throw this.buffer.makeError("All elements of a byte array must be bytes!");
|
||||
}
|
||||
|
||||
if (this.separatorOrCompleteWith(Tokens.ARRAY_END)) {
|
||||
final byte[] result = new byte[bytes.size()];
|
||||
for (int i = 0; i < bytes.size(); ++i) {
|
||||
result[i] = (byte) bytes.getInt(i); // Via
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw this.buffer.makeError("Reached end of document without array close");
|
||||
}
|
||||
|
||||
private int[] intArray() throws StringifiedTagParseException {
|
||||
if (this.buffer.takeIf(Tokens.ARRAY_END)) {
|
||||
return EMPTY_INT_ARRAY;
|
||||
}
|
||||
|
||||
final IntStream.Builder builder = IntStream.builder();
|
||||
while (this.buffer.hasMore()) {
|
||||
final Tag value = this.tag();
|
||||
if (!(value instanceof IntTag)) {
|
||||
throw this.buffer.makeError("All elements of an int array must be ints!");
|
||||
}
|
||||
builder.add(((NumberTag) value).asInt());
|
||||
if (this.separatorOrCompleteWith(Tokens.ARRAY_END)) {
|
||||
return builder.build().toArray();
|
||||
}
|
||||
}
|
||||
throw this.buffer.makeError("Reached end of document without array close");
|
||||
}
|
||||
|
||||
private long[] longArray() throws StringifiedTagParseException {
|
||||
if (this.buffer.takeIf(Tokens.ARRAY_END)) {
|
||||
return EMPTY_LONG_ARRAY;
|
||||
}
|
||||
|
||||
final LongStream.Builder longs = LongStream.builder();
|
||||
while (this.buffer.hasMore()) {
|
||||
final CharSequence value = this.buffer.skipWhitespace().takeUntil(Tokens.TYPE_LONG);
|
||||
try {
|
||||
longs.add(Long.parseLong(value.toString()));
|
||||
} catch (final NumberFormatException ex) {
|
||||
throw this.buffer.makeError("All elements of a long array must be longs!");
|
||||
}
|
||||
|
||||
if (this.separatorOrCompleteWith(Tokens.ARRAY_END)) {
|
||||
return longs.build().toArray();
|
||||
}
|
||||
}
|
||||
throw this.buffer.makeError("Reached end of document without array close");
|
||||
}
|
||||
|
||||
public String key() throws StringifiedTagParseException {
|
||||
this.buffer.skipWhitespace();
|
||||
final char starChar = this.buffer.peek();
|
||||
try {
|
||||
if (starChar == Tokens.SINGLE_QUOTE || starChar == Tokens.DOUBLE_QUOTE) {
|
||||
return unescape(this.buffer.takeUntil(this.buffer.take()).toString());
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
while (this.buffer.hasMore()) {
|
||||
final char peek = this.buffer.peek();
|
||||
if (!Tokens.id(peek)) {
|
||||
if (this.acceptLegacy) {
|
||||
// In legacy format, a key is any non-colon character, with escapes allowed
|
||||
if (peek == Tokens.ESCAPE_MARKER) {
|
||||
this.buffer.take(); // skip
|
||||
continue;
|
||||
} else if (peek != Tokens.COMPOUND_KEY_TERMINATOR) {
|
||||
builder.append(this.buffer.take());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
builder.append(this.buffer.take());
|
||||
}
|
||||
return builder.toString();
|
||||
} finally {
|
||||
this.buffer.expect(Tokens.COMPOUND_KEY_TERMINATOR);
|
||||
}
|
||||
}
|
||||
|
||||
public Tag tag() throws StringifiedTagParseException {
|
||||
if (this.depth++ > MAX_DEPTH) {
|
||||
throw this.buffer.makeError("Exceeded maximum allowed depth of " + MAX_DEPTH + " when reading tag");
|
||||
}
|
||||
try {
|
||||
final char startToken = this.buffer.skipWhitespace().peek();
|
||||
switch (startToken) {
|
||||
case Tokens.COMPOUND_BEGIN:
|
||||
return this.compound();
|
||||
case Tokens.ARRAY_BEGIN:
|
||||
// Maybe add in a legacy-only mode to read those?
|
||||
if (this.buffer.hasMore(2) && this.buffer.peek(2) == ';') { // we know we're an array tag
|
||||
return this.array(this.buffer.peek(1));
|
||||
} else {
|
||||
return this.list();
|
||||
}
|
||||
case Tokens.SINGLE_QUOTE:
|
||||
case Tokens.DOUBLE_QUOTE:
|
||||
// definitely a string tag
|
||||
this.buffer.advance();
|
||||
return new StringTag(unescape(this.buffer.takeUntil(startToken).toString()));
|
||||
default: // scalar
|
||||
return this.scalar();
|
||||
}
|
||||
} finally {
|
||||
this.depth--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tag that is definitely some sort of scalar.
|
||||
*
|
||||
* <p>Does not detect quoted strings, so those should have been parsed already.</p>
|
||||
*
|
||||
* @return a parsed tag
|
||||
*/
|
||||
private Tag scalar() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
int noLongerNumericAt = -1;
|
||||
while (this.buffer.hasMore()) {
|
||||
char current = this.buffer.peek();
|
||||
if (current == '\\') { // escape -- we are significantly more lenient than original format at the moment
|
||||
this.buffer.advance();
|
||||
current = this.buffer.take();
|
||||
} else if (Tokens.id(current)) {
|
||||
this.buffer.advance();
|
||||
} else { // end of value
|
||||
break;
|
||||
}
|
||||
builder.append(current);
|
||||
if (noLongerNumericAt == -1 && !Tokens.numeric(current)) {
|
||||
noLongerNumericAt = builder.length();
|
||||
}
|
||||
}
|
||||
|
||||
final int length = builder.length();
|
||||
final String built = builder.toString();
|
||||
if (noLongerNumericAt == length) {
|
||||
final char last = built.charAt(length - 1);
|
||||
try {
|
||||
switch (Character.toLowerCase(last)) { // try to read and return as a number
|
||||
case Tokens.TYPE_BYTE:
|
||||
return new ByteTag(Byte.parseByte(built.substring(0, length - 1)));
|
||||
case Tokens.TYPE_SHORT:
|
||||
return new ShortTag(Short.parseShort(built.substring(0, length - 1)));
|
||||
case Tokens.TYPE_INT:
|
||||
return new IntTag(Integer.parseInt(built.substring(0, length - 1)));
|
||||
case Tokens.TYPE_LONG:
|
||||
return new LongTag(Long.parseLong(built.substring(0, length - 1)));
|
||||
case Tokens.TYPE_FLOAT:
|
||||
final float floatValue = Float.parseFloat(built.substring(0, length - 1));
|
||||
if (Float.isFinite(floatValue)) { // don't accept NaN and Infinity
|
||||
return new FloatTag(floatValue);
|
||||
}
|
||||
break;
|
||||
case Tokens.TYPE_DOUBLE:
|
||||
final double doubleValue = Double.parseDouble(built.substring(0, length - 1));
|
||||
if (Double.isFinite(doubleValue)) { // don't accept NaN and Infinity
|
||||
return new DoubleTag(doubleValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (final NumberFormatException ignored) {
|
||||
}
|
||||
} else if (noLongerNumericAt == -1) { // if we run out of content without an explicit value separator, then we're either an integer or string tag -- all others have a character at the end
|
||||
try {
|
||||
return new IntTag(Integer.parseInt(built));
|
||||
} catch (final NumberFormatException ex) {
|
||||
if (built.indexOf('.') != -1) {
|
||||
try {
|
||||
return new DoubleTag(Double.parseDouble(built));
|
||||
} catch (final NumberFormatException ex2) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (built.equalsIgnoreCase(Tokens.LITERAL_TRUE)) {
|
||||
return new ByteTag((byte) 1);
|
||||
} else if (built.equalsIgnoreCase(Tokens.LITERAL_FALSE)) {
|
||||
return new ByteTag((byte) 0);
|
||||
}
|
||||
return new StringTag(built);
|
||||
|
||||
}
|
||||
|
||||
private boolean separatorOrCompleteWith(final char endCharacter) throws StringifiedTagParseException {
|
||||
if (this.buffer.takeIf(endCharacter)) {
|
||||
return true;
|
||||
}
|
||||
this.buffer.expect(Tokens.VALUE_SEPARATOR);
|
||||
return this.buffer.takeIf(endCharacter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove simple escape sequences from a string.
|
||||
*
|
||||
* @param withEscapes input string with escapes
|
||||
* @return string with escapes processed
|
||||
*/
|
||||
private static String unescape(final String withEscapes) {
|
||||
int escapeIdx = withEscapes.indexOf(Tokens.ESCAPE_MARKER);
|
||||
if (escapeIdx == -1) { // nothing to unescape
|
||||
return withEscapes;
|
||||
}
|
||||
int lastEscape = 0;
|
||||
final StringBuilder output = new StringBuilder(withEscapes.length());
|
||||
do {
|
||||
output.append(withEscapes, lastEscape, escapeIdx);
|
||||
lastEscape = escapeIdx + 1;
|
||||
} while ((escapeIdx = withEscapes.indexOf(Tokens.ESCAPE_MARKER, lastEscape + 1)) != -1); // add one extra character to make sure we don't include escaped backslashes
|
||||
output.append(withEscapes.substring(lastEscape));
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
public void legacy(final boolean acceptLegacy) {
|
||||
this.acceptLegacy = acceptLegacy;
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2020 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.DoubleTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.FloatTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
|
||||
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 java.util.Map;
|
||||
|
||||
// Specific Via changes:
|
||||
// - Use ViaNBT tags
|
||||
// - Do not throw IOException for non-I/O operation, replace Appendable with explicit StringBuilder
|
||||
|
||||
/**
|
||||
* An emitter for the SNBT format.
|
||||
*
|
||||
* <p>Details on the format are described in the package documentation.</p>
|
||||
*/
|
||||
final class TagStringWriter {
|
||||
private final StringBuilder out;
|
||||
/**
|
||||
* Whether a {@link Tokens#VALUE_SEPARATOR} needs to be printed before the beginning of the next object.
|
||||
*/
|
||||
private boolean needsSeparator;
|
||||
|
||||
public TagStringWriter(final StringBuilder out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
// NBT-specific
|
||||
|
||||
public TagStringWriter writeTag(final Tag tag) {
|
||||
if (tag instanceof CompoundTag) {
|
||||
return this.writeCompound((CompoundTag) tag);
|
||||
} else if (tag instanceof ListTag) {
|
||||
return this.writeList((ListTag) tag);
|
||||
} else if (tag instanceof ByteArrayTag) {
|
||||
return this.writeByteArray((ByteArrayTag) tag);
|
||||
} else if (tag instanceof IntArrayTag) {
|
||||
return this.writeIntArray((IntArrayTag) tag);
|
||||
} else if (tag instanceof LongArrayTag) {
|
||||
return this.writeLongArray((LongArrayTag) tag);
|
||||
} else if (tag instanceof StringTag) {
|
||||
return this.value(((StringTag) tag).getValue(), Tokens.EOF);
|
||||
} else if (tag instanceof ByteTag) {
|
||||
return this.value(Byte.toString(((NumberTag) tag).asByte()), Tokens.TYPE_BYTE);
|
||||
} else if (tag instanceof ShortTag) {
|
||||
return this.value(Short.toString(((NumberTag) tag).asShort()), Tokens.TYPE_SHORT);
|
||||
} else if (tag instanceof IntTag) {
|
||||
return this.value(Integer.toString(((NumberTag) tag).asInt()), Tokens.TYPE_INT);
|
||||
} else if (tag instanceof LongTag) {
|
||||
return this.value(Long.toString(((NumberTag) tag).asLong()), Character.toUpperCase(Tokens.TYPE_LONG)); // special case
|
||||
} else if (tag instanceof FloatTag) {
|
||||
return this.value(Float.toString(((NumberTag) tag).asFloat()), Tokens.TYPE_FLOAT);
|
||||
} else if (tag instanceof DoubleTag) {
|
||||
return this.value(Double.toString(((NumberTag) tag).asDouble()), Tokens.TYPE_DOUBLE);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown tag type: " + tag.getClass().getSimpleName());
|
||||
// unknown!
|
||||
}
|
||||
}
|
||||
|
||||
private TagStringWriter writeCompound(final CompoundTag tag) {
|
||||
this.beginCompound();
|
||||
for (final Map.Entry<String, Tag> entry : tag.entrySet()) {
|
||||
this.key(entry.getKey());
|
||||
this.writeTag(entry.getValue());
|
||||
}
|
||||
this.endCompound();
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter writeList(final ListTag tag) {
|
||||
this.beginList();
|
||||
for (final Tag el : tag) {
|
||||
this.printAndResetSeparator();
|
||||
this.writeTag(el);
|
||||
}
|
||||
this.endList();
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter writeByteArray(final ByteArrayTag tag) {
|
||||
this.beginArray(Tokens.TYPE_BYTE);
|
||||
|
||||
final byte[] value = tag.getValue();
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
this.printAndResetSeparator();
|
||||
this.value(Byte.toString(value[i]), Tokens.TYPE_BYTE);
|
||||
}
|
||||
this.endArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter writeIntArray(final IntArrayTag tag) {
|
||||
this.beginArray(Tokens.TYPE_INT);
|
||||
|
||||
final int[] value = tag.getValue();
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
this.printAndResetSeparator();
|
||||
this.value(Integer.toString(value[i]), Tokens.TYPE_INT);
|
||||
}
|
||||
this.endArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter writeLongArray(final LongArrayTag tag) {
|
||||
this.beginArray(Tokens.TYPE_LONG);
|
||||
|
||||
final long[] value = tag.getValue();
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
this.printAndResetSeparator();
|
||||
this.value(Long.toString(value[i]), Tokens.TYPE_LONG);
|
||||
}
|
||||
this.endArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
// Value types
|
||||
|
||||
public TagStringWriter beginCompound() {
|
||||
this.printAndResetSeparator();
|
||||
this.out.append(Tokens.COMPOUND_BEGIN);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TagStringWriter endCompound() {
|
||||
this.out.append(Tokens.COMPOUND_END);
|
||||
this.needsSeparator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TagStringWriter key(final String key) {
|
||||
this.printAndResetSeparator();
|
||||
this.writeMaybeQuoted(key, false);
|
||||
this.out.append(Tokens.COMPOUND_KEY_TERMINATOR);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TagStringWriter value(final String value, final char valueType) {
|
||||
if (valueType == Tokens.EOF) { // string doesn't have its type
|
||||
this.writeMaybeQuoted(value, true);
|
||||
} else {
|
||||
this.out.append(value);
|
||||
if (valueType != Tokens.TYPE_INT) {
|
||||
this.out.append(valueType);
|
||||
}
|
||||
}
|
||||
this.needsSeparator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TagStringWriter beginList() {
|
||||
this.printAndResetSeparator();
|
||||
this.out.append(Tokens.ARRAY_BEGIN);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TagStringWriter endList() {
|
||||
this.out.append(Tokens.ARRAY_END);
|
||||
this.needsSeparator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter beginArray(final char type) {
|
||||
this.beginList()
|
||||
.out.append(type)
|
||||
.append(Tokens.ARRAY_SIGNATURE_SEPARATOR);
|
||||
return this;
|
||||
}
|
||||
|
||||
private TagStringWriter endArray() {
|
||||
return this.endList();
|
||||
}
|
||||
|
||||
private void writeMaybeQuoted(final String content, boolean requireQuotes) {
|
||||
if (!requireQuotes) {
|
||||
for (int i = 0; i < content.length(); ++i) {
|
||||
if (!Tokens.id(content.charAt(i))) {
|
||||
requireQuotes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (requireQuotes) {
|
||||
this.out.append(Tokens.DOUBLE_QUOTE);
|
||||
this.out.append(escape(content, Tokens.DOUBLE_QUOTE));
|
||||
this.out.append(Tokens.DOUBLE_QUOTE);
|
||||
} else {
|
||||
this.out.append(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static String escape(final String content, final char quoteChar) {
|
||||
final StringBuilder output = new StringBuilder(content.length());
|
||||
for (int i = 0; i < content.length(); ++i) {
|
||||
final char c = content.charAt(i);
|
||||
if (c == quoteChar || c == '\\') {
|
||||
output.append(Tokens.ESCAPE_MARKER);
|
||||
}
|
||||
output.append(c);
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
private void printAndResetSeparator() {
|
||||
if (this.needsSeparator) {
|
||||
this.out.append(Tokens.VALUE_SEPARATOR);
|
||||
this.needsSeparator = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of adventure, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2017-2021 KyoriPowered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.steveice10.opennbt.stringified;
|
||||
|
||||
final class Tokens {
|
||||
// Compounds
|
||||
static final char COMPOUND_BEGIN = '{';
|
||||
static final char COMPOUND_END = '}';
|
||||
static final char COMPOUND_KEY_TERMINATOR = ':';
|
||||
|
||||
// Arrays
|
||||
static final char ARRAY_BEGIN = '[';
|
||||
static final char ARRAY_END = ']';
|
||||
static final char ARRAY_SIGNATURE_SEPARATOR = ';';
|
||||
|
||||
static final char VALUE_SEPARATOR = ',';
|
||||
|
||||
static final char SINGLE_QUOTE = '\'';
|
||||
static final char DOUBLE_QUOTE = '"';
|
||||
static final char ESCAPE_MARKER = '\\';
|
||||
|
||||
static final char TYPE_BYTE = 'b';
|
||||
static final char TYPE_SHORT = 's';
|
||||
static final char TYPE_INT = 'i'; // array only
|
||||
static final char TYPE_LONG = 'l';
|
||||
static final char TYPE_FLOAT = 'f';
|
||||
static final char TYPE_DOUBLE = 'd';
|
||||
|
||||
static final String LITERAL_TRUE = "true";
|
||||
static final String LITERAL_FALSE = "false";
|
||||
|
||||
static final String NEWLINE = System.getProperty("line.separator", "\n");
|
||||
static final char EOF = '\0';
|
||||
|
||||
private Tokens() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if a character is a valid component in an identifier.
|
||||
*
|
||||
* <p>An identifier character must match the expression {@code [a-zA-Z0-9_+.-]}</p>
|
||||
*
|
||||
* @param c the character
|
||||
* @return identifier
|
||||
*/
|
||||
static boolean id(final char c) {
|
||||
return (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9')
|
||||
|| c == '-' || c == '_'
|
||||
|| c == '.' || c == '+';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a character could be at some position in a number.
|
||||
*
|
||||
* <p>A string passing this check does not necessarily mean it is syntactically valid.</p>
|
||||
*
|
||||
* @param c character to check
|
||||
* @return if possibly part of a number
|
||||
*/
|
||||
static boolean numeric(final char c) {
|
||||
return (c >= '0' && c <= '9') // digit
|
||||
|| c == '+' || c == '-' // positive or negative
|
||||
|| c == 'e' || c == 'E' // exponent
|
||||
|| c == '.'; // decimal
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.github.steveice10.opennbt.tag;
|
||||
|
||||
/**
|
||||
* An exception thrown when an error occurs while created a tag instance.
|
||||
*/
|
||||
public class TagCreateException extends Exception {
|
||||
private static final long serialVersionUID = -2022049594558041160L;
|
||||
|
||||
public TagCreateException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TagCreateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TagCreateException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public TagCreateException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.github.steveice10.opennbt.tag;
|
||||
|
||||
/**
|
||||
* An exception thrown when an error occurs while registering a tag.
|
||||
*/
|
||||
public class TagRegisterException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2022049594558041160L;
|
||||
|
||||
public TagRegisterException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TagRegisterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TagRegisterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public TagRegisterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -23,12 +23,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public final class TagRegistry {
|
||||
private static final int HIGHEST_ID = LongArrayTag.ID;
|
||||
private static final Class<? extends Tag>[] idToTag = new Class[HIGHEST_ID + 1];
|
||||
private static final Supplier<? extends Tag>[] instanceSuppliers = new Supplier[HIGHEST_ID + 1];
|
||||
private static final Object2IntMap<Class<? extends Tag>> tagToId = new Object2IntOpenHashMap<>();
|
||||
private static final RegisteredTagType[] TAGS = new RegisteredTagType[HIGHEST_ID + 1];
|
||||
private static final Object2IntMap<Class<? extends Tag>> TAG_TO_ID = new Object2IntOpenHashMap<>();
|
||||
|
||||
static {
|
||||
tagToId.defaultReturnValue(-1);
|
||||
TAG_TO_ID.defaultReturnValue(-1);
|
||||
|
||||
register(ByteTag.ID, ByteTag.class, ByteTag::new);
|
||||
register(ShortTag.ID, ShortTag.class, ShortTag::new);
|
||||
@ -49,22 +48,21 @@ public final class TagRegistry {
|
||||
*
|
||||
* @param id ID of the tag.
|
||||
* @param tag Tag class to register.
|
||||
* @throws TagRegisterException If an error occurs while registering the tag.
|
||||
* @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) throws TagRegisterException {
|
||||
public static void register(int id, Class<? extends Tag> tag, Supplier<? extends Tag> supplier) {
|
||||
if (id < 0 || id > HIGHEST_ID) {
|
||||
throw new TagRegisterException("Tag ID must be between 0 and " + HIGHEST_ID);
|
||||
throw new IllegalArgumentException("Tag ID must be between 0 and " + HIGHEST_ID);
|
||||
}
|
||||
if (idToTag[id] != null) {
|
||||
throw new TagRegisterException("Tag ID \"" + id + "\" is already in use.");
|
||||
if (TAGS[id] != null) {
|
||||
throw new IllegalArgumentException("Tag ID \"" + id + "\" is already in use.");
|
||||
}
|
||||
if (tagToId.containsKey(tag)) {
|
||||
throw new TagRegisterException("Tag \"" + tag.getSimpleName() + "\" is already registered.");
|
||||
if (TAG_TO_ID.containsKey(tag)) {
|
||||
throw new IllegalArgumentException("Tag \"" + tag.getSimpleName() + "\" is already registered.");
|
||||
}
|
||||
|
||||
instanceSuppliers[id] = supplier;
|
||||
idToTag[id] = tag;
|
||||
tagToId.put(tag, id);
|
||||
TAGS[id] = new RegisteredTagType(tag, supplier);
|
||||
TAG_TO_ID.put(tag, id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,9 +71,8 @@ public final class TagRegistry {
|
||||
* @param id ID of the tag to unregister.
|
||||
*/
|
||||
public static void unregister(int id) {
|
||||
tagToId.removeInt(getClassFor(id));
|
||||
idToTag[id] = null;
|
||||
instanceSuppliers[id] = null;
|
||||
TAG_TO_ID.removeInt(getClassFor(id));
|
||||
TAGS[id] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +83,7 @@ public final class TagRegistry {
|
||||
*/
|
||||
@Nullable
|
||||
public static Class<? extends Tag> getClassFor(int id) {
|
||||
return id >= 0 && id < idToTag.length ? idToTag[id] : null;
|
||||
return id >= 0 && id < TAGS.length ? TAGS[id].type : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +93,7 @@ public final class TagRegistry {
|
||||
* @return The id of the given tag class, or -1 if it cannot be found.
|
||||
*/
|
||||
public static int getIdFor(Class<? extends Tag> clazz) {
|
||||
return tagToId.getInt(clazz);
|
||||
return TAG_TO_ID.getInt(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,14 +101,25 @@ public final class TagRegistry {
|
||||
*
|
||||
* @param id Id of the tag.
|
||||
* @return The created tag.
|
||||
* @throws TagCreateException If an error occurs while creating the tag.
|
||||
* @throws IllegalArgumentException if no tags is registered over the provided id
|
||||
*/
|
||||
public static Tag createInstance(int id) throws TagCreateException {
|
||||
Supplier<? extends Tag> supplier = id > 0 && id < instanceSuppliers.length ? instanceSuppliers[id] : null;
|
||||
public static Tag createInstance(int id) {
|
||||
Supplier<? extends Tag> supplier = id > 0 && id < TAGS.length ? TAGS[id].supplier : null;
|
||||
if (supplier == null) {
|
||||
throw new TagCreateException("Could not find tag with ID \"" + id + "\".");
|
||||
throw new IllegalArgumentException("Could not find tag with ID \"" + id + "\".");
|
||||
}
|
||||
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
private static final class RegisteredTagType {
|
||||
|
||||
private final Class<? extends Tag> type;
|
||||
private final Supplier<? extends Tag> supplier;
|
||||
|
||||
private RegisteredTagType(final Class<? extends Tag> type, final Supplier<? extends Tag> supplier) {
|
||||
this.type = type;
|
||||
this.supplier = supplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.github.steveice10.opennbt.tag.builtin;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.TagCreateException;
|
||||
import com.github.steveice10.opennbt.tag.TagRegistry;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
@ -204,8 +203,6 @@ public class CompoundTag extends Tag implements Iterable<Entry<String, Tag>> {
|
||||
tag.read(in, tagLimiter, newNestingLevel);
|
||||
this.value.put(name, tag);
|
||||
}
|
||||
} catch (TagCreateException e) {
|
||||
throw new IOException("Failed to create tag.", e);
|
||||
} catch (EOFException ignored) {
|
||||
throw new IOException("Closing tag was not found!");
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.github.steveice10.opennbt.tag.builtin;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.TagCreateException;
|
||||
import com.github.steveice10.opennbt.tag.TagRegistry;
|
||||
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
|
||||
import java.io.DataInput;
|
||||
@ -160,7 +159,7 @@ public class ListTag extends Tag implements Iterable<Tag> {
|
||||
Tag tag;
|
||||
try {
|
||||
tag = TagRegistry.createInstance(id);
|
||||
} catch (TagCreateException e) {
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Failed to create tag.", e);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
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;
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* Represents an NBT tag.
|
||||
@ -20,6 +20,16 @@ public abstract class Tag implements Cloneable {
|
||||
*/
|
||||
public abstract Object getValue();
|
||||
|
||||
/**
|
||||
* Returns the unchecked value of this tag.
|
||||
*
|
||||
* @return unchecked value of this tag
|
||||
* @param <T> expected type
|
||||
*/
|
||||
public <T> T value() {
|
||||
return (T) getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this tag from an input stream.
|
||||
*
|
||||
@ -71,26 +81,6 @@ public abstract class Tag implements Cloneable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//TODO cleanup/push down
|
||||
String value = "";
|
||||
if (this.getValue() != null) {
|
||||
value = this.getValue().toString();
|
||||
if (this.getValue().getClass().isArray()) {
|
||||
StringBuilder build = new StringBuilder();
|
||||
build.append("[");
|
||||
for (int index = 0; index < Array.getLength(this.getValue()); index++) {
|
||||
if (index > 0) {
|
||||
build.append(", ");
|
||||
}
|
||||
|
||||
build.append(Array.get(this.getValue(), index));
|
||||
}
|
||||
|
||||
build.append("]");
|
||||
value = build.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return this.getClass().getSimpleName() + " { " + value + " }";
|
||||
return SNBT.serialize(this);
|
||||
}
|
||||
}
|
||||
|
@ -39,27 +39,27 @@ public interface TagLimiter {
|
||||
void checkLevel(int nestedLevel);
|
||||
|
||||
default void countByte() {
|
||||
this.countBytes(1);
|
||||
this.countBytes(Byte.BYTES);
|
||||
}
|
||||
|
||||
default void countShort() {
|
||||
this.countBytes(2);
|
||||
this.countBytes(Short.BYTES);
|
||||
}
|
||||
|
||||
default void countInt() {
|
||||
this.countBytes(4);
|
||||
this.countBytes(Integer.BYTES);
|
||||
}
|
||||
|
||||
default void countFloat() {
|
||||
this.countBytes(4);
|
||||
this.countBytes(Double.BYTES);
|
||||
}
|
||||
|
||||
default void countLong() {
|
||||
this.countBytes(8);
|
||||
this.countBytes(Long.BYTES);
|
||||
}
|
||||
|
||||
default void countDouble() {
|
||||
this.countBytes(8);
|
||||
this.countBytes(Double.BYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user