137 | */
138 | public static class Reader {
139 |
140 | private boolean littleEndian = false;
141 |
142 | public Reader() {
143 | }
144 |
145 | public static Reader read() {
146 | return new Reader();
147 | }
148 |
149 | /**
150 | * Read from the source as Little Endian. Usually reserved for network packets or some systems architectures.
151 | *
152 | * @return the reader builder
153 | */
154 | public Reader littleEndian() {
155 | this.littleEndian = true;
156 | return this;
157 | }
158 |
159 | /**
160 | * Read from the source as Big Endian. This is the default.
161 | *
162 | * @return the reader builder
163 | */
164 | public Reader bigEndian() {
165 | this.littleEndian = false;
166 | return this;
167 | }
168 |
169 | /**
170 | * Reads the NBT tag from an input stream. Terminal operator.
171 | *
172 | * @param is The input stream to read from
173 | * @return The parsed NBT tag
174 | * @throws IOException In case of error reading from the input stream
175 | */
176 | public NamedTag from(InputStream is) throws IOException {
177 | return new NBTDeserializer(false/* ignored, will autodetect compression */, littleEndian)
178 | .fromStream(detectDecompression(is));
179 | }
180 |
181 | /**
182 | * Reads the NBT tag from a byte array. Terminal operator.
183 | *
184 | * @param bytes The byte array
185 | * @return The parsed NBT tag
186 | * @throws IOException In case of error reading from the input stream
187 | */
188 | public NamedTag from(byte[] bytes) throws IOException {
189 | return from(new ByteArrayInputStream(bytes));
190 | }
191 |
192 | /**
193 | * Reads the NBT tag from a file. Terminal operator.
194 | *
195 | * @param path The file path to read from
196 | * @return The parsed NBT tag
197 | * @throws IOException In case of error reading from the file
198 | */
199 | public NamedTag from(Path path) throws IOException {
200 | try (InputStream is = new BufferedInputStream(Files.newInputStream(path))) {
201 | return from(is);
202 | }
203 | }
204 |
205 | /**
206 | * Reads the NBT tag from a file. Terminal operator.
207 | *
208 | * @param file The file path to read from
209 | * @return The parsed NBT tag
210 | * @throws IOException In case of error reading from the file
211 | */
212 | public NamedTag from(File file) throws IOException {
213 | return from(file.toPath());
214 | }
215 |
216 | /**
217 | * Reads the NBT tag from a file. Terminal operator.
218 | *
219 | * @param file The file path to read from
220 | * @return The parsed NBT tag
221 | * @throws IOException In case of error reading from the file
222 | */
223 | public NamedTag from(String file) throws IOException {
224 | return from(Paths.get(file));
225 | }
226 |
227 | private static InputStream detectDecompression(InputStream is) throws IOException {
228 | PushbackInputStream pbis = new PushbackInputStream(is, 2);
229 | int signature = (pbis.read() & 0xFF) + (pbis.read() << 8);
230 | pbis.unread(signature >> 8);
231 | pbis.unread(signature & 0xFF);
232 | return signature == GZIPInputStream.GZIP_MAGIC ? new GZIPInputStream(pbis) : pbis;
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/NamedTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import net.sandrohc.schematic4j.nbt.tag.Tag;
5 |
6 | /**
7 | * A named tag.
8 | */
9 | public class NamedTag {
10 |
11 | /**
12 | * The name of the named tag.
13 | */
14 | private String name;
15 |
16 | /**
17 | * The inner tag.
18 | */
19 | private Tag> tag;
20 |
21 | public NamedTag(String name, Tag> tag) {
22 | this.name = name;
23 | this.tag = tag;
24 | }
25 |
26 | /**
27 | * Set a new name.
28 | *
29 | * @param name The new name
30 | */
31 | public void setName(String name) {
32 | this.name = name;
33 | }
34 |
35 | /**
36 | * Set a new tag.
37 | *
38 | * @param tag The new tag
39 | */
40 | public void setTag(Tag> tag) {
41 | this.tag = tag;
42 | }
43 |
44 | /**
45 | * Get the named tag name.
46 | *
47 | * @return The named tag name
48 | */
49 | public String getName() {
50 | return name;
51 | }
52 |
53 | /**
54 | * Get the named tag inner tag.
55 | *
56 | * @return The named tag inner tag
57 | */
58 | public Tag> getTag() {
59 | return tag;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/ParseException.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.io.IOException;
5 |
6 | public class ParseException extends IOException {
7 |
8 | public ParseException(String msg) {
9 | super(msg);
10 | }
11 |
12 | public ParseException(String msg, String value, int index) {
13 | super(msg + " at: " + formatError(value, index));
14 | }
15 |
16 | private static String formatError(String value, int index) {
17 | StringBuilder builder = new StringBuilder();
18 | int i = Math.min(value.length(), index);
19 | if (i > 35) {
20 | builder.append("...");
21 | }
22 | builder.append(value, Math.max(0, i - 35), i);
23 | builder.append("<--[HERE]");
24 | return builder.toString();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/SNBTDeserializer.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.io.BufferedReader;
5 | import java.io.IOException;
6 | import java.io.Reader;
7 | import java.util.stream.Collectors;
8 |
9 | import net.sandrohc.schematic4j.nbt.StringDeserializer;
10 | import net.sandrohc.schematic4j.nbt.tag.Tag;
11 |
12 | public class SNBTDeserializer implements StringDeserializer> {
13 |
14 | @Override
15 | public Tag> fromReader(Reader reader) throws IOException {
16 | return fromReader(reader, Tag.DEFAULT_MAX_DEPTH);
17 | }
18 |
19 | public Tag> fromReader(Reader reader, int maxDepth) throws IOException {
20 | BufferedReader bufferedReader;
21 | if (reader instanceof BufferedReader) {
22 | bufferedReader = (BufferedReader) reader;
23 | } else {
24 | bufferedReader = new BufferedReader(reader);
25 | }
26 | return SNBTParser.parse(bufferedReader.lines().collect(Collectors.joining()), maxDepth);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/SNBTParser.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.regex.Pattern;
7 |
8 | import net.sandrohc.schematic4j.nbt.MaxDepthIO;
9 | import net.sandrohc.schematic4j.nbt.tag.ArrayTag;
10 | import net.sandrohc.schematic4j.nbt.tag.ByteArrayTag;
11 | import net.sandrohc.schematic4j.nbt.tag.ByteTag;
12 | import net.sandrohc.schematic4j.nbt.tag.CompoundTag;
13 | import net.sandrohc.schematic4j.nbt.tag.DoubleTag;
14 | import net.sandrohc.schematic4j.nbt.tag.EndTag;
15 | import net.sandrohc.schematic4j.nbt.tag.FloatTag;
16 | import net.sandrohc.schematic4j.nbt.tag.IntArrayTag;
17 | import net.sandrohc.schematic4j.nbt.tag.IntTag;
18 | import net.sandrohc.schematic4j.nbt.tag.ListTag;
19 | import net.sandrohc.schematic4j.nbt.tag.LongArrayTag;
20 | import net.sandrohc.schematic4j.nbt.tag.LongTag;
21 | import net.sandrohc.schematic4j.nbt.tag.ShortTag;
22 | import net.sandrohc.schematic4j.nbt.tag.StringTag;
23 | import net.sandrohc.schematic4j.nbt.tag.Tag;
24 |
25 | public final class SNBTParser implements MaxDepthIO {
26 |
27 | private static final Pattern
28 | FLOAT_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?f$", Pattern.CASE_INSENSITIVE),
29 | DOUBLE_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?d$", Pattern.CASE_INSENSITIVE),
30 | DOUBLE_LITERAL_NO_SUFFIX_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.|\\d*\\.\\d+)(?:e[-+]?\\d+)?$", Pattern.CASE_INSENSITIVE),
31 | BYTE_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+b$", Pattern.CASE_INSENSITIVE),
32 | SHORT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+s$", Pattern.CASE_INSENSITIVE),
33 | INT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+$", Pattern.CASE_INSENSITIVE),
34 | LONG_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+l$", Pattern.CASE_INSENSITIVE),
35 | NUMBER_PATTERN = Pattern.compile("^[-+]?\\d+$");
36 |
37 | private StringPointer ptr;
38 |
39 | private SNBTParser(String string) {
40 | this.ptr = new StringPointer(string);
41 | }
42 |
43 | public static Tag> parse(String string, int maxDepth) throws ParseException {
44 | SNBTParser parser = new SNBTParser(string);
45 | Tag> tag = parser.parseAnything(maxDepth);
46 | parser.ptr.skipWhitespace();
47 | if (parser.ptr.hasNext()) {
48 | throw parser.ptr.parseException("invalid characters after end of snbt");
49 | }
50 | return tag;
51 | }
52 |
53 | public static Tag> parse(String string) throws ParseException {
54 | return parse(string, Tag.DEFAULT_MAX_DEPTH);
55 | }
56 |
57 | private Tag> parseAnything(int maxDepth) throws ParseException {
58 | ptr.skipWhitespace();
59 | switch (ptr.currentChar()) {
60 | case '{':
61 | return parseCompoundTag(maxDepth);
62 | case '[':
63 | if (ptr.hasCharsLeft(2) && ptr.lookAhead(1) != '"' && ptr.lookAhead(2) == ';') {
64 | return parseNumArray();
65 | }
66 | return parseListTag(maxDepth);
67 | }
68 | return parseStringOrLiteral();
69 | }
70 |
71 | private Tag> parseStringOrLiteral() throws ParseException {
72 | ptr.skipWhitespace();
73 | if (ptr.currentChar() == '"') {
74 | return new StringTag(ptr.parseQuotedString());
75 | }
76 | String s = ptr.parseSimpleString();
77 | if (s.isEmpty()) {
78 | throw new ParseException("expected non empty value");
79 | }
80 | if (FLOAT_LITERAL_PATTERN.matcher(s).matches()) {
81 | return new FloatTag(Float.parseFloat(s.substring(0, s.length() - 1)));
82 | } else if (BYTE_LITERAL_PATTERN.matcher(s).matches()) {
83 | try {
84 | return new ByteTag(Byte.parseByte(s.substring(0, s.length() - 1)));
85 | } catch (NumberFormatException ex) {
86 | throw ptr.parseException("byte not in range: \"" + s.substring(0, s.length() - 1) + "\"");
87 | }
88 | } else if (SHORT_LITERAL_PATTERN.matcher(s).matches()) {
89 | try {
90 | return new ShortTag(Short.parseShort(s.substring(0, s.length() - 1)));
91 | } catch (NumberFormatException ex) {
92 | throw ptr.parseException("short not in range: \"" + s.substring(0, s.length() - 1) + "\"");
93 | }
94 | } else if (LONG_LITERAL_PATTERN.matcher(s).matches()) {
95 | try {
96 | return new LongTag(Long.parseLong(s.substring(0, s.length() - 1)));
97 | } catch (NumberFormatException ex) {
98 | throw ptr.parseException("long not in range: \"" + s.substring(0, s.length() - 1) + "\"");
99 | }
100 | } else if (INT_LITERAL_PATTERN.matcher(s).matches()) {
101 | try {
102 | return new IntTag(Integer.parseInt(s));
103 | } catch (NumberFormatException ex) {
104 | throw ptr.parseException("int not in range: \"" + s.substring(0, s.length() - 1) + "\"");
105 | }
106 | } else if (DOUBLE_LITERAL_PATTERN.matcher(s).matches()) {
107 | return new DoubleTag(Double.parseDouble(s.substring(0, s.length() - 1)));
108 | } else if (DOUBLE_LITERAL_NO_SUFFIX_PATTERN.matcher(s).matches()) {
109 | return new DoubleTag(Double.parseDouble(s));
110 | } else if ("true".equalsIgnoreCase(s)) {
111 | return new ByteTag(true);
112 | } else if ("false".equalsIgnoreCase(s)) {
113 | return new ByteTag(false);
114 | }
115 | return new StringTag(s);
116 | }
117 |
118 | private CompoundTag parseCompoundTag(int maxDepth) throws ParseException {
119 | ptr.expectChar('{');
120 |
121 | CompoundTag compoundTag = new CompoundTag();
122 |
123 | ptr.skipWhitespace();
124 | while (ptr.hasNext() && ptr.currentChar() != '}') {
125 | ptr.skipWhitespace();
126 | String key = ptr.currentChar() == '"' ? ptr.parseQuotedString() : ptr.parseSimpleString();
127 | if (key.isEmpty()) {
128 | throw new ParseException("empty keys are not allowed");
129 | }
130 | ptr.expectChar(':');
131 |
132 | compoundTag.put(key, parseAnything(decrementMaxDepth(maxDepth)));
133 |
134 | if (!ptr.nextArrayElement()) {
135 | break;
136 | }
137 | }
138 | ptr.expectChar('}');
139 | return compoundTag;
140 | }
141 |
142 | private ListTag> parseListTag(int maxDepth) throws ParseException {
143 | ptr.expectChar('[');
144 | ptr.skipWhitespace();
145 | ListTag> list = ListTag.createUnchecked(EndTag.class);
146 | while (ptr.currentChar() != ']') {
147 | Tag> element = parseAnything(decrementMaxDepth(maxDepth));
148 | try {
149 | list.addUnchecked(element);
150 | } catch (IllegalArgumentException ex) {
151 | throw ptr.parseException(ex.getMessage());
152 | }
153 | if (!ptr.nextArrayElement()) {
154 | break;
155 | }
156 | }
157 | ptr.expectChar(']');
158 | return list;
159 | }
160 |
161 | private ArrayTag> parseNumArray() throws ParseException {
162 | ptr.expectChar('[');
163 | char arrayType = ptr.next();
164 | ptr.expectChar(';');
165 | ptr.skipWhitespace();
166 | switch (arrayType) {
167 | case 'B':
168 | return parseByteArrayTag();
169 | case 'I':
170 | return parseIntArrayTag();
171 | case 'L':
172 | return parseLongArrayTag();
173 | }
174 | throw new ParseException("invalid array type '" + arrayType + "'");
175 | }
176 |
177 | private ByteArrayTag parseByteArrayTag() throws ParseException {
178 | List byteList = new ArrayList<>();
179 | while (ptr.currentChar() != ']') {
180 | String s = ptr.parseSimpleString();
181 | ptr.skipWhitespace();
182 | if (NUMBER_PATTERN.matcher(s).matches()) {
183 | try {
184 | byteList.add(Byte.parseByte(s));
185 | } catch (NumberFormatException ex) {
186 | throw ptr.parseException("byte not in range: \"" + s + "\"");
187 | }
188 | } else {
189 | throw ptr.parseException("invalid byte in ByteArrayTag: \"" + s + "\"");
190 | }
191 | if (!ptr.nextArrayElement()) {
192 | break;
193 | }
194 | }
195 | ptr.expectChar(']');
196 | byte[] bytes = new byte[byteList.size()];
197 | for (int i = 0; i < byteList.size(); i++) {
198 | bytes[i] = byteList.get(i);
199 | }
200 | return new ByteArrayTag(bytes);
201 | }
202 |
203 | private IntArrayTag parseIntArrayTag() throws ParseException {
204 | List intList = new ArrayList<>();
205 | while (ptr.currentChar() != ']') {
206 | String s = ptr.parseSimpleString();
207 | ptr.skipWhitespace();
208 | if (NUMBER_PATTERN.matcher(s).matches()) {
209 | try {
210 | intList.add(Integer.parseInt(s));
211 | } catch (NumberFormatException ex) {
212 | throw ptr.parseException("int not in range: \"" + s + "\"");
213 | }
214 | } else {
215 | throw ptr.parseException("invalid int in IntArrayTag: \"" + s + "\"");
216 | }
217 | if (!ptr.nextArrayElement()) {
218 | break;
219 | }
220 | }
221 | ptr.expectChar(']');
222 | return new IntArrayTag(intList.stream().mapToInt(i -> i).toArray());
223 | }
224 |
225 | private LongArrayTag parseLongArrayTag() throws ParseException {
226 | List longList = new ArrayList<>();
227 | while (ptr.currentChar() != ']') {
228 | String s = ptr.parseSimpleString();
229 | ptr.skipWhitespace();
230 | if (NUMBER_PATTERN.matcher(s).matches()) {
231 | try {
232 | longList.add(Long.parseLong(s));
233 | } catch (NumberFormatException ex) {
234 | throw ptr.parseException("long not in range: \"" + s + "\"");
235 | }
236 | } else {
237 | throw ptr.parseException("invalid long in LongArrayTag: \"" + s + "\"");
238 | }
239 | if (!ptr.nextArrayElement()) {
240 | break;
241 | }
242 | }
243 | ptr.expectChar(']');
244 | return new LongArrayTag(longList.stream().mapToLong(l -> l).toArray());
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/SNBTSerializer.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.io.IOException;
5 | import java.io.Writer;
6 |
7 | import net.sandrohc.schematic4j.nbt.StringSerializer;
8 | import net.sandrohc.schematic4j.nbt.tag.Tag;
9 |
10 | public class SNBTSerializer implements StringSerializer> {
11 |
12 | @Override
13 | public void toWriter(Tag> tag, Writer writer) throws IOException {
14 | SNBTWriter.write(tag, writer);
15 | }
16 |
17 | public void toWriter(Tag> tag, Writer writer, int maxDepth) throws IOException {
18 | SNBTWriter.write(tag, writer, maxDepth);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/SNBTUtil.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.io.IOException;
5 |
6 | import net.sandrohc.schematic4j.nbt.tag.Tag;
7 |
8 | public class SNBTUtil {
9 |
10 | public static String toSNBT(Tag> tag) throws IOException {
11 | return new SNBTSerializer().toString(tag);
12 | }
13 |
14 | public static Tag> fromSNBT(String string) throws IOException {
15 | return new SNBTDeserializer().fromString(string);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/SNBTWriter.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | import java.io.IOException;
5 | import java.io.Writer;
6 | import java.lang.reflect.Array;
7 | import java.util.Map;
8 | import java.util.regex.Pattern;
9 |
10 | import net.sandrohc.schematic4j.nbt.MaxDepthIO;
11 | import net.sandrohc.schematic4j.nbt.tag.ByteArrayTag;
12 | import net.sandrohc.schematic4j.nbt.tag.ByteTag;
13 | import net.sandrohc.schematic4j.nbt.tag.CompoundTag;
14 | import net.sandrohc.schematic4j.nbt.tag.DoubleTag;
15 | import net.sandrohc.schematic4j.nbt.tag.EndTag;
16 | import net.sandrohc.schematic4j.nbt.tag.FloatTag;
17 | import net.sandrohc.schematic4j.nbt.tag.IntArrayTag;
18 | import net.sandrohc.schematic4j.nbt.tag.IntTag;
19 | import net.sandrohc.schematic4j.nbt.tag.ListTag;
20 | import net.sandrohc.schematic4j.nbt.tag.LongArrayTag;
21 | import net.sandrohc.schematic4j.nbt.tag.LongTag;
22 | import net.sandrohc.schematic4j.nbt.tag.ShortTag;
23 | import net.sandrohc.schematic4j.nbt.tag.StringTag;
24 | import net.sandrohc.schematic4j.nbt.tag.Tag;
25 |
26 | /**
27 | * SNBTWriter creates an SNBT String.
28 | *
29 | * */
30 | public final class SNBTWriter implements MaxDepthIO {
31 |
32 | private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z_.+\\-]+");
33 |
34 | private Writer writer;
35 |
36 | private SNBTWriter(Writer writer) {
37 | this.writer = writer;
38 | }
39 |
40 | public static void write(Tag> tag, Writer writer, int maxDepth) throws IOException {
41 | new SNBTWriter(writer).writeAnything(tag, maxDepth);
42 | }
43 |
44 | public static void write(Tag> tag, Writer writer) throws IOException {
45 | write(tag, writer, Tag.DEFAULT_MAX_DEPTH);
46 | }
47 |
48 | private void writeAnything(Tag> tag, int maxDepth) throws IOException {
49 | switch (tag.getID()) {
50 | case EndTag.ID:
51 | //do nothing
52 | break;
53 | case ByteTag.ID:
54 | writer.append(Byte.toString(((ByteTag) tag).asByte())).write('b');
55 | break;
56 | case ShortTag.ID:
57 | writer.append(Short.toString(((ShortTag) tag).asShort())).write('s');
58 | break;
59 | case IntTag.ID:
60 | writer.write(Integer.toString(((IntTag) tag).asInt()));
61 | break;
62 | case LongTag.ID:
63 | writer.append(Long.toString(((LongTag) tag).asLong())).write('l');
64 | break;
65 | case FloatTag.ID:
66 | writer.append(Float.toString(((FloatTag) tag).asFloat())).write('f');
67 | break;
68 | case DoubleTag.ID:
69 | writer.append(Double.toString(((DoubleTag) tag).asDouble())).write('d');
70 | break;
71 | case ByteArrayTag.ID:
72 | writeArray(((ByteArrayTag) tag).getValue(), ((ByteArrayTag) tag).length(), "B");
73 | break;
74 | case StringTag.ID:
75 | writer.write(escapeString(((StringTag) tag).getValue()));
76 | break;
77 | case ListTag.ID:
78 | writer.write('[');
79 | for (int i = 0; i < ((ListTag>) tag).size(); i++) {
80 | writer.write(i == 0 ? "" : ",");
81 | writeAnything(((ListTag>) tag).get(i), decrementMaxDepth(maxDepth));
82 | }
83 | writer.write(']');
84 | break;
85 | case CompoundTag.ID:
86 | writer.write('{');
87 | boolean first = true;
88 | for (Map.Entry> entry : (CompoundTag) tag) {
89 | writer.write(first ? "" : ",");
90 | writer.append(escapeString(entry.getKey())).write(':');
91 | writeAnything(entry.getValue(), decrementMaxDepth(maxDepth));
92 | first = false;
93 | }
94 | writer.write('}');
95 | break;
96 | case IntArrayTag.ID:
97 | writeArray(((IntArrayTag) tag).getValue(), ((IntArrayTag) tag).length(), "I");
98 | break;
99 | case LongArrayTag.ID:
100 | writeArray(((LongArrayTag) tag).getValue(), ((LongArrayTag) tag).length(), "L");
101 | break;
102 | default:
103 | throw new IOException("unknown tag with id \"" + tag.getID() + "\"");
104 | }
105 | }
106 |
107 | private void writeArray(Object array, int length, String prefix) throws IOException {
108 | writer.append('[').append(prefix).write(';');
109 | for (int i = 0; i < length; i++) {
110 | writer.append(i == 0 ? "" : ",").write(Array.get(array, i).toString());
111 | }
112 | writer.write(']');
113 | }
114 |
115 | public static String escapeString(String s) {
116 | if (!NON_QUOTE_PATTERN.matcher(s).matches()) {
117 | StringBuilder sb = new StringBuilder();
118 | sb.append('"');
119 | for (int i = 0; i < s.length(); i++) {
120 | char c = s.charAt(i);
121 | if (c == '\\' || c == '"') {
122 | sb.append('\\');
123 | }
124 | sb.append(c);
125 | }
126 | sb.append('"');
127 | return sb.toString();
128 | }
129 | return s;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/io/StringPointer.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.io;
3 |
4 | public class StringPointer {
5 |
6 | private String value;
7 | private int index;
8 |
9 | public StringPointer(String value) {
10 | this.value = value;
11 | }
12 |
13 | public String parseSimpleString() {
14 | int oldIndex = index;
15 | while (hasNext() && isSimpleChar(currentChar())) {
16 | index++;
17 | }
18 | return value.substring(oldIndex, index);
19 | }
20 |
21 | public String parseQuotedString() throws ParseException {
22 | int oldIndex = ++index; //ignore beginning quotes
23 | StringBuilder sb = null;
24 | boolean escape = false;
25 | while (hasNext()) {
26 | char c = next();
27 | if (escape) {
28 | if (c != '\\' && c != '"') {
29 | throw parseException("invalid escape of '" + c + "'");
30 | }
31 | escape = false;
32 | } else {
33 | if (c == '\\') { //escape
34 | escape = true;
35 | if (sb != null) {
36 | continue;
37 | }
38 | sb = new StringBuilder(value.substring(oldIndex, index - 1));
39 | continue;
40 | }
41 | if (c == '"') {
42 | return sb == null ? value.substring(oldIndex, index - 1) : sb.toString();
43 | }
44 | }
45 | if (sb != null) {
46 | sb.append(c);
47 | }
48 | }
49 | throw parseException("missing end quote");
50 | }
51 |
52 | public boolean nextArrayElement() {
53 | skipWhitespace();
54 | if (hasNext() && currentChar() == ',') {
55 | index++;
56 | skipWhitespace();
57 | return true;
58 | }
59 | return false;
60 | }
61 |
62 | public void expectChar(char c) throws ParseException {
63 | skipWhitespace();
64 | boolean hasNext = hasNext();
65 | if (hasNext && currentChar() == c) {
66 | index++;
67 | return;
68 | }
69 | throw parseException("expected '" + c + "' but got " + (hasNext ? "'" + currentChar() + "'" : "EOF"));
70 | }
71 |
72 | public void skipWhitespace() {
73 | while (hasNext() && Character.isWhitespace(currentChar())) {
74 | index++;
75 | }
76 | }
77 |
78 | public boolean hasNext() {
79 | return index < value.length();
80 | }
81 |
82 | public boolean hasCharsLeft(int num) {
83 | return this.index + num < value.length();
84 | }
85 |
86 | public char currentChar() {
87 | return value.charAt(index);
88 | }
89 |
90 | public char next() {
91 | return value.charAt(index++);
92 | }
93 |
94 | public void skip(int offset) {
95 | index += offset;
96 | }
97 |
98 | public char lookAhead(int offset) {
99 | return value.charAt(index + offset);
100 | }
101 |
102 | private static boolean isSimpleChar(char c) {
103 | return c >= 'a' && c <= 'z'
104 | || c >= 'A' && c <= 'Z'
105 | || c >= '0' && c <= '9'
106 | || c == '-'
107 | || c == '+'
108 | || c == '.'
109 | || c == '_';
110 | }
111 |
112 | public ParseException parseException(String msg) {
113 | return new ParseException(msg, value, index);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/ArrayTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.lang.reflect.Array;
5 |
6 | /**
7 | * ArrayTag is an abstract representation of any NBT array tag.
8 | * For implementations see {@link ByteArrayTag}, {@link IntArrayTag}, {@link LongArrayTag}.
9 | * @param The array type.
10 | * */
11 | public abstract class ArrayTag extends Tag {
12 | /**
13 | * An array tag.
14 | * @param value The inner array
15 | */
16 | public ArrayTag(T value) {
17 | super(value);
18 | if (!value.getClass().isArray()) {
19 | throw new UnsupportedOperationException("type of array tag must be an array");
20 | }
21 | }
22 |
23 | /**
24 | * Get ghe array length, or size.
25 | * @return The array length
26 | */
27 | public int length() {
28 | return Array.getLength(getValue());
29 | }
30 |
31 | @Override
32 | public T getValue() {
33 | return super.getValue();
34 | }
35 |
36 | @Override
37 | public void setValue(T value) {
38 | super.setValue(value);
39 | }
40 |
41 | @Override
42 | public String valueToString(int maxDepth) {
43 | return arrayToString("", "");
44 | }
45 |
46 | /**
47 | * @param prefix The item prefix
48 | * @param suffix The item suffix
49 | * @return The generated string
50 | */
51 | protected String arrayToString(String prefix, String suffix) {
52 | StringBuilder sb = new StringBuilder("[").append(prefix).append("".equals(prefix) ? "" : ";");
53 | for (int i = 0; i < length(); i++) {
54 | sb.append(i == 0 ? "" : ",").append(Array.get(getValue(), i)).append(suffix);
55 | }
56 | sb.append("]");
57 | return sb.toString();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/ByteArrayTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.util.Arrays;
5 |
6 | /**
7 | * A byte array NBT tag.
8 | */
9 | public class ByteArrayTag extends ArrayTag implements Comparable {
10 |
11 | /**
12 | * The byte array tag discriminator.
13 | */
14 | public static final byte ID = 7;
15 |
16 | /**
17 | * The default value.
18 | */
19 | public static final byte[] ZERO_VALUE = new byte[0];
20 |
21 | /**
22 | * An empty byte array tag.
23 | */
24 | public ByteArrayTag() {
25 | super(ZERO_VALUE);
26 | }
27 |
28 | /**
29 | * A byte array tag.
30 | * @param value The inner array
31 | */
32 | public ByteArrayTag(byte[] value) {
33 | super(value);
34 | }
35 |
36 | @Override
37 | public byte getID() {
38 | return ID;
39 | }
40 |
41 | @Override
42 | public boolean equals(Object other) {
43 | return super.equals(other) && Arrays.equals(getValue(), ((ByteArrayTag) other).getValue());
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | return Arrays.hashCode(getValue());
49 | }
50 |
51 | @Override
52 | public int compareTo(ByteArrayTag other) {
53 | return Integer.compare(length(), other.length());
54 | }
55 |
56 | @Override
57 | public ByteArrayTag clone() {
58 | return new ByteArrayTag(Arrays.copyOf(getValue(), length()));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/ByteTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A byte NBT tag.
6 | */
7 | public class ByteTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The byte tag discriminator.
11 | */
12 | public static final byte ID = 1;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final byte ZERO_VALUE = 0;
18 |
19 | /**
20 | * A byte tag with the default value.
21 | */
22 | public ByteTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A byte tag.
28 | * @param value The inner value
29 | */
30 | public ByteTag(byte value) {
31 | super(value);
32 | }
33 |
34 | /**
35 | * A byte tag.
36 | * @param value The inner value
37 | */
38 | public ByteTag(boolean value) {
39 | super((byte) (value ? 1 : 0));
40 | }
41 |
42 | @Override
43 | public byte getID() {
44 | return ID;
45 | }
46 |
47 | /**
48 | * Convert this byte into a boolean value. Values greater than zero map to true.
49 | *
50 | * @return {@code true} if greater than 0, {@code false} otherwise
51 | */
52 | public boolean asBoolean() {
53 | return getValue() > 0;
54 | }
55 |
56 | /**
57 | * Sets the inner byte value.
58 | *
59 | * @param value The new value
60 | */
61 | public void setValue(byte value) {
62 | super.setValue(value);
63 | }
64 |
65 | @Override
66 | public boolean equals(Object other) {
67 | return super.equals(other) && asByte() == ((ByteTag) other).asByte();
68 | }
69 |
70 | @Override
71 | public int compareTo(ByteTag other) {
72 | return getValue().compareTo(other.getValue());
73 | }
74 |
75 | @Override
76 | public ByteTag clone() {
77 | return new ByteTag(getValue());
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/DoubleTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A double NBT tag.
6 | */
7 | public class DoubleTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The double tag discriminator.
11 | */
12 | public static final byte ID = 6;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final double ZERO_VALUE = 0.0D;
18 |
19 | /**
20 | * A double tag with the default value.
21 | */
22 | public DoubleTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A double tag.
28 | *
29 | * @param value The inner value
30 | */
31 | public DoubleTag(double value) {
32 | super(value);
33 | }
34 |
35 | @Override
36 | public byte getID() {
37 | return ID;
38 | }
39 |
40 | /**
41 | * Set a new value.
42 | *
43 | * @param value The new value
44 | */
45 | public void setValue(double value) {
46 | super.setValue(value);
47 | }
48 |
49 | @Override
50 | public boolean equals(Object other) {
51 | return super.equals(other) && getValue().equals(((DoubleTag) other).getValue());
52 | }
53 |
54 | @Override
55 | public int compareTo(DoubleTag other) {
56 | return getValue().compareTo(other.getValue());
57 | }
58 |
59 | @Override
60 | public DoubleTag clone() {
61 | return new DoubleTag(getValue());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/EndTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * An end NBT tag. Used to represent the lack of value, like a {@code null}.
6 | */
7 | public final class EndTag extends Tag {
8 |
9 | /**
10 | * The end tag discriminator.
11 | */
12 | public static final byte ID = 0;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final EndTag INSTANCE = new EndTag();
18 |
19 | /**
20 | * An end tag.
21 | */
22 | private EndTag() {
23 | super(null);
24 | }
25 |
26 | @Override
27 | public byte getID() {
28 | return ID;
29 | }
30 |
31 | @Override
32 | protected Void checkValue(Void value) {
33 | return value;
34 | }
35 |
36 | @Override
37 | public String valueToString(int maxDepth) {
38 | return "\"end\"";
39 | }
40 |
41 | @Override
42 | public EndTag clone() {
43 | return INSTANCE;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/FloatTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A float NBT tag.
6 | */
7 | public class FloatTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The float tag discriminator.
11 | */
12 | public static final byte ID = 5;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final float ZERO_VALUE = 0.0F;
18 |
19 | /**
20 | * A float tag with the default value.
21 | */
22 | public FloatTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A float tag.
28 | * @param value The inner value
29 | */
30 | public FloatTag(float value) {
31 | super(value);
32 | }
33 |
34 | @Override
35 | public byte getID() {
36 | return ID;
37 | }
38 |
39 | /**
40 | * Set a new value.
41 | *
42 | * @param value The new value
43 | */
44 | public void setValue(float value) {
45 | super.setValue(value);
46 | }
47 |
48 | @Override
49 | public boolean equals(Object other) {
50 | return super.equals(other) && getValue().equals(((FloatTag) other).getValue());
51 | }
52 |
53 | @Override
54 | public int compareTo(FloatTag other) {
55 | return getValue().compareTo(other.getValue());
56 | }
57 |
58 | @Override
59 | public FloatTag clone() {
60 | return new FloatTag(getValue());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/IntArrayTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.util.Arrays;
5 |
6 | /**
7 | * An int array NBT tag.
8 | */
9 | public class IntArrayTag extends ArrayTag implements Comparable {
10 |
11 | /**
12 | * The int array tag discriminator.
13 | */
14 | public static final byte ID = 11;
15 |
16 | /**
17 | * The default value.
18 | */
19 | public static final int[] ZERO_VALUE = new int[0];
20 |
21 | /**
22 | * An empty int array tag.
23 | */
24 | public IntArrayTag() {
25 | super(ZERO_VALUE);
26 | }
27 |
28 | /**
29 | * An int array tag.
30 | * @param value The inner value
31 | */
32 | public IntArrayTag(int[] value) {
33 | super(value);
34 | }
35 |
36 | @Override
37 | public byte getID() {
38 | return ID;
39 | }
40 |
41 | @Override
42 | public boolean equals(Object other) {
43 | return super.equals(other) && Arrays.equals(getValue(), ((IntArrayTag) other).getValue());
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | return Arrays.hashCode(getValue());
49 | }
50 |
51 | @Override
52 | public int compareTo(IntArrayTag other) {
53 | return Integer.compare(length(), other.length());
54 | }
55 |
56 | @Override
57 | public IntArrayTag clone() {
58 | return new IntArrayTag(Arrays.copyOf(getValue(), length()));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/IntTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * An int NBT tag.
6 | */
7 | public class IntTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The int tag discriminator.
11 | */
12 | public static final byte ID = 3;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final int ZERO_VALUE = 0;
18 |
19 | /**
20 | * An int tag with the default value.
21 | */
22 | public IntTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * An int tag.
28 | * @param value The inner value
29 | */
30 | public IntTag(int value) {
31 | super(value);
32 | }
33 |
34 | @Override
35 | public byte getID() {
36 | return ID;
37 | }
38 |
39 | /**
40 | * Set a new value.
41 | *
42 | * @param value The new value
43 | */
44 | public void setValue(int value) {
45 | super.setValue(value);
46 | }
47 |
48 | @Override
49 | public boolean equals(Object other) {
50 | return super.equals(other) && asInt() == ((IntTag) other).asInt();
51 | }
52 |
53 | @Override
54 | public int compareTo(IntTag other) {
55 | return getValue().compareTo(other.getValue());
56 | }
57 |
58 | @Override
59 | public IntTag clone() {
60 | return new IntTag(getValue());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/LongArrayTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.util.Arrays;
5 |
6 | /**
7 | * A long array NBT tag.
8 | */
9 | public class LongArrayTag extends ArrayTag implements Comparable {
10 |
11 | /**
12 | * The long array tag discriminator.
13 | */
14 | public static final byte ID = 12;
15 |
16 | /**
17 | * The default value.
18 | */
19 | public static final long[] ZERO_VALUE = new long[0];
20 |
21 | /**
22 | * An empty long array tag.
23 | */
24 | public LongArrayTag() {
25 | super(ZERO_VALUE);
26 | }
27 |
28 | /**
29 | * A long array tag.
30 | * @param value The inner value
31 | */
32 | public LongArrayTag(long[] value) {
33 | super(value);
34 | }
35 |
36 | @Override
37 | public byte getID() {
38 | return ID;
39 | }
40 |
41 | @Override
42 | public boolean equals(Object other) {
43 | return super.equals(other) && Arrays.equals(getValue(), ((LongArrayTag) other).getValue());
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | return Arrays.hashCode(getValue());
49 | }
50 |
51 | @Override
52 | public int compareTo(LongArrayTag other) {
53 | return Integer.compare(length(), other.length());
54 | }
55 |
56 | @Override
57 | public LongArrayTag clone() {
58 | return new LongArrayTag(Arrays.copyOf(getValue(), length()));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/LongTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A long NBT tag.
6 | */
7 | public class LongTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The long tag discriminator.
11 | */
12 | public static final byte ID = 4;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final long ZERO_VALUE = 0L;
18 |
19 | /**
20 | * A long tag with the default value.
21 | */
22 | public LongTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A long tag.
28 | * @param value The inner value
29 | */
30 | public LongTag(long value) {
31 | super(value);
32 | }
33 |
34 | @Override
35 | public byte getID() {
36 | return ID;
37 | }
38 |
39 | /**
40 | * Set a new value.
41 | *
42 | * @param value The new value
43 | */
44 | public void setValue(long value) {
45 | super.setValue(value);
46 | }
47 |
48 | @Override
49 | public boolean equals(Object other) {
50 | return super.equals(other) && asLong() == ((LongTag) other).asLong();
51 | }
52 |
53 | @Override
54 | public int compareTo(LongTag other) {
55 | return getValue().compareTo(other.getValue());
56 | }
57 |
58 | @Override
59 | public LongTag clone() {
60 | return new LongTag(getValue());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/NonNullEntrySet.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.util.Collection;
5 | import java.util.Iterator;
6 | import java.util.Map;
7 | import java.util.Set;
8 |
9 | import org.checkerframework.checker.nullness.qual.NonNull;
10 |
11 | /**
12 | * A decorator for the Set returned by CompoundTag#entrySet()
13 | * that disallows setting null values.
14 | * */
15 | class NonNullEntrySet implements Set> {
16 |
17 | /**
18 | * The inner set.
19 | */
20 | private final Set> set;
21 |
22 | NonNullEntrySet(Set> set) {
23 | this.set = set;
24 | }
25 |
26 | @Override
27 | public int size() {
28 | return set.size();
29 | }
30 |
31 | @Override
32 | public boolean isEmpty() {
33 | return set.isEmpty();
34 | }
35 |
36 | @Override
37 | public boolean contains(Object o) {
38 | return set.contains(o);
39 | }
40 |
41 | @Override
42 | public @NonNull Iterator> iterator() {
43 | return new NonNullEntrySetIterator(set.iterator());
44 | }
45 |
46 | @Override
47 | public Object @NonNull [] toArray() {
48 | return set.toArray();
49 | }
50 |
51 | @Override
52 | public T @NonNull [] toArray(T @NonNull [] a) {
53 | return set.toArray(a);
54 | }
55 |
56 | @Override
57 | public boolean add(Map.Entry kvEntry) {
58 | return set.add(kvEntry);
59 | }
60 |
61 | @Override
62 | public boolean remove(Object o) {
63 | return set.remove(o);
64 | }
65 |
66 | @Override
67 | public boolean containsAll(@NonNull Collection> c) {
68 | return set.containsAll(c);
69 | }
70 |
71 | @Override
72 | public boolean addAll(@NonNull Collection extends Map.Entry> c) {
73 | return set.addAll(c);
74 | }
75 |
76 | @Override
77 | public boolean retainAll(@NonNull Collection> c) {
78 | return set.retainAll(c);
79 | }
80 |
81 | @Override
82 | public boolean removeAll(@NonNull Collection> c) {
83 | return set.removeAll(c);
84 | }
85 |
86 | @Override
87 | public void clear() {
88 | set.clear();
89 | }
90 |
91 | class NonNullEntrySetIterator implements Iterator> {
92 |
93 | private final Iterator> iterator;
94 |
95 | NonNullEntrySetIterator(Iterator> iterator) {
96 | this.iterator = iterator;
97 | }
98 |
99 | @Override
100 | public boolean hasNext() {
101 | return iterator.hasNext();
102 | }
103 |
104 | @Override
105 | public Map.Entry next() {
106 | return new NonNullEntry(iterator.next());
107 | }
108 | }
109 |
110 | class NonNullEntry implements Map.Entry {
111 |
112 | private final Map.Entry entry;
113 |
114 | NonNullEntry(Map.Entry entry) {
115 | this.entry = entry;
116 | }
117 |
118 | @Override
119 | public K getKey() {
120 | return entry.getKey();
121 | }
122 |
123 | @Override
124 | public V getValue() {
125 | return entry.getValue();
126 | }
127 |
128 | @Override
129 | public V setValue(V value) {
130 | if (value == null) {
131 | throw new NullPointerException(getClass().getSimpleName() + " does not allow setting null");
132 | }
133 | return entry.setValue(value);
134 | }
135 |
136 | @Override
137 | public boolean equals(Object o) {
138 | return entry.equals(o);
139 | }
140 |
141 | @Override
142 | public int hashCode() {
143 | return entry.hashCode();
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/NumberTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A generic numeric NBT tag.
6 | *
7 | * @see ByteTag
8 | * @see ShortTag
9 | * @see IntTag
10 | * @see LongTag
11 | * @see FloatTag
12 | * @see DoubleTag
13 | */
14 | public abstract class NumberTag> extends Tag {
15 |
16 | /**
17 | * A number tag.
18 | *
19 | * @param value The inner value
20 | */
21 | public NumberTag(T value) {
22 | super(value);
23 | }
24 |
25 | public byte asByte() {
26 | return getValue().byteValue();
27 | }
28 |
29 | public short asShort() {
30 | return getValue().shortValue();
31 | }
32 |
33 | public int asInt() {
34 | return getValue().intValue();
35 | }
36 |
37 | public long asLong() {
38 | return getValue().longValue();
39 | }
40 |
41 | public float asFloat() {
42 | return getValue().floatValue();
43 | }
44 |
45 | public double asDouble() {
46 | return getValue().doubleValue();
47 | }
48 |
49 | @Override
50 | public String valueToString(int maxDepth) {
51 | return getValue().toString();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/ShortTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A short NBT tag.
6 | */
7 | public class ShortTag extends NumberTag implements Comparable {
8 |
9 | /**
10 | * The short tag discriminator.
11 | */
12 | public static final byte ID = 2;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final short ZERO_VALUE = 0;
18 |
19 | /**
20 | * A short tag with the default value.
21 | */
22 | public ShortTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A short tag.
28 | * @param value The inner value
29 | */
30 | public ShortTag(short value) {
31 | super(value);
32 | }
33 |
34 | @Override
35 | public byte getID() {
36 | return ID;
37 | }
38 |
39 | /**
40 | * Set a new value.
41 | *
42 | * @param value The new value
43 | */
44 | public void setValue(short value) {
45 | super.setValue(value);
46 | }
47 |
48 | @Override
49 | public boolean equals(Object other) {
50 | return super.equals(other) && asShort() == ((ShortTag) other).asShort();
51 | }
52 |
53 | @Override
54 | public int compareTo(ShortTag other) {
55 | return getValue().compareTo(other.getValue());
56 | }
57 |
58 | @Override
59 | public ShortTag clone() {
60 | return new ShortTag(getValue());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/StringTag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | /**
5 | * A string NBT tag.
6 | */
7 | public class StringTag extends Tag implements Comparable {
8 |
9 | /**
10 | * The string tag discriminator.
11 | */
12 | public static final byte ID = 8;
13 |
14 | /**
15 | * The default value.
16 | */
17 | public static final String ZERO_VALUE = "";
18 |
19 | /**
20 | * An empty string tag.
21 | */
22 | public StringTag() {
23 | super(ZERO_VALUE);
24 | }
25 |
26 | /**
27 | * A string tag.
28 | * @param value The inner value
29 | */
30 | public StringTag(String value) {
31 | super(value);
32 | }
33 |
34 | @Override
35 | public byte getID() {
36 | return ID;
37 | }
38 |
39 | @Override
40 | public String getValue() {
41 | return super.getValue();
42 | }
43 |
44 | @Override
45 | public void setValue(String value) {
46 | super.setValue(value);
47 | }
48 |
49 | @Override
50 | public String valueToString(int maxDepth) {
51 | return escapeString(getValue(), false);
52 | }
53 |
54 | @Override
55 | public boolean equals(Object other) {
56 | return super.equals(other) && getValue().equals(((StringTag) other).getValue());
57 | }
58 |
59 | @Override
60 | public int compareTo(StringTag o) {
61 | return getValue().compareTo(o.getValue());
62 | }
63 |
64 | @Override
65 | public StringTag clone() {
66 | return new StringTag(getValue());
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/nbt/tag/Tag.java:
--------------------------------------------------------------------------------
1 | /* Vendored version of Quertz NBT 6.1 - https://github.com/Querz/NBT */
2 | package net.sandrohc.schematic4j.nbt.tag;
3 |
4 | import java.util.Collections;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Objects;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import net.sandrohc.schematic4j.nbt.MaxDepthReachedException;
12 |
13 | /**
14 | * Base class for all NBT tags.
15 | *
16 | *
Nesting
17 | *
All methods serializing instances or deserializing data track the nesting levels to prevent
18 | * circular references or malicious data which could, when deserialized, result in thousands
19 | * of instances causing a denial of service.
20 | *
21 | *
These methods have a parameter for the maximum nesting depth they are allowed to traverse. A
22 | * value of {@code 0} means that only the object itself, but no nested objects may be processed.
23 | * If an instance is nested further than allowed, a {@link MaxDepthReachedException} will be thrown.
24 | * Providing a negative maximum nesting depth will cause an {@code IllegalArgumentException}
25 | * to be thrown.
26 | *
27 | *
Some methods do not provide a parameter to specify the maximum nesting depth, but instead use
28 | * {@link #DEFAULT_MAX_DEPTH}, which is also the maximum used by Minecraft. This is documented for
29 | * the respective methods.
30 | *
31 | *
If custom NBT tags contain objects other than NBT tags, which can be nested as well, then there
32 | * is no guarantee that {@code MaxDepthReachedException}s are thrown for them. The respective class
33 | * will document this behavior accordingly.
34 | *
35 | * @param The type of the contained value
36 | * */
37 | public abstract class Tag implements Cloneable {
38 |
39 | /**
40 | * The default maximum depth of the NBT structure.
41 | * */
42 | public static final int DEFAULT_MAX_DEPTH = 512;
43 |
44 | /**
45 | * Map of characters and their escaped counterparts.
46 | */
47 | private static final Map ESCAPE_CHARACTERS;
48 | static {
49 | final Map temp = new HashMap<>();
50 | temp.put("\\", "\\\\\\\\");
51 | temp.put("\n", "\\\\n");
52 | temp.put("\t", "\\\\t");
53 | temp.put("\r", "\\\\r");
54 | temp.put("\"", "\\\\\"");
55 | ESCAPE_CHARACTERS = Collections.unmodifiableMap(temp);
56 | }
57 |
58 | private static final Pattern ESCAPE_PATTERN = Pattern.compile("[\\\\\n\t\r\"]");
59 | private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z0-9_\\-+]+");
60 |
61 | private T value;
62 |
63 | /**
64 | * Initializes this Tag with some value. If the value is {@code null}, it will
65 | * throw a {@code NullPointerException}
66 | * @param value The value to be set for this Tag.
67 | * */
68 | public Tag(T value) {
69 | setValue(value);
70 | }
71 |
72 | /**
73 | * @return This Tag's ID, usually used for serialization and deserialization.
74 | * */
75 | public abstract byte getID();
76 |
77 | /**
78 | * @return The value of this Tag.
79 | * */
80 | protected T getValue() {
81 | return value;
82 | }
83 |
84 | /**
85 | * Sets the value for this Tag directly.
86 | * @param value The value to be set.
87 | * @throws NullPointerException If the value is null
88 | * */
89 | protected void setValue(T value) {
90 | this.value = checkValue(value);
91 | }
92 |
93 | /**
94 | * Checks if the value {@code value} is {@code null}.
95 | * @param value The value to check
96 | * @throws NullPointerException If {@code value} was {@code null}
97 | * @return The parameter {@code value}
98 | * */
99 | protected T checkValue(T value) {
100 | return Objects.requireNonNull(value);
101 | }
102 |
103 | /**
104 | * Calls {@link Tag#toString(int)} with an initial depth of {@code 0}.
105 | * @see Tag#toString(int)
106 | * @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
107 | * */
108 | @Override
109 | public final String toString() {
110 | return toString(DEFAULT_MAX_DEPTH);
111 | }
112 |
113 | /**
114 | * Creates a string representation of this Tag in a valid JSON format.
115 | * @param maxDepth The maximum nesting depth.
116 | * @return The string representation of this Tag.
117 | * @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
118 | * */
119 | public String toString(int maxDepth) {
120 | return "{\"type\":\""+ getClass().getSimpleName() + "\"," +
121 | "\"value\":" + valueToString(maxDepth) + "}";
122 | }
123 |
124 | /**
125 | * Calls {@link Tag#valueToString(int)} with {@link Tag#DEFAULT_MAX_DEPTH}.
126 | * @return The string representation of the value of this Tag.
127 | * @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
128 | * */
129 | public String valueToString() {
130 | return valueToString(DEFAULT_MAX_DEPTH);
131 | }
132 |
133 | /**
134 | * Returns a JSON representation of the value of this Tag.
135 | * @param maxDepth The maximum nesting depth.
136 | * @return The string representation of the value of this Tag.
137 | * @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
138 | * */
139 | public abstract String valueToString(int maxDepth);
140 |
141 | /**
142 | * Returns whether this Tag and some other Tag are equal.
143 | * They are equal if {@code other} is not {@code null} and they are of the same class.
144 | * Custom Tag implementations should overwrite this but check the result
145 | * of this {@code super}-method while comparing.
146 | * @param other The Tag to compare to.
147 | * @return {@code true} if they are equal based on the conditions mentioned above.
148 | * */
149 | @Override
150 | public boolean equals(Object other) {
151 | return other != null && getClass() == other.getClass();
152 | }
153 |
154 | /**
155 | * Calculates the hash code of this Tag. Tags which are equal according to {@link Tag#equals(Object)}
156 | * must return an equal hash code.
157 | * @return The hash code of this Tag.
158 | * */
159 | @Override
160 | public int hashCode() {
161 | return value.hashCode();
162 | }
163 |
164 | /**
165 | * Creates a clone of this Tag.
166 | * @return A clone of this Tag.
167 | * */
168 | public abstract Tag clone();
169 |
170 | /**
171 | * Escapes a string to fit into a JSON-like string representation for Minecraft
172 | * or to create the JSON string representation of a Tag returned from {@link Tag#toString()}
173 | * @param s The string to be escaped.
174 | * @param lenient {@code true} if it should force double quotes ({@code "}) at the start and
175 | * the end of the string.
176 | * @return The escaped string.
177 | * */
178 | protected static String escapeString(String s, boolean lenient) {
179 | StringBuffer sb = new StringBuffer();
180 | Matcher m = ESCAPE_PATTERN.matcher(s);
181 | while (m.find()) {
182 | m.appendReplacement(sb, ESCAPE_CHARACTERS.get(m.group()));
183 | }
184 | m.appendTail(sb);
185 | m = NON_QUOTE_PATTERN.matcher(s);
186 | if (!lenient || !m.matches()) {
187 | sb.insert(0, "\"").append("\"");
188 | }
189 | return sb.toString();
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/parser/Parser.java:
--------------------------------------------------------------------------------
1 | package net.sandrohc.schematic4j.parser;
2 |
3 | import org.checkerframework.checker.nullness.qual.NonNull;
4 | import org.checkerframework.checker.nullness.qual.Nullable;
5 |
6 | import net.sandrohc.schematic4j.exception.ParsingException;
7 | import net.sandrohc.schematic4j.nbt.tag.CompoundTag;
8 | import net.sandrohc.schematic4j.schematic.Schematic;
9 |
10 | /**
11 | * A schematic parser.
12 | */
13 | public interface Parser {
14 |
15 | /**
16 | * Parses the input NBT into a schematic.
17 | *
18 | * @param nbt The input NBT.
19 | * @return The parsed schematic.
20 | * @throws ParsingException In case there is a parsing error
21 | */
22 | @NonNull
23 | Schematic parse(@Nullable CompoundTag nbt) throws ParsingException;
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/net/sandrohc/schematic4j/parser/SchematicaParser.java:
--------------------------------------------------------------------------------
1 | package net.sandrohc.schematic4j.parser;
2 |
3 | import java.util.Map.Entry;
4 |
5 | import org.checkerframework.checker.nullness.qual.NonNull;
6 | import org.checkerframework.checker.nullness.qual.Nullable;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import net.sandrohc.schematic4j.exception.ParsingException;
11 | import net.sandrohc.schematic4j.nbt.tag.CompoundTag;
12 | import net.sandrohc.schematic4j.nbt.tag.ListTag;
13 | import net.sandrohc.schematic4j.nbt.tag.NumberTag;
14 | import net.sandrohc.schematic4j.nbt.tag.Tag;
15 | import net.sandrohc.schematic4j.schematic.Schematic;
16 | import net.sandrohc.schematic4j.schematic.SchematicaSchematic;
17 | import net.sandrohc.schematic4j.schematic.types.SchematicBlockEntity;
18 | import net.sandrohc.schematic4j.schematic.types.SchematicEntity;
19 | import net.sandrohc.schematic4j.schematic.types.SchematicItem;
20 |
21 | import static net.sandrohc.schematic4j.utils.TagUtils.getByte;
22 | import static net.sandrohc.schematic4j.utils.TagUtils.getByteArrayOrThrow;
23 | import static net.sandrohc.schematic4j.utils.TagUtils.getCompound;
24 | import static net.sandrohc.schematic4j.utils.TagUtils.getCompoundList;
25 | import static net.sandrohc.schematic4j.utils.TagUtils.getCompoundOrThrow;
26 | import static net.sandrohc.schematic4j.utils.TagUtils.getShort;
27 | import static net.sandrohc.schematic4j.utils.TagUtils.getString;
28 |
29 | /**
30 | * Parses Schematica files (.schematic).
31 | *
32 | */
33 | public class SchematicaSchematic implements Schematic {
34 |
35 | public static final String MATERIAL_CLASSIC = "Classic";
36 | public static final String MATERIAL_ALPHA = "Alpha";
37 | public static final String MATERIAL_STRUCTURE = "Structure";
38 |
39 | /**
40 | * The schematic width, the X axis.
41 | */
42 | public int width;
43 |
44 | /**
45 | * The schematic height, the Y axis.
46 | */
47 | public int height;
48 |
49 | /**
50 | * The schematic length, the Z axis.
51 | */
52 | public int length;
53 |
54 | /**
55 | * The unpacked list of block IDs.
56 | */
57 | public int @NonNull [] blockIds = new int[0];
58 |
59 | /**
60 | * The unpacked list of block metadata (used as discriminator before Minecraft's 1.7 block ID overhaul).
61 | */
62 | public int @NonNull [] blockMetadata = new int[0];
63 |
64 | /**
65 | * The unpacked list of blocks.
66 | */
67 | public String @NonNull [] blockPalette = new String[0];
68 |
69 | /**
70 | * The list of block/tile entities.
71 | */
72 | public @NonNull SchematicBlockEntity @NonNull [] blockEntities = new SchematicBlockEntity[0];
73 |
74 | /**
75 | * The list of entities.
76 | */
77 | public @NonNull SchematicEntity @NonNull [] entities = new SchematicEntity[0];
78 |
79 | /**
80 | * The schematic icon, if available.
81 | */
82 | public @Nullable SchematicItem icon;
83 |
84 | /**
85 | * The schematic materials, if available.
86 | *