├── settings.gradle ├── .gitignore ├── src ├── test │ ├── resources │ │ └── bigtest.nbt │ └── java │ │ └── net │ │ └── kyori │ │ └── nbt │ │ ├── BigTest.java │ │ └── ReadWriteTest.java └── main │ └── java │ └── net │ └── kyori │ └── nbt │ ├── IndexedCollectionTag.java │ ├── display │ ├── TagDisplay.java │ └── StringTagDisplay.java │ ├── CollectionTag.java │ ├── EndTag.java │ ├── Tag.java │ ├── NumberTag.java │ ├── StringTag.java │ ├── IntTag.java │ ├── ShortTag.java │ ├── LongTag.java │ ├── FloatTag.java │ ├── DoubleTag.java │ ├── ByteArrayTag.java │ ├── ByteTag.java │ ├── IntArrayTag.java │ ├── LongArrayTag.java │ ├── TagType.java │ ├── TagIO.java │ ├── ListTag.java │ └── CompoundTag.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── readme.md ├── .travis.sh ├── license.txt ├── header.txt ├── .travis.yml ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'nbt' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /build/ 4 | /classes/ 5 | /out/ 6 | /*.iml 7 | -------------------------------------------------------------------------------- /src/test/resources/bigtest.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KyoriPowered/nbt/HEAD/src/test/resources/bigtest.nbt -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KyoriPowered/nbt/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # nbt - legacy, no longer maintained 2 | 3 | This project has been merged into [Adventure](https://github.com/KyoriPowered/adventure). 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip 6 | -------------------------------------------------------------------------------- /.travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ${TRAVIS_PULL_REQUEST} = 'false' ] && [ ${TRAVIS_BRANCH} = 'master' ]; then 4 | ./gradlew -PsonatypeUsername="${SONATYPE_USERNAME}" -PsonatypePassword="${SONATYPE_PASSWORD}" build uploadArchives 5 | else 6 | ./gradlew build 7 | fi 8 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 KyoriPowered 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | This file is part of nbt, licensed under the MIT License. 2 | 3 | Copyright (c) 2017 KyoriPowered 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/IndexedCollectionTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import java.util.List; 27 | 28 | /** 29 | * An indexed collection tag. 30 | */ 31 | public interface IndexedCollectionTag extends CollectionTag, List { 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/display/TagDisplay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt.display; 25 | 26 | import net.kyori.nbt.Tag; 27 | import org.checkerframework.checker.nullness.qual.NonNull; 28 | 29 | /** 30 | * A tag renderer. 31 | * 32 | * @param the result type 33 | */ 34 | public interface TagDisplay { 35 | /** 36 | * Renders a tag. 37 | * 38 | * @param tag the tag 39 | * @return the rendered result 40 | */ 41 | @NonNull R render(final @NonNull Tag tag); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/CollectionTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | /** 27 | * A collection tag. 28 | */ 29 | public interface CollectionTag extends Tag { 30 | /** 31 | * Gets the size of this collection tag. 32 | * 33 | * @return the size 34 | */ 35 | int size(); 36 | 37 | /** 38 | * Tests if this collection tag is empty. 39 | * 40 | * @return {@code true} if this collection tag is empty, {@code false} otherwise 41 | */ 42 | boolean isEmpty(); 43 | } 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | 5 | dist: trusty 6 | sudo: false 7 | env: 8 | global: 9 | - secure: "2npM290EL41ojRbNIgz+z61JdACXVHtUgFUvqtwTdR1MulnjSUJUOpcGmF3JhTkwaDdmYq/tVCfxFD02u1vjSLDfRQLw7fiESOQ6L00AChmnJsN31NFYl1UGbgUHeNYvidexQ/A/XamHvLV9bCchZIuZmW3fvyvx9cfn7vqsZod6Hnr39xA431Vs5lzTZ53pO/wHkCTEeVRwtX5NULEAJOyKE00V4KoJTsMwnwGqSCa0jGzQtkDtikJ8yFWsSZeJt6lSOdFCH6bsjHe4z28eUar05fZn7UPkOPqcWAQdqft7jbPLv+J43TO/tSaT4yn3QL2kG25TT6T3CQ+JUZw3vA3H1lzDvt47QcOOSNg8VS7gGMaN8DDIJzfaU2yTYEtEwHO0RPf2soZgRDe14y67bHU/lTja00njf59kymUSTzrLLWLIpjuyaZMT9NiGdeG+aqFOuzY/gchH8VTPkLQoIx9OCLrT7/wFKqb0eeUzDcMZXT6LGvBPOHtgCPnWO3ZHmO9G5mMu7sNyWB0Ai32csDwNHBpehJpLruyloQ0BorJpGgNL4uRjDsl8iVb9XmNOUsRwKZ+GZpwyVN2Sl7Jxk7kgkvetaG2sfW91Vogr+CwtKj+WQeySuZEa0VZXc4EHYPUZ0AKL4OxMKrzdxtrGk12p9BQkfNbjnEdKuA4xS38=" 10 | - secure: "ypE5+ESoWPR9B7u63rntQ2Biy3B5Q/lXDm2Q7KF/xFGdx0HTZ5Xs7XgV9NTaw12isuhUwy4p4PM2U65Pg1EZtMHTx9uNYBLBU2sXz9qrdrV9F1QONS7r+qbwhBK119P9TFqCo/X0L4oNWBUF9l1fPkCdrqYuxaEEJoCPgIrBaSZW80FVOPqjoT+zWDFycqx7RhNRRwur38rR6MgHHMkBOlxsp306ZlC58LHgKeZXOZluTpt2rjP8QYwNGEzmVxDv6Gy9VXF7KjRZH3hrCr+JHj56Iss7xfcZatnXFWal09wMjGZddNnhkn51VHMIxQdpTf1xRmbPYVDxX7Q3ft3Cew/mqCSEPPv2BiA9sRpOO+XyFZeHvNvuPsbD9uaJJisit13agudnDjH75aElkdbw/IDPz6F69dZJLqj2lydxiK3gQItKOIKdZq4mCkCNWBZiKEtRlYJPG+qGPXj66/fCoCB8PF8KqYwOqkkUxenWYD22/NAe0KXL+RbF0NOOCBmJlnY/hfdg3sYdIQGUdWrxILf3g+ih6o4LknVjm5xzKZTU6YL0VqNyAWsqRioIGYmxR4V9oqSsmsKdqrpdDJb4oTokEDm6tNTdSkIUVuq7soRKfeN5NORQwuyUVRLmWXZwtqDumykr0mY14HO3hYAfciQetIFi5VEHvcNbgveCy14=" 11 | 12 | install: true 13 | script: ./.travis.sh 14 | 15 | after_success: 16 | - bash <(curl -s https://codecov.io/bash) 17 | 18 | before_cache: 19 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 20 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 21 | cache: 22 | directories: 23 | - $HOME/.gradle/caches/ 24 | - $HOME/.gradle/wrapper/ 25 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/EndTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | 31 | /** 32 | * An end tag. 33 | */ 34 | public final class EndTag implements Tag { 35 | EndTag() { 36 | } 37 | 38 | @Override 39 | public void read(final @NonNull DataInput input, final int depth) { 40 | } 41 | 42 | @Override 43 | public void write(final @NonNull DataOutput output) { 44 | } 45 | 46 | @Override 47 | public @NonNull TagType type() { 48 | return TagType.END; 49 | } 50 | 51 | @Override 52 | public @NonNull EndTag copy() { 53 | return this; // end tags have no data 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return TagType.END.hashCode(); 59 | } 60 | 61 | @Override 62 | public boolean equals(final Object that) { 63 | return this == that || that instanceof EndTag; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/Tag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag. 34 | */ 35 | public interface Tag { 36 | /** 37 | * Reads the value of this tag from {@code input}. 38 | * 39 | * @param input the input 40 | * @param depth the depth 41 | * @throws IOException if an exception was encountered while reading 42 | */ 43 | void read(final @NonNull DataInput input, final int depth) throws IOException; 44 | 45 | /** 46 | * Writes the value of this tag to {@code output}. 47 | * 48 | * @param output the output 49 | * @throws IOException if an exception was encountered while writing 50 | */ 51 | void write(final @NonNull DataOutput output) throws IOException; 52 | 53 | /** 54 | * Gets the type of this tag. 55 | * 56 | * @return the type 57 | */ 58 | @NonNull TagType type(); 59 | 60 | /** 61 | * Creates a copy of this tag. 62 | * 63 | * @return a copy of this tag 64 | */ 65 | @NonNull Tag copy(); 66 | 67 | @Override 68 | int hashCode(); 69 | 70 | @Override 71 | boolean equals(final Object that); 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/NumberTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | /** 27 | * A number tag. 28 | */ 29 | public interface NumberTag extends Tag { 30 | /** 31 | * Gets the byte value. 32 | * 33 | * @return the byte value 34 | */ 35 | byte byteValue(); 36 | 37 | /** 38 | * Gets the double value. 39 | * 40 | * @return the double value 41 | */ 42 | double doubleValue(); 43 | 44 | /** 45 | * Gets the float value. 46 | * 47 | * @return the float value 48 | */ 49 | float floatValue(); 50 | 51 | /** 52 | * Gets the int value. 53 | * 54 | * @return the int value 55 | */ 56 | int intValue(); 57 | 58 | /** 59 | * Gets the long value. 60 | * 61 | * @return the long value 62 | */ 63 | long longValue(); 64 | 65 | /** 66 | * Gets the short value. 67 | * 68 | * @return the short value 69 | */ 70 | short shortValue(); 71 | 72 | // rather than depending on math as a whole, we'll just copy these two methods over 73 | // https://github.com/KyoriPowered/math/blob/master/src/main/java/net/kyori/math/Mth.java 74 | static int floor(final double n) { 75 | final int i = (int) n; 76 | return n < (double) i ? i - 1 : i; 77 | } 78 | 79 | static int floor(final float n) { 80 | final int i = (int) n; 81 | return n < (float) i ? i - 1 : i; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/StringTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | import java.util.Objects; 32 | 33 | import static java.util.Objects.requireNonNull; 34 | 35 | /** 36 | * A tag representing a {@link String}. 37 | */ 38 | public final class StringTag implements Tag { 39 | private String value; 40 | 41 | StringTag() { 42 | } 43 | 44 | public StringTag(final @NonNull String value) { 45 | this.value = requireNonNull(value, "value"); 46 | } 47 | 48 | public @NonNull String value() { 49 | return this.value; 50 | } 51 | 52 | @Override 53 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 54 | this.value = input.readUTF(); 55 | } 56 | 57 | @Override 58 | public void write(final @NonNull DataOutput output) throws IOException { 59 | output.writeUTF(this.value); 60 | } 61 | 62 | @Override 63 | public @NonNull TagType type() { 64 | return TagType.STRING; 65 | } 66 | 67 | @Override 68 | public @NonNull StringTag copy() { 69 | return new StringTag(this.value); 70 | } 71 | 72 | @Override 73 | public int hashCode() { 74 | return this.value.hashCode(); 75 | } 76 | 77 | @Override 78 | public boolean equals(final Object that) { 79 | return this == that || (that instanceof StringTag && Objects.equals(this.value, ((StringTag) that).value)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/IntTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing an {@code int}. 34 | */ 35 | public final class IntTag implements NumberTag { 36 | /** 37 | * The int value. 38 | */ 39 | private int value; 40 | 41 | IntTag() { 42 | } 43 | 44 | public IntTag(final int value) { 45 | this.value = value; 46 | } 47 | 48 | @Override 49 | public byte byteValue() { 50 | return (byte) (this.value & 0xff); 51 | } 52 | 53 | @Override 54 | public double doubleValue() { 55 | return (double) this.value; 56 | } 57 | 58 | @Override 59 | public float floatValue() { 60 | return (float) this.value; 61 | } 62 | 63 | @Override 64 | public int intValue() { 65 | return this.value; 66 | } 67 | 68 | @Override 69 | public long longValue() { 70 | return (long) this.value; 71 | } 72 | 73 | @Override 74 | public short shortValue() { 75 | return (short) (this.value & 0xffff); 76 | } 77 | 78 | @Override 79 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 80 | this.value = input.readInt(); 81 | } 82 | 83 | @Override 84 | public void write(final @NonNull DataOutput output) throws IOException { 85 | output.writeInt(this.value); 86 | } 87 | 88 | @Override 89 | public @NonNull TagType type() { 90 | return TagType.INT; 91 | } 92 | 93 | @Override 94 | public @NonNull IntTag copy() { 95 | return new IntTag(this.value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Integer.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof IntTag && this.value == ((IntTag) that).value); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/ShortTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing a {@code short}. 34 | */ 35 | public final class ShortTag implements NumberTag { 36 | /** 37 | * The short value. 38 | */ 39 | private short value; 40 | 41 | ShortTag() { 42 | } 43 | 44 | public ShortTag(final short value) { 45 | this.value = value; 46 | } 47 | 48 | @Override 49 | public byte byteValue() { 50 | return (byte) (this.value & 0xff); 51 | } 52 | 53 | @Override 54 | public double doubleValue() { 55 | return (double) this.value; 56 | } 57 | 58 | @Override 59 | public float floatValue() { 60 | return (float) this.value; 61 | } 62 | 63 | @Override 64 | public int intValue() { 65 | return (int) this.value; 66 | } 67 | 68 | @Override 69 | public long longValue() { 70 | return (long) this.value; 71 | } 72 | 73 | @Override 74 | public short shortValue() { 75 | return this.value; 76 | } 77 | 78 | @Override 79 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 80 | this.value = input.readShort(); 81 | } 82 | 83 | @Override 84 | public void write(final @NonNull DataOutput output) throws IOException { 85 | output.writeShort(this.value); 86 | } 87 | 88 | @Override 89 | public @NonNull TagType type() { 90 | return TagType.SHORT; 91 | } 92 | 93 | @Override 94 | public @NonNull ShortTag copy() { 95 | return new ShortTag(this.value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Short.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof ShortTag && this.value == ((ShortTag) that).value); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/LongTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing a {@code long}. 34 | */ 35 | public final class LongTag implements NumberTag { 36 | /** 37 | * The long value. 38 | */ 39 | private long value; 40 | 41 | LongTag() { 42 | } 43 | 44 | public LongTag(final long value) { 45 | this.value = value; 46 | } 47 | 48 | @Override 49 | public byte byteValue() { 50 | return (byte) (this.value & 0xff); 51 | } 52 | 53 | @Override 54 | public double doubleValue() { 55 | return (double) this.value; 56 | } 57 | 58 | @Override 59 | public float floatValue() { 60 | return (float) this.value; 61 | } 62 | 63 | @Override 64 | public int intValue() { 65 | return Math.toIntExact(this.value); 66 | } 67 | 68 | @Override 69 | public long longValue() { 70 | return this.value; 71 | } 72 | 73 | @Override 74 | public short shortValue() { 75 | return (short) (this.value & 0xffff); 76 | } 77 | 78 | @Override 79 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 80 | this.value = input.readLong(); 81 | } 82 | 83 | @Override 84 | public void write(final @NonNull DataOutput output) throws IOException { 85 | output.writeLong(this.value); 86 | } 87 | 88 | @Override 89 | public @NonNull TagType type() { 90 | return TagType.LONG; 91 | } 92 | 93 | @Override 94 | public @NonNull LongTag copy() { 95 | return new LongTag(this.value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Long.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof LongTag && this.value == ((LongTag) that).value); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/FloatTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing a {@code float}. 34 | */ 35 | public final class FloatTag implements NumberTag { 36 | /** 37 | * The float value. 38 | */ 39 | private float value; 40 | 41 | FloatTag() { 42 | } 43 | 44 | public FloatTag(final float value) { 45 | this.value = value; 46 | } 47 | 48 | @Override 49 | public byte byteValue() { 50 | return (byte) (NumberTag.floor(this.value) & 0xff); 51 | } 52 | 53 | @Override 54 | public double doubleValue() { 55 | return (double) this.value; 56 | } 57 | 58 | @Override 59 | public float floatValue() { 60 | return this.value; 61 | } 62 | 63 | @Override 64 | public int intValue() { 65 | return NumberTag.floor(this.value); 66 | } 67 | 68 | @Override 69 | public long longValue() { 70 | return (long) this.value; 71 | } 72 | 73 | @Override 74 | public short shortValue() { 75 | return (short) (NumberTag.floor(this.value) & 0xffff); 76 | } 77 | 78 | @Override 79 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 80 | this.value = input.readFloat(); 81 | } 82 | 83 | @Override 84 | public void write(final @NonNull DataOutput output) throws IOException { 85 | output.writeFloat(this.value); 86 | } 87 | 88 | @Override 89 | public @NonNull TagType type() { 90 | return TagType.FLOAT; 91 | } 92 | 93 | @Override 94 | public @NonNull FloatTag copy() { 95 | return new FloatTag(this.value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Float.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof FloatTag && Float.floatToIntBits(this.value) == Float.floatToIntBits(((FloatTag) that).value)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/DoubleTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing a {@code double}. 34 | */ 35 | public final class DoubleTag implements NumberTag { 36 | /** 37 | * The double value. 38 | */ 39 | private double value; 40 | 41 | DoubleTag() { 42 | } 43 | 44 | public DoubleTag(final double value) { 45 | this.value = value; 46 | } 47 | 48 | @Override 49 | public byte byteValue() { 50 | return (byte) (NumberTag.floor(this.value) & 0xff); 51 | } 52 | 53 | @Override 54 | public double doubleValue() { 55 | return this.value; 56 | } 57 | 58 | @Override 59 | public float floatValue() { 60 | return (float) this.value; 61 | } 62 | 63 | @Override 64 | public int intValue() { 65 | return NumberTag.floor(this.value); 66 | } 67 | 68 | @Override 69 | public long longValue() { 70 | return (long) Math.floor(this.value); 71 | } 72 | 73 | @Override 74 | public short shortValue() { 75 | return (short) (NumberTag.floor(this.value) & 0xffff); 76 | } 77 | 78 | @Override 79 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 80 | this.value = input.readDouble(); 81 | } 82 | 83 | @Override 84 | public void write(final @NonNull DataOutput output) throws IOException { 85 | output.writeDouble(this.value); 86 | } 87 | 88 | @Override 89 | public @NonNull TagType type() { 90 | return TagType.DOUBLE; 91 | } 92 | 93 | @Override 94 | public @NonNull DoubleTag copy() { 95 | return new DoubleTag(this.value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Double.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof DoubleTag && Double.doubleToLongBits(this.value) == Double.doubleToLongBits(((DoubleTag) that).value)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/ByteArrayTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | import java.util.AbstractList; 32 | import java.util.Arrays; 33 | 34 | /** 35 | * A tag representing an array of {@code byte}s. 36 | */ 37 | public final class ByteArrayTag extends AbstractList implements IndexedCollectionTag { 38 | /** 39 | * The array of bytes. 40 | */ 41 | private byte[] value; 42 | 43 | ByteArrayTag() { 44 | } 45 | 46 | public ByteArrayTag(final byte@NonNull[] value) { 47 | this.value = value; 48 | } 49 | 50 | @Override 51 | public int size() { 52 | return this.value.length; 53 | } 54 | 55 | @Override 56 | public ByteTag get(final int index) { 57 | return new ByteTag(this.value[index]); 58 | } 59 | 60 | /** 61 | * Gets the array of bytes. 62 | * 63 | * @return the array of bytes 64 | */ 65 | public byte@NonNull[] value() { 66 | return this.value; 67 | } 68 | 69 | @Override 70 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 71 | final int length = input.readInt(); 72 | this.value = new byte[length]; 73 | input.readFully(this.value); 74 | } 75 | 76 | @Override 77 | public void write(final @NonNull DataOutput output) throws IOException { 78 | output.writeInt(this.value.length); 79 | output.write(this.value); 80 | } 81 | 82 | @Override 83 | public @NonNull TagType type() { 84 | return TagType.BYTE_ARRAY; 85 | } 86 | 87 | @Override 88 | public @NonNull ByteArrayTag copy() { 89 | final byte[] value = new byte[this.value.length]; 90 | System.arraycopy(this.value, 0, value, 0, this.value.length); 91 | return new ByteArrayTag(value); 92 | } 93 | 94 | @Override 95 | public int hashCode() { 96 | return Arrays.hashCode(this.value); 97 | } 98 | 99 | @Override 100 | public boolean equals(final Object that) { 101 | return this == that || (that instanceof ByteArrayTag && Arrays.equals(this.value, ((ByteArrayTag) that).value)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/ByteTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | 32 | /** 33 | * A tag representing a {@code byte}. 34 | */ 35 | public final class ByteTag implements NumberTag { 36 | /** 37 | * A {@code byte} representing a {@code boolean} value of {@code false}. 38 | */ 39 | static final byte FALSE = 0; 40 | /** 41 | * A {@code byte} representing a {@code boolean} value of {@code true}. 42 | */ 43 | static final byte TRUE = 1; 44 | /** 45 | * The byte value. 46 | */ 47 | private byte value; 48 | 49 | ByteTag() { 50 | } 51 | 52 | public ByteTag(final byte value) { 53 | this.value = value; 54 | } 55 | 56 | @Override 57 | public byte byteValue() { 58 | return this.value; 59 | } 60 | 61 | @Override 62 | public double doubleValue() { 63 | return (double) this.value; 64 | } 65 | 66 | @Override 67 | public float floatValue() { 68 | return (float) this.value; 69 | } 70 | 71 | @Override 72 | public int intValue() { 73 | return (int) this.value; 74 | } 75 | 76 | @Override 77 | public long longValue() { 78 | return (long) this.value; 79 | } 80 | 81 | @Override 82 | public short shortValue() { 83 | return (short) this.value; 84 | } 85 | 86 | @Override 87 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 88 | this.value = input.readByte(); 89 | } 90 | 91 | @Override 92 | public void write(final @NonNull DataOutput output) throws IOException { 93 | output.writeByte(this.value); 94 | } 95 | 96 | @Override 97 | public @NonNull TagType type() { 98 | return TagType.BYTE; 99 | } 100 | 101 | @Override 102 | public @NonNull ByteTag copy() { 103 | return new ByteTag(this.value); 104 | } 105 | 106 | @Override 107 | public int hashCode() { 108 | return Byte.hashCode(this.value); 109 | } 110 | 111 | @Override 112 | public boolean equals(final Object that) { 113 | return this == that || (that instanceof ByteTag && this.value == ((ByteTag) that).value); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/IntArrayTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | import java.util.AbstractList; 32 | import java.util.Arrays; 33 | 34 | /** 35 | * A tag representing an array of {@code int}s. 36 | */ 37 | public final class IntArrayTag extends AbstractList implements IndexedCollectionTag { 38 | /** 39 | * The array of ints. 40 | */ 41 | private int[] value; 42 | 43 | IntArrayTag() { 44 | } 45 | 46 | public IntArrayTag(final int@NonNull[] value) { 47 | this.value = value; 48 | } 49 | 50 | @Override 51 | public int size() { 52 | return this.value.length; 53 | } 54 | 55 | @Override 56 | public IntTag get(final int index) { 57 | return new IntTag(this.value[index]); 58 | } 59 | 60 | /** 61 | * Gets the array of ints. 62 | * 63 | * @return the array of ints 64 | */ 65 | public int@NonNull[] value() { 66 | return this.value; 67 | } 68 | 69 | @Override 70 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 71 | final int length = input.readInt(); 72 | this.value = new int[length]; 73 | for(int i = 0; i < length; i++) { 74 | this.value[i] = input.readInt(); 75 | } 76 | } 77 | 78 | @Override 79 | public void write(final @NonNull DataOutput output) throws IOException { 80 | output.writeInt(this.value.length); 81 | for(int i = 0, length = this.value.length; i < length; i++) { 82 | output.writeInt(this.value[i]); 83 | } 84 | } 85 | 86 | @Override 87 | public @NonNull TagType type() { 88 | return TagType.INT_ARRAY; 89 | } 90 | 91 | @Override 92 | public @NonNull IntArrayTag copy() { 93 | final int[] value = new int[this.value.length]; 94 | System.arraycopy(this.value, 0, value, 0, this.value.length); 95 | return new IntArrayTag(value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Arrays.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof IntArrayTag && Arrays.equals(this.value, ((IntArrayTag) that).value)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/LongArrayTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataOutput; 30 | import java.io.IOException; 31 | import java.util.AbstractList; 32 | import java.util.Arrays; 33 | 34 | /** 35 | * A tag representing an array of {@code long}s. 36 | */ 37 | public final class LongArrayTag extends AbstractList implements IndexedCollectionTag { 38 | /** 39 | * The array of longs. 40 | */ 41 | private long[] value; 42 | 43 | LongArrayTag() { 44 | } 45 | 46 | public LongArrayTag(final long@NonNull[] value) { 47 | this.value = value; 48 | } 49 | 50 | @Override 51 | public int size() { 52 | return this.value.length; 53 | } 54 | 55 | @Override 56 | public LongTag get(final int index) { 57 | return new LongTag(this.value[index]); 58 | } 59 | 60 | /** 61 | * Gets the array of longs. 62 | * 63 | * @return the array of longs 64 | */ 65 | public long@NonNull[] value() { 66 | return this.value; 67 | } 68 | 69 | @Override 70 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 71 | final int length = input.readInt(); 72 | this.value = new long[length]; 73 | for(int i = 0; i < length; i++) { 74 | this.value[i] = input.readLong(); 75 | } 76 | } 77 | 78 | @Override 79 | public void write(final @NonNull DataOutput output) throws IOException { 80 | output.writeInt(this.value.length); 81 | for(int i = 0, length = this.value.length; i < length; i++) { 82 | output.writeLong(this.value[i]); 83 | } 84 | } 85 | 86 | @Override 87 | public @NonNull TagType type() { 88 | return TagType.LONG_ARRAY; 89 | } 90 | 91 | @Override 92 | public @NonNull LongArrayTag copy() { 93 | final long[] value = new long[this.value.length]; 94 | System.arraycopy(this.value, 0, value, 0, this.value.length); 95 | return new LongArrayTag(value); 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Arrays.hashCode(this.value); 101 | } 102 | 103 | @Override 104 | public boolean equals(final Object that) { 105 | return this == that || (that instanceof LongArrayTag && Arrays.equals(this.value, ((LongArrayTag) that).value)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/TagType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.util.function.Predicate; 29 | import java.util.function.Supplier; 30 | 31 | /** 32 | * An enumeration of tag types. 33 | */ 34 | public enum TagType implements Predicate { 35 | /** 36 | * @see EndTag 37 | */ 38 | END((byte) 0, EndTag::new), 39 | /** 40 | * @see ByteTag 41 | */ 42 | BYTE((byte) 1, true, ByteTag::new), 43 | /** 44 | * @see ShortTag 45 | */ 46 | SHORT((byte) 2, true, ShortTag::new), 47 | /** 48 | * @see IntTag 49 | */ 50 | INT((byte) 3, true, IntTag::new), 51 | /** 52 | * @see LongTag 53 | */ 54 | LONG((byte) 4, true, LongTag::new), 55 | /** 56 | * @see FloatTag 57 | */ 58 | FLOAT((byte) 5, true, FloatTag::new), 59 | /** 60 | * @see DoubleTag 61 | */ 62 | DOUBLE((byte) 6, true, DoubleTag::new), 63 | /** 64 | * @see ByteArrayTag 65 | */ 66 | BYTE_ARRAY((byte) 7, ByteArrayTag::new), 67 | /** 68 | * @see StringTag 69 | */ 70 | STRING((byte) 8, StringTag::new), 71 | /** 72 | * @see ListTag 73 | */ 74 | LIST((byte) 9, ListTag::new), 75 | /** 76 | * @see CompoundTag 77 | */ 78 | COMPOUND((byte) 10, CompoundTag::new), 79 | /** 80 | * @see IntArrayTag 81 | */ 82 | INT_ARRAY((byte) 11, IntArrayTag::new), 83 | /** 84 | * @see LongArrayTag 85 | */ 86 | LONG_ARRAY((byte) 12, LongArrayTag::new); 87 | 88 | private static final TagType[] TYPES = values(); 89 | /** 90 | * The byte id of this tag type. 91 | */ 92 | private final byte id; 93 | /** 94 | * If this tag type is a {@link NumberTag number} type. 95 | */ 96 | private final boolean number; 97 | /** 98 | * The tag factory. 99 | */ 100 | private final @NonNull Supplier factory; 101 | 102 | TagType(final byte id, final @NonNull Supplier factory) { 103 | this(id, false, factory); 104 | } 105 | 106 | TagType(final byte id, final boolean number, final @NonNull Supplier factory) { 107 | this.id = id; 108 | this.number = number; 109 | this.factory = factory; 110 | } 111 | 112 | /** 113 | * Gets the byte id of this tag type. 114 | * 115 | * @return the byte id 116 | */ 117 | public byte id() { 118 | return this.id; 119 | } 120 | 121 | /** 122 | * Checks if this tag type is a {@link NumberTag number} type. 123 | * 124 | * @return {@code true} if a number type, {@code false} if not 125 | */ 126 | public boolean number() { 127 | return this.number; 128 | } 129 | 130 | /** 131 | * Creates a new tag. 132 | * 133 | * @return a new tag 134 | */ 135 | @NonNull Tag create() { 136 | return this.factory.get(); 137 | } 138 | 139 | @Override 140 | public boolean test(final @NonNull TagType that) { 141 | return this == that || (this.number && that.number); 142 | } 143 | 144 | /** 145 | * Gets the tag type for the specified id. 146 | * 147 | * @param id the id 148 | * @return the tag type 149 | * @throws ArrayIndexOutOfBoundsException if the id is not without bounds 150 | */ 151 | static @NonNull TagType of(final byte id) { 152 | return TYPES[id]; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/test/java/net/kyori/nbt/BigTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.junit.jupiter.api.BeforeAll; 27 | import org.junit.jupiter.api.Test; 28 | 29 | import java.io.IOException; 30 | import java.net.URISyntaxException; 31 | import java.net.URL; 32 | import java.nio.file.Paths; 33 | 34 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 35 | import static org.junit.jupiter.api.Assertions.assertEquals; 36 | 37 | /** 38 | * Ensure that we can read the bigtest.nbt file. 39 | */ 40 | class BigTest { 41 | private static final double DOUBLE_DELTA = 1e-15; 42 | private static final byte[] BYTE_ARRAY_TEST = new byte[1000]; 43 | private static final ListTag LONG_LIST = new ListTag(); 44 | private static final ListTag COMPOUND_LIST = new ListTag(); 45 | private static final CompoundTag NESTED_COMPOUND = new CompoundTag(); 46 | private static CompoundTag compound; 47 | 48 | static { 49 | for(int i = 0; i < 1000; i++) { 50 | BYTE_ARRAY_TEST[i] = (byte) ((i * i * 255 + i * 7) % 100); 51 | } 52 | 53 | LONG_LIST.add(new LongTag(11)); 54 | LONG_LIST.add(new LongTag(12)); 55 | LONG_LIST.add(new LongTag(13)); 56 | LONG_LIST.add(new LongTag(14)); 57 | LONG_LIST.add(new LongTag(15)); 58 | 59 | final CompoundTag listTestCompoundTag0 = new CompoundTag(); 60 | listTestCompoundTag0.putLong("created-on", 1264099775885L); 61 | listTestCompoundTag0.putString("name", "Compound tag #0"); 62 | final CompoundTag listTestCompoundTag1 = new CompoundTag(); 63 | listTestCompoundTag1.putLong("created-on", 1264099775885L); 64 | listTestCompoundTag1.putString("name", "Compound tag #1"); 65 | 66 | COMPOUND_LIST.add(listTestCompoundTag0); 67 | COMPOUND_LIST.add(listTestCompoundTag1); 68 | 69 | final CompoundTag egg = new CompoundTag(); 70 | egg.putString("name", "Eggbert"); 71 | egg.putFloat("value", 0.5f); 72 | final CompoundTag ham = new CompoundTag(); 73 | ham.putString("name", "Hampus"); 74 | ham.putFloat("value", 0.75f); 75 | 76 | NESTED_COMPOUND.put("egg", egg); 77 | NESTED_COMPOUND.put("ham", ham); 78 | } 79 | 80 | @BeforeAll 81 | static void before() throws IOException, URISyntaxException { 82 | final URL url = BigTest.class.getResource("/bigtest.nbt"); 83 | compound = TagIO.readCompressedPath(Paths.get(url.toURI())); 84 | } 85 | 86 | @Test 87 | void testCorrectValues() { 88 | assertEquals(Short.MAX_VALUE, compound.getShort("shortTest")); 89 | assertEquals(Long.MAX_VALUE, compound.getLong("longTest")); 90 | assertEquals(Byte.MAX_VALUE, compound.getByte("byteTest")); 91 | assertArrayEquals(BYTE_ARRAY_TEST, compound.getByteArray("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))")); 92 | assertEquals(LONG_LIST, compound.getList("listTest (long)")); 93 | assertEquals(0.49823147f, compound.getFloat("floatTest"), DOUBLE_DELTA); 94 | assertEquals(0.4931287132182315d, compound.getDouble("doubleTest"), DOUBLE_DELTA); 95 | assertEquals(Integer.MAX_VALUE, compound.getInt("intTest")); 96 | assertEquals(COMPOUND_LIST, compound.getList("listTest (compound)")); 97 | assertEquals(NESTED_COMPOUND, compound.getCompound("nested compound test")); 98 | assertEquals("HELLO WORLD THIS IS A TEST STRING ÅÄÖ!", compound.getString("stringTest")); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/test/java/net/kyori/nbt/ReadWriteTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import com.google.common.io.ByteArrayDataInput; 27 | import com.google.common.io.ByteArrayDataOutput; 28 | import com.google.common.io.ByteStreams; 29 | import org.junit.jupiter.api.Test; 30 | 31 | import java.io.IOException; 32 | 33 | import static org.junit.jupiter.api.Assertions.assertEquals; 34 | 35 | class ReadWriteTest { 36 | @Test 37 | void testByteArray() throws IOException { 38 | this.testWriteRead(new ByteArrayTag(new byte[]{Byte.MIN_VALUE, -100, 0, 100, Byte.MAX_VALUE}), new ByteArrayTag()); 39 | } 40 | 41 | @Test 42 | void testByte() throws IOException { 43 | this.testWriteRead(new ByteTag((byte) 2), new ByteTag()); 44 | } 45 | 46 | @Test 47 | void testCompound() throws IOException { 48 | final CompoundTag a = new CompoundTag(); 49 | { 50 | a.putByte("AByte", (byte) 0); 51 | a.putInt("AnInt", 1); 52 | a.putIntArray("AnIntArray", new int[]{0, 1, 4, 5, 8, 9}); 53 | } 54 | final CompoundTag b = new CompoundTag(); 55 | this.testWriteRead(a, b); 56 | assertEquals(a.type(), b.type()); 57 | assertEquals(a, b); 58 | } 59 | 60 | @Test 61 | void testDouble() throws IOException { 62 | this.testWriteRead(new DoubleTag(4d), new DoubleTag()); 63 | } 64 | 65 | @Test 66 | void testEnd() throws IOException { 67 | this.testWriteRead(new EndTag(), new EndTag()); 68 | } 69 | 70 | @Test 71 | void testFloat() throws IOException { 72 | this.testWriteRead(new FloatTag(6f), new FloatTag()); 73 | } 74 | 75 | @Test 76 | void testIntArray() throws IOException { 77 | this.testWriteRead(new IntArrayTag(new int[]{Integer.MIN_VALUE, -100, 0, 100, Integer.MAX_VALUE}), new IntArrayTag()); 78 | } 79 | 80 | @Test 81 | void testInt() throws IOException { 82 | this.testWriteRead(new IntTag(8), new IntTag()); 83 | } 84 | 85 | @Test 86 | void testList() throws IOException { 87 | final ListTag a = new ListTag(TagType.DOUBLE); 88 | { 89 | a.add(new DoubleTag(32d)); 90 | a.add(new DoubleTag(64d)); 91 | } 92 | final ListTag b = new ListTag(); 93 | this.testWriteRead(a, b); 94 | assertEquals(a.type(), b.type()); 95 | assertEquals(a, b); 96 | } 97 | 98 | @Test 99 | void testLongArray() throws IOException { 100 | this.testWriteRead(new LongArrayTag(new long[]{Long.MIN_VALUE, -100, 0, 100, Long.MAX_VALUE}), new LongArrayTag()); 101 | } 102 | 103 | @Test 104 | void testLong() throws IOException { 105 | this.testWriteRead(new LongTag(10), new LongTag()); 106 | } 107 | 108 | @Test 109 | void testShort() throws IOException { 110 | this.testWriteRead(new ShortTag((short) 12), new ShortTag()); 111 | } 112 | 113 | @Test 114 | void testString() throws IOException { 115 | final StringTag a = new StringTag("Hello, world!"); 116 | final StringTag b = new StringTag(); 117 | this.writeRead(a, b); 118 | assertEquals(a, b); 119 | } 120 | 121 | private void testWriteRead(final T a, final T b) throws IOException { 122 | this.writeRead(a, b); 123 | assertEquals(a, b); 124 | } 125 | 126 | @SuppressWarnings("UnstableApiUsage") 127 | private void writeRead(final Tag a, final Tag b) throws IOException { 128 | final ByteArrayDataOutput output = ByteStreams.newDataOutput(); 129 | a.write(output); 130 | final ByteArrayDataInput input = ByteStreams.newDataInput(output.toByteArray()); 131 | b.read(input, 0); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/TagIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | 28 | import java.io.DataInput; 29 | import java.io.DataInputStream; 30 | import java.io.DataOutput; 31 | import java.io.DataOutputStream; 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.io.OutputStream; 35 | import java.nio.file.Files; 36 | import java.nio.file.Path; 37 | import java.util.zip.GZIPInputStream; 38 | import java.util.zip.GZIPOutputStream; 39 | 40 | public final class TagIO { 41 | private TagIO() { 42 | } 43 | 44 | /** 45 | * Reads a compound tag from {@code path}. 46 | * 47 | * @param path the path 48 | * @return the compound tag 49 | * @throws IOException if an exception was encountered while reading a compound tag 50 | */ 51 | public static @NonNull CompoundTag readPath(final @NonNull Path path) throws IOException { 52 | return readInputStream(Files.newInputStream(path)); 53 | } 54 | 55 | /** 56 | * Reads a compound tag from an input stream. 57 | * 58 | * @param input the input stream 59 | * @return the compound tag 60 | * @throws IOException if an exception was encountered while reading a compound tag 61 | */ 62 | public static @NonNull CompoundTag readInputStream(final @NonNull InputStream input) throws IOException { 63 | try(final DataInputStream dis = new DataInputStream(input)) { 64 | return readDataInput(dis); 65 | } 66 | } 67 | 68 | /** 69 | * Reads a compound tag from {@code path} using GZIP decompression. 70 | * 71 | * @param path the path 72 | * @return the compound tag 73 | * @throws IOException if an exception was encountered while reading a compound tag 74 | */ 75 | public static @NonNull CompoundTag readCompressedPath(final @NonNull Path path) throws IOException { 76 | return readCompressedInputStream(Files.newInputStream(path)); 77 | } 78 | 79 | /** 80 | * Reads a compound tag from an input stream using GZIP decompression. 81 | * 82 | * @param input the input stream 83 | * @return the compound tag 84 | * @throws IOException if an exception was encountered while reading a compound tag 85 | */ 86 | public static @NonNull CompoundTag readCompressedInputStream(final @NonNull InputStream input) throws IOException { 87 | try(final DataInputStream dis = new DataInputStream(new GZIPInputStream(input))) { 88 | return readDataInput(dis); 89 | } 90 | } 91 | 92 | /** 93 | * Reads a compound tag from {@code input}. 94 | * 95 | * @param input the input 96 | * @return the compound tag 97 | * @throws IOException if an exception was encountered while reading a compound tag 98 | */ 99 | public static @NonNull CompoundTag readDataInput(final @NonNull DataInput input) throws IOException { 100 | final TagType type = TagType.of(input.readByte()); 101 | if(type != TagType.COMPOUND) { 102 | throw new IOException(String.format("Expected root tag to be a %s, was %s", TagType.COMPOUND, type)); 103 | } 104 | input.skipBytes(input.readUnsignedShort()); // read empty name 105 | final Tag tag = type.create(); 106 | tag.read(input, 0); // initial depth is zero 107 | return (CompoundTag) tag; 108 | } 109 | 110 | /** 111 | * Writes a compound tag to {@code path}. 112 | * 113 | * @param tag the compound tag 114 | * @param path the path 115 | * @throws IOException if an exception was encountered while writing the compound tag 116 | */ 117 | public static void writePath(final @NonNull CompoundTag tag, final @NonNull Path path) throws IOException { 118 | writeOutputStream(tag, Files.newOutputStream(path)); 119 | } 120 | 121 | /** 122 | * Writes a compound tag to an output stream. 123 | * 124 | * @param tag the compound tag 125 | * @param output the output stream 126 | * @throws IOException if an exception was encountered while writing the compound tag 127 | */ 128 | public static void writeOutputStream(final @NonNull CompoundTag tag, final @NonNull OutputStream output) throws IOException { 129 | try(final DataOutputStream dos = new DataOutputStream(output)) { 130 | writeDataOutput(tag, dos); 131 | } 132 | } 133 | 134 | /** 135 | * Writes a compound tag to {@code path} using GZIP compression. 136 | * 137 | * @param tag the compound tag 138 | * @param path the path 139 | * @throws IOException if an exception was encountered while writing the compound tag 140 | */ 141 | public static void writeCompressedPath(final @NonNull CompoundTag tag, final @NonNull Path path) throws IOException { 142 | writeCompressedOutputStream(tag, Files.newOutputStream(path)); 143 | } 144 | 145 | /** 146 | * Writes a compound tag to an output stream using GZIP compression. 147 | * 148 | * @param tag the compound tag 149 | * @param output the output stream 150 | * @throws IOException if an exception was encountered while writing the compound tag 151 | */ 152 | public static void writeCompressedOutputStream(final @NonNull CompoundTag tag, final @NonNull OutputStream output) throws IOException { 153 | try(final DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(output))) { 154 | writeDataOutput(tag, dos); 155 | } 156 | } 157 | 158 | /** 159 | * Writes a compound tag to {@code output}. 160 | * 161 | * @param tag the compound tag 162 | * @param output the output 163 | * @throws IOException if an exception was encountered while writing the compound tag 164 | */ 165 | public static void writeDataOutput(final @NonNull CompoundTag tag, final @NonNull DataOutput output) throws IOException { 166 | output.writeByte(tag.type().id()); 167 | if(tag.type() != TagType.END) { 168 | output.writeUTF(""); // write empty name 169 | tag.write(output); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/display/StringTagDisplay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt.display; 25 | 26 | import net.kyori.nbt.ByteArrayTag; 27 | import net.kyori.nbt.ByteTag; 28 | import net.kyori.nbt.CompoundTag; 29 | import net.kyori.nbt.DoubleTag; 30 | import net.kyori.nbt.EndTag; 31 | import net.kyori.nbt.FloatTag; 32 | import net.kyori.nbt.IntArrayTag; 33 | import net.kyori.nbt.IntTag; 34 | import net.kyori.nbt.ListTag; 35 | import net.kyori.nbt.LongArrayTag; 36 | import net.kyori.nbt.LongTag; 37 | import net.kyori.nbt.ShortTag; 38 | import net.kyori.nbt.StringTag; 39 | import net.kyori.nbt.Tag; 40 | import org.checkerframework.checker.nullness.qual.NonNull; 41 | import org.checkerframework.checker.nullness.qual.Nullable; 42 | 43 | import java.util.Collections; 44 | import java.util.Iterator; 45 | import java.util.regex.Pattern; 46 | 47 | /** 48 | * A tag displayer that renders a {@link Tag} into a {@link String}. 49 | */ 50 | public class StringTagDisplay implements TagDisplay { 51 | private static final Pattern SIMPLE_VALUE = Pattern.compile("[a-zA-Z0-9._+-]+"); 52 | 53 | @Override 54 | public @NonNull String render(final @NonNull Tag tag) { 55 | final StringBuilder sb = new StringBuilder(); 56 | this.render(tag, sb); 57 | return sb.toString(); 58 | } 59 | 60 | public void render(final @NonNull Tag tag, final @NonNull StringBuilder sb) { 61 | this.render(null, tag, sb, 0); 62 | } 63 | 64 | private void render(final @Nullable String name, final Tag tag, final StringBuilder sb, final int depth) { 65 | if(tag instanceof CompoundTag) { 66 | this.indent(sb, depth); 67 | 68 | if(name != null) { 69 | sb.append(name).append(' '); 70 | } 71 | 72 | sb.append('{'); 73 | sb.append('\n'); 74 | 75 | for(final Iterator iterator = ((CompoundTag) tag).keySet().iterator(); iterator.hasNext(); ) { 76 | final String key = iterator.next(); 77 | final Tag value = ((CompoundTag) tag).get(key); 78 | this.render(key, value, sb, depth + 1); 79 | if(iterator.hasNext()) { 80 | sb.append(',').append('\n'); 81 | } 82 | } 83 | 84 | sb.append('\n'); 85 | this.indent(sb, depth); 86 | sb.append('}'); 87 | } else if(tag instanceof ListTag) { 88 | this.indent(sb, depth); 89 | 90 | if(name != null) { 91 | sb.append(name).append(' '); 92 | } 93 | 94 | sb.append('['); 95 | 96 | if(!((ListTag) tag).isEmpty()) { 97 | sb.append('\n'); 98 | 99 | for(int i = 0, size = ((ListTag) tag).size(); i < size; i++) { 100 | final Tag child = ((ListTag) tag).get(i); 101 | this.render(null, child, sb, depth + 1); 102 | if(i + 1 < size) { 103 | sb.append(',').append('\n'); 104 | } 105 | } 106 | 107 | sb.append('\n'); 108 | this.indent(sb, depth); 109 | } 110 | 111 | sb.append(']'); 112 | } else { 113 | this.indent(sb, depth); 114 | 115 | if(name != null) { 116 | sb.append(name).append('='); 117 | } 118 | 119 | sb.append(toString(tag)); 120 | } 121 | } 122 | 123 | private void indent(final StringBuilder sb, final int depth) { 124 | sb.append(String.join("", Collections.nCopies(depth, " "))); 125 | } 126 | 127 | private static String toString(final Tag tag) { 128 | if(tag instanceof ByteArrayTag) { 129 | final StringBuilder sb = new StringBuilder("[B;"); 130 | for(int i = 0, length = ((ByteArrayTag) tag).value().length; i < length; ++i) { 131 | if(i != 0) { 132 | sb.append(','); 133 | } 134 | sb.append(((ByteArrayTag) tag).value()[i]).append('B'); 135 | } 136 | return sb.append(']').toString(); 137 | } else if(tag instanceof ByteTag) { 138 | return String.valueOf(((ByteTag) tag).byteValue()); 139 | } else if(tag instanceof CompoundTag) { 140 | final StringBuilder sb = new StringBuilder("{"); 141 | for(final String key : ((CompoundTag) tag).keySet()) { 142 | if(sb.length() != 1) { 143 | sb.append(','); 144 | } 145 | sb.append(renderKey(key)).append(':').append(((CompoundTag) tag).get(key)); 146 | } 147 | return sb.append('}').toString(); 148 | } else if(tag instanceof DoubleTag) { 149 | return ((DoubleTag) tag).doubleValue() + "d"; 150 | } else if(tag instanceof EndTag) { 151 | return ""; 152 | } else if(tag instanceof FloatTag) { 153 | return ((FloatTag) tag).floatValue() + "f"; 154 | } else if(tag instanceof IntArrayTag) { 155 | final StringBuilder sb = new StringBuilder("[I;"); 156 | for(int i = 0; i < ((IntArrayTag) tag).value().length; ++i) { 157 | if(i != 0) { 158 | sb.append(','); 159 | } 160 | sb.append(((IntArrayTag) tag).value()[i]); 161 | } 162 | return sb.append(']').toString(); 163 | } else if(tag instanceof IntTag) { 164 | return String.valueOf(((IntTag) tag).intValue()); 165 | } else if(tag instanceof ListTag) { 166 | final StringBuilder sb = new StringBuilder("["); 167 | for(int i = 0; i < ((ListTag) tag).size(); ++i) { 168 | if(i != 0) { 169 | sb.append(','); 170 | } 171 | sb.append(((ListTag) tag).get(i)); 172 | } 173 | return sb.append(']').toString(); 174 | } else if(tag instanceof LongArrayTag) { 175 | final StringBuilder sb = new StringBuilder("[L;"); 176 | for(int i = 0; i < ((LongArrayTag) tag).value().length; ++i) { 177 | if(i != 0) { 178 | sb.append(','); 179 | } 180 | sb.append(((LongArrayTag) tag).value()[i]).append('L'); 181 | } 182 | return sb.append(']').toString(); 183 | } else if(tag instanceof LongTag) { 184 | return ((LongTag) tag).longValue() + "L"; 185 | } else if(tag instanceof ShortTag) { 186 | return ((ShortTag) tag).shortValue() + "s"; 187 | } else if(tag instanceof StringTag) { 188 | return renderQuotedString(((StringTag) tag).value()); 189 | } 190 | throw new IllegalArgumentException(tag.getClass().getName()); 191 | } 192 | 193 | private static String renderKey(final String string) { 194 | if(SIMPLE_VALUE.matcher(string).matches()) { 195 | return string; 196 | } 197 | return renderQuotedString(string); 198 | } 199 | 200 | private static String renderQuotedString(final String string) { 201 | final StringBuilder sb = new StringBuilder(); 202 | sb.append('"'); 203 | for(int i = 0; i < string.length(); ++i) { 204 | final char c = string.charAt(i); 205 | if(c == '\\' || c == '"') { 206 | sb.append('\\'); 207 | } 208 | sb.append(c); 209 | } 210 | sb.append('"'); 211 | return sb.toString(); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/ListTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.index.qual.NonNegative; 27 | import org.checkerframework.checker.nullness.qual.NonNull; 28 | 29 | import java.io.DataInput; 30 | import java.io.DataOutput; 31 | import java.io.IOException; 32 | import java.util.AbstractList; 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | 36 | import static java.util.Objects.requireNonNull; 37 | 38 | /** 39 | * A list tag. 40 | */ 41 | public final class ListTag extends AbstractList implements IndexedCollectionTag { 42 | /** 43 | * The maximum depth. 44 | */ 45 | public static final int MAX_DEPTH = 512; 46 | /** 47 | * The list of tags. 48 | */ 49 | private final List tags = new ArrayList<>(); 50 | /** 51 | * The type of this list. 52 | */ 53 | private @NonNull TagType type; 54 | 55 | public ListTag() { 56 | this(TagType.END); 57 | } 58 | 59 | public ListTag(final @NonNull TagType type) { 60 | this.type = type; 61 | } 62 | 63 | /** 64 | * Creates a list tag with some double values. 65 | * 66 | * @param values the double values 67 | * @return the list tag 68 | */ 69 | public static ListTag doubles(final double... values) { 70 | final ListTag tag = new ListTag(); 71 | for(final double value : values) { 72 | tag.add(new DoubleTag(value)); 73 | } 74 | return tag; 75 | } 76 | 77 | /** 78 | * Creates a list tag with some float values. 79 | * 80 | * @param values the float values 81 | * @return the list tag 82 | */ 83 | public static ListTag floats(final float... values) { 84 | final ListTag tag = new ListTag(); 85 | for(final float value : values) { 86 | tag.add(new FloatTag(value)); 87 | } 88 | return tag; 89 | } 90 | 91 | /** 92 | * Creates a list tag with some string values. 93 | * 94 | * @param values the string values 95 | * @return the list tag 96 | */ 97 | public static ListTag strings(final String... values) { 98 | final ListTag tag = new ListTag(); 99 | for(int i = 0, length = values.length; i < length; i++) { 100 | tag.add(new StringTag(requireNonNull(values[i], "value at index " + i))); 101 | } 102 | return tag; 103 | } 104 | 105 | /** 106 | * Gets the type of this list. 107 | * 108 | * @return the type 109 | */ 110 | public @NonNull TagType listType() { 111 | return this.type; 112 | } 113 | 114 | /** 115 | * Gets a tag. 116 | * 117 | * @param index the index 118 | * @return the tag 119 | * @throws IndexOutOfBoundsException if the index is out of range 120 | */ 121 | @Override 122 | public @NonNull Tag get(final @NonNegative int index) { 123 | return this.tags.get(index); 124 | } 125 | 126 | /** 127 | * Gets a byte. 128 | * 129 | * @param index the index 130 | * @return the byte value, or {@code 0} 131 | */ 132 | public byte getByte(final @NonNegative int index) { 133 | return this.getByte(index, (byte) 0); 134 | } 135 | 136 | /** 137 | * Gets a byte. 138 | * 139 | * @param index the index 140 | * @param defaultValue the default value 141 | * @return the byte value, or {@code defaultValue} 142 | */ 143 | public byte getByte(final @NonNegative int index, final byte defaultValue) { 144 | final Tag tag = this.get(index); 145 | if(tag.type().number()) { 146 | return ((NumberTag) tag).byteValue(); 147 | } 148 | return defaultValue; 149 | } 150 | 151 | /** 152 | * Gets a short. 153 | * 154 | * @param index the index 155 | * @return the short value, or {@code 0} 156 | */ 157 | public short getShort(final @NonNegative int index) { 158 | return this.getShort(index, (short) 0); 159 | } 160 | 161 | /** 162 | * Gets a short. 163 | * 164 | * @param index the index 165 | * @param defaultValue the default value 166 | * @return the short value, or {@code defaultValue} 167 | */ 168 | public short getShort(final @NonNegative int index, final short defaultValue) { 169 | final Tag tag = this.get(index); 170 | if(tag.type().number()) { 171 | return ((NumberTag) tag).shortValue(); 172 | } 173 | return defaultValue; 174 | } 175 | 176 | /** 177 | * Gets an int. 178 | * 179 | * @param index the index 180 | * @return the int value, or {@code 0} 181 | */ 182 | public int getInt(final @NonNegative int index) { 183 | return this.getInt(index, 0); 184 | } 185 | 186 | /** 187 | * Gets an int. 188 | * 189 | * @param index the index 190 | * @param defaultValue the default value 191 | * @return the int value, or {@code defaultValue} 192 | */ 193 | public int getInt(final @NonNegative int index, final int defaultValue) { 194 | final Tag tag = this.get(index); 195 | if(tag.type().number()) { 196 | return ((NumberTag) tag).intValue(); 197 | } 198 | return defaultValue; 199 | } 200 | 201 | /** 202 | * Gets a long. 203 | * 204 | * @param index the index 205 | * @return the long value, or {@code 0} 206 | */ 207 | public long getLong(final @NonNegative int index) { 208 | return this.getLong(index, 0L); 209 | } 210 | 211 | /** 212 | * Gets a long. 213 | * 214 | * @param index the index 215 | * @param defaultValue the default value 216 | * @return the long value, or {@code defaultValue} 217 | */ 218 | public long getLong(final @NonNegative int index, final long defaultValue) { 219 | final Tag tag = this.get(index); 220 | if(tag.type().number()) { 221 | return ((NumberTag) tag).longValue(); 222 | } 223 | return defaultValue; 224 | } 225 | 226 | /** 227 | * Gets a float. 228 | * 229 | * @param index the index 230 | * @return the float value, or {@code 0} 231 | */ 232 | public float getFloat(final @NonNegative int index) { 233 | return this.getFloat(index, 0f); 234 | } 235 | 236 | /** 237 | * Gets a float. 238 | * 239 | * @param index the index 240 | * @param defaultValue the default value 241 | * @return the float value, or {@code defaultValue} 242 | */ 243 | public float getFloat(final @NonNegative int index, final float defaultValue) { 244 | final Tag tag = this.get(index); 245 | if(tag.type().number()) { 246 | return ((NumberTag) tag).floatValue(); 247 | } 248 | return defaultValue; 249 | } 250 | 251 | /** 252 | * Gets a double. 253 | * 254 | * @param index the index 255 | * @return the double value, or {@code 0} 256 | */ 257 | public double getDouble(final @NonNegative int index) { 258 | return this.getDouble(index, 0d); 259 | } 260 | 261 | /** 262 | * Gets a double. 263 | * 264 | * @param index the index 265 | * @param defaultValue the default value 266 | * @return the double value, or {@code defaultValue} 267 | */ 268 | public double getDouble(final @NonNegative int index, final double defaultValue) { 269 | final Tag tag = this.get(index); 270 | if(tag.type().number()) { 271 | return ((NumberTag) tag).doubleValue(); 272 | } 273 | return defaultValue; 274 | } 275 | 276 | /** 277 | * Gets an array of bytes. 278 | * 279 | * @param index the index 280 | * @return the array of bytes, or a zero-length array 281 | */ 282 | public @NonNull byte[] getByteArray(final @NonNegative int index) { 283 | final Tag tag = this.get(index); 284 | if(tag.type() == TagType.BYTE_ARRAY) { 285 | return ((ByteArrayTag) tag).value(); 286 | } 287 | return new byte[0]; 288 | } 289 | 290 | /** 291 | * Gets an array of bytes. 292 | * 293 | * @param index the index 294 | * @param defaultValue the default value 295 | * @return the array of bytes, or {@code defaultValue} 296 | */ 297 | public @NonNull byte[] getByteArray(final @NonNegative int index, final @NonNull byte[] defaultValue) { 298 | final Tag tag = this.get(index); 299 | if(tag.type() == TagType.BYTE_ARRAY) { 300 | return ((ByteArrayTag) tag).value(); 301 | } 302 | return defaultValue; 303 | } 304 | 305 | /** 306 | * Gets a string. 307 | * 308 | * @param index the index 309 | * @return the string value, or {@code ""} 310 | */ 311 | public @NonNull String getString(final @NonNegative int index) { 312 | return this.getString(index, ""); 313 | } 314 | 315 | /** 316 | * Gets a string. 317 | * 318 | * @param index the index 319 | * @param defaultValue the default value 320 | * @return the string value, or {@code defaultValue} 321 | */ 322 | public @NonNull String getString(final @NonNegative int index, final @NonNull String defaultValue) { 323 | final Tag tag = this.get(index); 324 | if(tag.type() == TagType.STRING) { 325 | return ((StringTag) tag).value(); 326 | } 327 | return defaultValue; 328 | } 329 | 330 | /** 331 | * Gets a compound. 332 | * 333 | * @param index the index 334 | * @return the compound, or a new compound 335 | */ 336 | public @NonNull CompoundTag getCompound(final @NonNegative int index) { 337 | final Tag tag = this.get(index); 338 | if(tag.type() == TagType.COMPOUND) { 339 | return (CompoundTag) tag; 340 | } 341 | return new CompoundTag(); 342 | } 343 | 344 | /** 345 | * Gets a compound. 346 | * 347 | * @param index the index 348 | * @param defaultValue the default value 349 | * @return the compound, or {@code defaultValue} 350 | */ 351 | public @NonNull CompoundTag getCompound(final @NonNegative int index, final @NonNull CompoundTag defaultValue) { 352 | final Tag tag = this.get(index); 353 | if(tag.type() == TagType.COMPOUND) { 354 | return (CompoundTag) tag; 355 | } 356 | return defaultValue; 357 | } 358 | 359 | /** 360 | * Gets an array of ints. 361 | * 362 | * @param index the index 363 | * @return the array of ints, or a zero-length array 364 | */ 365 | public @NonNull int[] getIntArray(final @NonNegative int index) { 366 | final Tag tag = this.get(index); 367 | if(tag.type() == TagType.INT_ARRAY) { 368 | return ((IntArrayTag) tag).value(); 369 | } 370 | return new int[0]; 371 | } 372 | 373 | /** 374 | * Gets an array of ints. 375 | * 376 | * @param index the index 377 | * @param defaultValue the default value 378 | * @return the array of ints, or {@code defaultValue} 379 | */ 380 | public @NonNull int[] getIntArray(final @NonNegative int index, final @NonNull int[] defaultValue) { 381 | final Tag tag = this.get(index); 382 | if(tag.type() == TagType.INT_ARRAY) { 383 | return ((IntArrayTag) tag).value(); 384 | } 385 | return defaultValue; 386 | } 387 | 388 | /** 389 | * Gets an array of longs. 390 | * 391 | * @param index the index 392 | * @return the array of longs, or a zero-length array 393 | */ 394 | public @NonNull long[] getLongArray(final @NonNegative int index) { 395 | final Tag tag = this.get(index); 396 | if(tag.type() == TagType.LONG_ARRAY) { 397 | return ((LongArrayTag) tag).value(); 398 | } 399 | return new long[0]; 400 | } 401 | 402 | /** 403 | * Gets an array of longs. 404 | * 405 | * @param index the index 406 | * @param defaultValue the default value 407 | * @return the array of longs, or {@code defaultValue} 408 | */ 409 | public @NonNull long[] getLongArray(final @NonNegative int index, final @NonNull long[] defaultValue) { 410 | final Tag tag = this.get(index); 411 | if(tag.type() == TagType.LONG_ARRAY) { 412 | return ((LongArrayTag) tag).value(); 413 | } 414 | return defaultValue; 415 | } 416 | 417 | /** 418 | * Adds a tag. 419 | * 420 | * @param tag the tag 421 | */ 422 | @Override 423 | public boolean add(final @NonNull Tag tag) { 424 | // don't allow an end tag to be added 425 | if(tag.type() == TagType.END) { 426 | throw new IllegalArgumentException(String.format("Cannot add a '%s' to a '%s'", EndTag.class.getSimpleName(), ListTag.class.getSimpleName())); 427 | } 428 | // set the type if it has not yet been set 429 | if(this.type == TagType.END) { 430 | this.type = tag.type(); 431 | } 432 | this.tags.add(tag); 433 | return true; 434 | } 435 | 436 | /** 437 | * Sets the tag at the specified index. 438 | * 439 | * @param index the index 440 | * @param tag the tag 441 | * @throws IndexOutOfBoundsException if the index is out of range 442 | */ 443 | @Override 444 | public Tag set(final int index, final @NonNull Tag tag) { 445 | // don't allow an end tag to be added 446 | if(tag.type() == TagType.END) { 447 | throw new IllegalArgumentException(String.format("Cannot add a '%s' to a '%s'", EndTag.class.getSimpleName(), ListTag.class.getSimpleName())); 448 | } 449 | // set the type if it has not yet been set 450 | if(this.type == TagType.END) { 451 | this.type = tag.type(); 452 | } 453 | return this.tags.set(index, tag); 454 | } 455 | 456 | /** 457 | * Removes a tag. 458 | * 459 | * @param index the index 460 | * @return the tag 461 | * @throws IndexOutOfBoundsException if the index is out of range 462 | */ 463 | @Override 464 | public @NonNull Tag remove(final int index) { 465 | return this.tags.remove(index); 466 | } 467 | 468 | @Override 469 | public int size() { 470 | return this.tags.size(); 471 | } 472 | 473 | @Override 474 | public boolean isEmpty() { 475 | return this.tags.isEmpty(); 476 | } 477 | 478 | @Override 479 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 480 | if(depth > MAX_DEPTH) { 481 | throw new IllegalStateException(String.format("Depth of %d is higher than max of %d", depth, MAX_DEPTH)); 482 | } 483 | 484 | this.type = TagType.of(input.readByte()); 485 | 486 | final int length = input.readInt(); 487 | for(int i = 0; i < length; i++) { 488 | final Tag tag = this.type.create(); 489 | tag.read(input, depth + 1); 490 | this.tags.add(tag); 491 | } 492 | } 493 | 494 | @Override 495 | public void write(final @NonNull DataOutput output) throws IOException { 496 | output.writeByte(this.type.id()); 497 | output.writeInt(this.tags.size()); 498 | for(int i = 0, length = this.tags.size(); i < length; i++) { 499 | this.tags.get(i).write(output); 500 | } 501 | } 502 | 503 | @Override 504 | public @NonNull TagType type() { 505 | return TagType.LIST; 506 | } 507 | 508 | @Override 509 | public @NonNull ListTag copy() { 510 | final ListTag copy = new ListTag(this.type); 511 | for(final Tag tag : this.tags) { 512 | copy.tags.add(tag.copy()); // add directly to list, we can skip sanity checks 513 | } 514 | return copy; 515 | } 516 | 517 | @Override 518 | public int hashCode() { 519 | return this.tags.hashCode(); 520 | } 521 | 522 | @Override 523 | public boolean equals(final Object that) { 524 | return this == that || (that instanceof ListTag && this.tags.equals(((ListTag) that).tags)); 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /src/main/java/net/kyori/nbt/CompoundTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of nbt, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2017 KyoriPowered 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package net.kyori.nbt; 25 | 26 | import org.checkerframework.checker.nullness.qual.NonNull; 27 | import org.checkerframework.checker.nullness.qual.Nullable; 28 | 29 | import java.io.DataInput; 30 | import java.io.DataOutput; 31 | import java.io.IOException; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | import java.util.Set; 35 | import java.util.UUID; 36 | 37 | /** 38 | * A compound tag. 39 | */ 40 | public final class CompoundTag implements CollectionTag { 41 | /** 42 | * The maximum depth. 43 | */ 44 | public static final int MAX_DEPTH = 512; 45 | /** 46 | * The map of tags. 47 | */ 48 | private final Map tags = new HashMap<>(); 49 | 50 | /** 51 | * Gets a tag by its key. 52 | * 53 | * @param key the key 54 | * @return the tag, or {@code null} 55 | */ 56 | public @Nullable Tag get(final @NonNull String key) { 57 | return this.tags.get(key); 58 | } 59 | 60 | /** 61 | * Inserts a tag. 62 | * 63 | * @param key the key 64 | * @param tag the tag 65 | */ 66 | public void put(final @NonNull String key, final @NonNull Tag tag) { 67 | this.tags.put(key, tag); 68 | } 69 | 70 | /** 71 | * Removes a tag. 72 | * 73 | * @param key the key 74 | */ 75 | public void remove(final @NonNull String key) { 76 | this.tags.remove(key); 77 | } 78 | 79 | /** 80 | * Checks if this compound has a tag with the specified key. 81 | * 82 | * @param key the key 83 | * @return {@code true} if this compound has a tag with the specified key 84 | */ 85 | public boolean contains(final @NonNull String key) { 86 | return this.tags.containsKey(key); 87 | } 88 | 89 | @Override 90 | public int size() { 91 | return this.tags.size(); 92 | } 93 | 94 | @Override 95 | public boolean isEmpty() { 96 | return this.tags.isEmpty(); 97 | } 98 | 99 | /** 100 | * Gets a set of keys of the entries in this compound tag. 101 | * 102 | * @return a set of keys 103 | */ 104 | public Set keySet() { 105 | return this.tags.keySet(); 106 | } 107 | 108 | /** 109 | * Checks if this compound has a tag with the specified key and type. 110 | * 111 | * @param key the key 112 | * @param type the type 113 | * @return {@code true} if this compound has a tag with the specified key and type 114 | */ 115 | public boolean contains(final @NonNull String key, final @NonNull TagType type) { 116 | final /* @Nullable */ Tag tag = this.tags.get(key); 117 | return tag != null && type.test(tag.type()); 118 | } 119 | 120 | /** 121 | * Gets the tag type of the tag with the specified key. 122 | * 123 | * @param key the key 124 | * @return the tag type, or {@link TagType#END} 125 | */ 126 | public @NonNull TagType type(final @NonNull String key) { 127 | final /* @Nullable */ Tag tag = this.tags.get(key); 128 | return tag != null ? tag.type() : TagType.END; 129 | } 130 | 131 | /** 132 | * Gets a byte. 133 | * 134 | * @param key the key 135 | * @return the byte value, or {@code 0} if this compound does not contain a byte tag 136 | * with the specified key, or has a tag with a different type 137 | */ 138 | public byte getByte(final @NonNull String key) { 139 | return this.getByte(key, (byte) 0); 140 | } 141 | 142 | /** 143 | * Gets a byte. 144 | * 145 | * @param key the key 146 | * @param defaultValue the default value 147 | * @return the byte value, or {@code defaultValue} if this compound does not contain a byte tag 148 | * with the specified key, or has a tag with a different type 149 | */ 150 | public byte getByte(final @NonNull String key, final byte defaultValue) { 151 | if(this.contains(key, TagType.BYTE)) { 152 | return ((NumberTag) this.tags.get(key)).byteValue(); 153 | } 154 | return defaultValue; 155 | } 156 | 157 | /** 158 | * Inserts a byte. 159 | * 160 | * @param key the key 161 | * @param value the value 162 | */ 163 | public void putByte(final @NonNull String key, final byte value) { 164 | this.tags.put(key, new ByteTag(value)); 165 | } 166 | 167 | /** 168 | * Gets a short. 169 | * 170 | * @param key the key 171 | * @return the short value, or {@code 0} if this compound does not contain a short tag 172 | * with the specified key, or has a tag with a different type 173 | */ 174 | public short getShort(final @NonNull String key) { 175 | return this.getShort(key, (short) 0); 176 | } 177 | 178 | /** 179 | * Gets a short. 180 | * 181 | * @param key the key 182 | * @param defaultValue the default value 183 | * @return the short value, or {@code defaultValue} if this compound does not contain a short tag 184 | * with the specified key, or has a tag with a different type 185 | */ 186 | public short getShort(final @NonNull String key, final short defaultValue) { 187 | if(this.contains(key, TagType.SHORT)) { 188 | return ((NumberTag) this.tags.get(key)).shortValue(); 189 | } 190 | return defaultValue; 191 | } 192 | 193 | /** 194 | * Inserts a short. 195 | * 196 | * @param key the key 197 | * @param value the value 198 | */ 199 | public void putShort(final @NonNull String key, final short value) { 200 | this.tags.put(key, new ShortTag(value)); 201 | } 202 | 203 | /** 204 | * Gets an int. 205 | * 206 | * @param key the key 207 | * @return the int value, or {@code 0} if this compound does not contain an int tag 208 | * with the specified key, or has a tag with a different type 209 | */ 210 | public int getInt(final @NonNull String key) { 211 | return this.getInt(key, 0); 212 | } 213 | 214 | /** 215 | * Gets an int. 216 | * 217 | * @param key the key 218 | * @param defaultValue the default value 219 | * @return the int value, or {@code defaultValue} if this compound does not contain an int tag 220 | * with the specified key, or has a tag with a different type 221 | */ 222 | public int getInt(final @NonNull String key, final int defaultValue) { 223 | if(this.contains(key, TagType.INT)) { 224 | return ((NumberTag) this.tags.get(key)).intValue(); 225 | } 226 | return defaultValue; 227 | } 228 | 229 | /** 230 | * Inserts an int. 231 | * 232 | * @param key the key 233 | * @param value the value 234 | */ 235 | public void putInt(final @NonNull String key, final int value) { 236 | this.tags.put(key, new IntTag(value)); 237 | } 238 | 239 | /** 240 | * Gets a long. 241 | * 242 | * @param key the key 243 | * @return the long value, or {@code 0} if this compound does not contain a long tag 244 | * with the specified key, or has a tag with a different type 245 | */ 246 | public long getLong(final @NonNull String key) { 247 | return this.getLong(key, 0L); 248 | } 249 | 250 | /** 251 | * Gets a long. 252 | * 253 | * @param key the key 254 | * @param defaultValue the default value 255 | * @return the long value, or {@code defaultValue} if this compound does not contain a long tag 256 | * with the specified key, or has a tag with a different type 257 | */ 258 | public long getLong(final @NonNull String key, final long defaultValue) { 259 | if(this.contains(key, TagType.LONG)) { 260 | return ((NumberTag) this.tags.get(key)).longValue(); 261 | } 262 | return defaultValue; 263 | } 264 | 265 | /** 266 | * Inserts a long. 267 | * 268 | * @param key the key 269 | * @param value the value 270 | */ 271 | public void putLong(final @NonNull String key, final long value) { 272 | this.tags.put(key, new LongTag(value)); 273 | } 274 | 275 | /** 276 | * Gets a float. 277 | * 278 | * @param key the key 279 | * @return the float value, or {@code 0} if this compound does not contain a float tag 280 | * with the specified key, or has a tag with a different type 281 | */ 282 | public float getFloat(final @NonNull String key) { 283 | return this.getFloat(key, 0f); 284 | } 285 | 286 | /** 287 | * Gets a float. 288 | * 289 | * @param key the key 290 | * @param defaultValue the default value 291 | * @return the float value, or {@code defaultValue} if this compound does not contain a float tag 292 | * with the specified key, or has a tag with a different type 293 | */ 294 | public float getFloat(final @NonNull String key, final float defaultValue) { 295 | if(this.contains(key, TagType.FLOAT)) { 296 | return ((NumberTag) this.tags.get(key)).floatValue(); 297 | } 298 | return defaultValue; 299 | } 300 | 301 | /** 302 | * Inserts a float. 303 | * 304 | * @param key the key 305 | * @param value the value 306 | */ 307 | public void putFloat(final @NonNull String key, final float value) { 308 | this.tags.put(key, new FloatTag(value)); 309 | } 310 | 311 | /** 312 | * Gets a double. 313 | * 314 | * @param key the key 315 | * @return the double value, or {@code 0} if this compound does not contain a double tag 316 | * with the specified key, or has a tag with a different type 317 | */ 318 | public double getDouble(final @NonNull String key) { 319 | return this.getDouble(key, 0d); 320 | } 321 | 322 | /** 323 | * Gets a double. 324 | * 325 | * @param key the key 326 | * @param defaultValue the default value 327 | * @return the double value, or {@code defaultValue} if this compound does not contain a double tag 328 | * with the specified key, or has a tag with a different type 329 | */ 330 | public double getDouble(final @NonNull String key, final double defaultValue) { 331 | if(this.contains(key, TagType.DOUBLE)) { 332 | return ((NumberTag) this.tags.get(key)).doubleValue(); 333 | } 334 | return defaultValue; 335 | } 336 | 337 | /** 338 | * Inserts a double. 339 | * 340 | * @param key the key 341 | * @param value the value 342 | */ 343 | public void putDouble(final @NonNull String key, final double value) { 344 | this.tags.put(key, new DoubleTag(value)); 345 | } 346 | 347 | /** 348 | * Gets an array of bytes. 349 | * 350 | * @param key the key 351 | * @return the array of bytes, or a zero-length array if this compound does not contain a byte array tag 352 | * with the specified key, or has a tag with a different type 353 | */ 354 | public byte@NonNull[] getByteArray(final @NonNull String key) { 355 | if(this.contains(key, TagType.BYTE_ARRAY)) { 356 | return ((ByteArrayTag) this.tags.get(key)).value(); 357 | } 358 | return new byte[0]; 359 | } 360 | 361 | /** 362 | * Gets an array of bytes. 363 | * 364 | * @param key the key 365 | * @param defaultValue the default value 366 | * @return the array of bytes, or {@code defaultValue} 367 | */ 368 | public byte@NonNull[] getByteArray(final @NonNull String key, final byte@NonNull[] defaultValue) { 369 | if(this.contains(key, TagType.BYTE_ARRAY)) { 370 | return ((ByteArrayTag) this.tags.get(key)).value(); 371 | } 372 | return defaultValue; 373 | } 374 | 375 | /** 376 | * Inserts an array of bytes. 377 | * 378 | * @param key the key 379 | * @param value the value 380 | */ 381 | public void putByteArray(final @NonNull String key, final byte@NonNull[] value) { 382 | this.tags.put(key, new ByteArrayTag(value)); 383 | } 384 | 385 | /** 386 | * Gets a string. 387 | * 388 | * @param key the key 389 | * @return the string value, or {@code ""} if this compound does not contain a string tag 390 | * with the specified key, or has a tag with a different type 391 | */ 392 | public @NonNull String getString(final @NonNull String key) { 393 | return this.getString(key, ""); 394 | } 395 | 396 | /** 397 | * Gets a string. 398 | * 399 | * @param key the key 400 | * @param defaultValue the default value 401 | * @return the string value, or {@code defaultValue} if this compound does not contain a string tag 402 | * with the specified key, or has a tag with a different type 403 | */ 404 | public @NonNull String getString(final @NonNull String key, final @NonNull String defaultValue) { 405 | if(this.contains(key, TagType.STRING)) { 406 | return ((StringTag) this.tags.get(key)).value(); 407 | } 408 | return defaultValue; 409 | } 410 | 411 | /** 412 | * Inserts a string. 413 | * 414 | * @param key the key 415 | * @param value the value 416 | */ 417 | public void putString(final @NonNull String key, final @NonNull String value) { 418 | this.tags.put(key, new StringTag(value)); 419 | } 420 | 421 | /** 422 | * Gets a list. 423 | * 424 | * @param key the key 425 | * @return the list, or a new list if this compound does not contain a list tag 426 | * with the specified key, or has a tag with a different type 427 | */ 428 | public @NonNull ListTag getList(final @NonNull String key) { 429 | if(this.contains(key, TagType.LIST)) { 430 | return (ListTag) this.tags.get(key); 431 | } 432 | return new ListTag(); 433 | } 434 | 435 | /** 436 | * Gets a list, ensuring that the type is the same as {@code type}. 437 | * 438 | * @param key the key 439 | * @param expectedType the expected list type 440 | * @return the list, or a new list if this compound does not contain a list tag 441 | * with the specified key, has a tag with a different type, or the {@link ListTag#listType() list type} 442 | * does not match {@code expectedType} 443 | */ 444 | public @NonNull ListTag getList(final @NonNull String key, final @NonNull TagType expectedType) { 445 | if(this.contains(key, TagType.LIST)) { 446 | final ListTag tag = (ListTag) this.get(key); 447 | if(expectedType.test(tag.listType())) { 448 | return tag; 449 | } 450 | } 451 | return new ListTag(); 452 | } 453 | 454 | /** 455 | * Gets a list, ensuring that the type is the same as {@code type}. 456 | * 457 | * @param key the key 458 | * @param expectedType the expected list type 459 | * @param defaultValue the default value 460 | * @return the list, or {@code defaultValue} if this compound does not contain a list tag 461 | * with the specified key, has a tag with a different type, or the {@link ListTag#listType() list type} 462 | * does not match {@code expectedType} 463 | */ 464 | public @NonNull ListTag getList(final @NonNull String key, final @NonNull TagType expectedType, final @NonNull ListTag defaultValue) { 465 | if(this.contains(key, TagType.LIST)) { 466 | final ListTag tag = (ListTag) this.get(key); 467 | if(expectedType.test(tag.listType())) { 468 | return tag; 469 | } 470 | } 471 | return defaultValue; 472 | } 473 | 474 | /** 475 | * Gets a list. 476 | * 477 | * @param key the key 478 | * @param defaultValue the default value 479 | * @return the list, or {@code defaultValue} if this compound does not contain a list tag 480 | * with the specified key, or has a tag with a different type 481 | */ 482 | public @NonNull ListTag getList(final @NonNull String key, final @NonNull ListTag defaultValue) { 483 | if(this.contains(key, TagType.LIST)) { 484 | return (ListTag) this.tags.get(key); 485 | } 486 | return defaultValue; 487 | } 488 | 489 | /** 490 | * Gets a compound. 491 | * 492 | * @param key the key 493 | * @return the compound, or a new compound if this compound does not contain a compound tag 494 | * with the specified key, or has a tag with a different type 495 | */ 496 | public @NonNull CompoundTag getCompound(final @NonNull String key) { 497 | if(this.contains(key, TagType.COMPOUND)) { 498 | return (CompoundTag) this.tags.get(key); 499 | } 500 | return new CompoundTag(); 501 | } 502 | 503 | /** 504 | * Gets a compound. 505 | * 506 | * @param key the key 507 | * @param defaultValue the default value 508 | * @return the compound, or {@code defaultValue} if this compound does not contain a compound tag 509 | * with the specified key, or has a tag with a different type 510 | */ 511 | public @NonNull CompoundTag getCompound(final @NonNull String key, final @NonNull CompoundTag defaultValue) { 512 | if(this.contains(key, TagType.COMPOUND)) { 513 | return (CompoundTag) this.tags.get(key); 514 | } 515 | return defaultValue; 516 | } 517 | 518 | /** 519 | * Gets an array of ints. 520 | * 521 | * @param key the key 522 | * @return the array of ints, or a zero-length array if this compound does not contain a int array tag 523 | * with the specified key, or has a tag with a different type 524 | */ 525 | public int@NonNull[] getIntArray(final @NonNull String key) { 526 | if(this.contains(key, TagType.INT_ARRAY)) { 527 | return ((IntArrayTag) this.tags.get(key)).value(); 528 | } 529 | return new int[0]; 530 | } 531 | 532 | /** 533 | * Gets an array of ints. 534 | * 535 | * @param key the key 536 | * @param defaultValue the default value 537 | * @return the array of ints, or {@code defaultValue} 538 | */ 539 | public int@NonNull[] getIntArray(final @NonNull String key, final int@NonNull[] defaultValue) { 540 | if(this.contains(key, TagType.INT_ARRAY)) { 541 | return ((IntArrayTag) this.tags.get(key)).value(); 542 | } 543 | return defaultValue; 544 | } 545 | 546 | /** 547 | * Inserts an array of ints. 548 | * 549 | * @param key the key 550 | * @param value the value 551 | */ 552 | public void putIntArray(final @NonNull String key, final int@NonNull[] value) { 553 | this.tags.put(key, new IntArrayTag(value)); 554 | } 555 | 556 | /** 557 | * Gets an array of longs. 558 | * 559 | * @param key the key 560 | * @return the array of longs, or a zero-length array if this compound does not contain a long array tag 561 | * with the specified key, or has a tag with a different type 562 | */ 563 | public long@NonNull[] getLongArray(final @NonNull String key) { 564 | if(this.contains(key, TagType.LONG_ARRAY)) { 565 | return ((LongArrayTag) this.tags.get(key)).value(); 566 | } 567 | return new long[0]; 568 | } 569 | 570 | /** 571 | * Gets an array of longs. 572 | * 573 | * @param key the key 574 | * @param defaultValue the default value 575 | * @return the array of longs, or {@code defaultValue} 576 | */ 577 | public long@NonNull[] getLongArray(final @NonNull String key, final long@NonNull[] defaultValue) { 578 | if(this.contains(key, TagType.LONG_ARRAY)) { 579 | return ((LongArrayTag) this.tags.get(key)).value(); 580 | } 581 | return defaultValue; 582 | } 583 | 584 | /** 585 | * Inserts an array of longs. 586 | * 587 | * @param key the key 588 | * @param value the value 589 | */ 590 | public void putLongArray(final @NonNull String key, final long@NonNull[] value) { 591 | this.tags.put(key, new LongArrayTag(value)); 592 | } 593 | 594 | /** 595 | * Gets a boolean. 596 | * 597 | *

A boolean is stored as a {@code byte} internally.

598 | * 599 | * @param key the key 600 | * @return the boolean, or {@code false} if this compound does not contain a boolean with 601 | * the specified key, or has a tag with a different type 602 | */ 603 | public boolean getBoolean(final @NonNull String key) { 604 | return this.getBoolean(key, false); 605 | } 606 | 607 | /** 608 | * Gets a boolean. 609 | * 610 | *

A boolean is stored as a {@code byte} internally.

611 | * 612 | * @param key the key 613 | * @param defaultValue the default value 614 | * @return the boolean, or {@code defaultValue} if this compound does not contain a boolean with 615 | * the specified key, or has a tag with a different type 616 | */ 617 | public boolean getBoolean(final @NonNull String key, final boolean defaultValue) { 618 | // >=, as this can be something other than a byte 619 | return this.getByte(key, defaultValue ? ByteTag.TRUE : ByteTag.FALSE) >= ByteTag.TRUE; 620 | } 621 | 622 | /** 623 | * Inserts a boolean. 624 | * 625 | *

A boolean is stored as a {@code byte} internally.

626 | * 627 | * @param key the key 628 | * @param value the value 629 | */ 630 | public void putBoolean(final @NonNull String key, final boolean value) { 631 | this.putByte(key, value ? ByteTag.TRUE : ByteTag.FALSE); 632 | } 633 | 634 | /** 635 | * Checks if this compound has a boolean tag with the specified key. 636 | * 637 | * @param key the key 638 | * @return {@code true} if this compound has a boolean tag with the specified key 639 | */ 640 | public boolean containsBoolean(final @NonNull String key) { 641 | return this.contains(key, TagType.BYTE); 642 | } 643 | 644 | /** 645 | * Gets a unique id. 646 | * 647 | *

A unique id is stored as two {@code long}s internally.

648 | * 649 | * @param key the key 650 | * @return the unique id 651 | */ 652 | public @NonNull UUID getUniqueId(final @NonNull String key) { 653 | return new UUID(this.getLong(key + "Most"), this.getLong(key + "Least")); 654 | } 655 | 656 | /** 657 | * Inserts a unique id. 658 | * 659 | *

A unique id is stored as two {@code long}s internally.

660 | * 661 | * @param key the key 662 | * @param value the value 663 | */ 664 | public void putUniqueId(final @NonNull String key, final @NonNull UUID value) { 665 | this.putLong(key + "Least", value.getLeastSignificantBits()); 666 | this.putLong(key + "Most", value.getMostSignificantBits()); 667 | } 668 | 669 | /** 670 | * Checks if this compound has a unique id tag with the specified key. 671 | * 672 | * @param key the key 673 | * @return {@code true} if this compound has a unique id tag with the specified key 674 | */ 675 | public boolean containsUniqueId(final @NonNull String key) { 676 | return this.contains(key + "Least", TagType.LONG) && this.contains(key + "Most", TagType.LONG); 677 | } 678 | 679 | @Override 680 | public void read(final @NonNull DataInput input, final int depth) throws IOException { 681 | if(depth > MAX_DEPTH) { 682 | throw new IllegalStateException(String.format("Depth of %d is higher than max of %d", depth, MAX_DEPTH)); 683 | } 684 | 685 | TagType type; 686 | while((type = TagType.of(input.readByte())) != TagType.END) { 687 | final String key = input.readUTF(); 688 | final Tag tag = type.create(); 689 | tag.read(input, depth + 1); 690 | this.tags.put(key, tag); 691 | } 692 | } 693 | 694 | @Override 695 | public void write(final @NonNull DataOutput output) throws IOException { 696 | for(final String key : this.tags.keySet()) { 697 | final Tag tag = this.tags.get(key); 698 | output.writeByte(tag.type().id()); 699 | if(tag.type() != TagType.END) { 700 | output.writeUTF(key); 701 | tag.write(output); 702 | } 703 | } 704 | output.writeByte(TagType.END.id()); 705 | } 706 | 707 | @Override 708 | public @NonNull TagType type() { 709 | return TagType.COMPOUND; 710 | } 711 | 712 | @Override 713 | public @NonNull CompoundTag copy() { 714 | final CompoundTag copy = new CompoundTag(); 715 | for(final Map.Entry entry : this.tags.entrySet()) { 716 | copy.put(entry.getKey(), entry.getValue().copy()); 717 | } 718 | return copy; 719 | } 720 | 721 | @Override 722 | public int hashCode() { 723 | return this.tags.hashCode(); 724 | } 725 | 726 | @Override 727 | public boolean equals(final Object that) { 728 | return this == that || (that instanceof CompoundTag && this.tags.equals(((CompoundTag) that).tags)); 729 | } 730 | } 731 | --------------------------------------------------------------------------------