Skip to content

Commit d2d40d1

Browse files
authored
Merge pull request #1242 from mworzala/feat/hetergeneous_list_improvements
Improved support for hetergeneous lists
2 parents 00c0e7c + 660c27c commit d2d40d1

File tree

6 files changed

+112
-6
lines changed

6 files changed

+112
-6
lines changed

nbt/src/main/java/net/kyori/adventure/nbt/ListBinaryTagImpl.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,19 @@ static BinaryTag unbox(final CompoundBinaryTag compound) {
280280
}
281281

282282
static CompoundBinaryTag box(final BinaryTag tag) {
283-
if (tag instanceof CompoundBinaryTag) {
284-
return (CompoundBinaryTag) tag;
285-
} else {
283+
if (needsBox(tag)) {
286284
return new CompoundBinaryTagImpl(Collections.singletonMap(WRAPPER_KEY, tag));
285+
} else {
286+
return (CompoundBinaryTag) tag;
287287
}
288288
}
289+
290+
private static boolean needsBox(final BinaryTag tag) {
291+
if (!(tag instanceof CompoundBinaryTag)) {
292+
return true;
293+
}
294+
295+
final CompoundBinaryTag compound = (CompoundBinaryTag) tag;
296+
return compound.size() == 1 && compound.get(WRAPPER_KEY) != null;
297+
}
289298
}

nbt/src/main/java/net/kyori/adventure/nbt/TagStringIO.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ public final class TagStringIO {
7171

7272
private final boolean acceptLegacy;
7373
private final boolean emitLegacy;
74+
private final boolean acceptHeterogeneousLists;
75+
private final boolean emitHeterogeneousLists;
7476
private final String indent;
7577

7678
private TagStringIO(final @NotNull Builder builder) {
7779
this.acceptLegacy = builder.acceptLegacy;
7880
this.emitLegacy = builder.emitLegacy;
81+
this.acceptHeterogeneousLists = builder.acceptHeterogeneousLists;
82+
this.emitHeterogeneousLists = builder.emitHeterogeneousLists;
7983
this.indent = builder.indent;
8084
}
8185

@@ -96,6 +100,7 @@ private TagStringIO(final @NotNull Builder builder) {
96100
final CharBuffer buffer = new CharBuffer(input);
97101
final TagStringReader parser = new TagStringReader(buffer);
98102
parser.legacy(this.acceptLegacy);
103+
parser.heterogeneousLists(this.acceptHeterogeneousLists);
99104
final CompoundBinaryTag tag = parser.compound();
100105
if (buffer.skipWhitespace().hasMore()) {
101106
throw new IOException("Document had trailing content after first CompoundTag");
@@ -206,6 +211,7 @@ private TagStringIO(final @NotNull Builder builder) {
206211
final StringBuilder sb = new StringBuilder();
207212
try (final TagStringWriter emit = new TagStringWriter(sb, this.indent)) {
208213
emit.legacy(this.emitLegacy);
214+
emit.heterogeneousLists(this.emitHeterogeneousLists);
209215
emit.writeTag(input);
210216
}
211217
return sb.toString();
@@ -240,6 +246,7 @@ public void toWriter(final @NotNull BinaryTag input, final @NotNull Writer dest)
240246
Objects.requireNonNull(dest, "dest");
241247
try (final TagStringWriter emit = new TagStringWriter(dest, this.indent)) {
242248
emit.legacy(this.emitLegacy);
249+
emit.heterogeneousLists(this.emitHeterogeneousLists);
243250
emit.writeTag(input);
244251
}
245252
}
@@ -252,6 +259,8 @@ public void toWriter(final @NotNull BinaryTag input, final @NotNull Writer dest)
252259
public static class Builder {
253260
private boolean acceptLegacy = true;
254261
private boolean emitLegacy = false;
262+
private boolean acceptHeterogeneousLists = false;
263+
private boolean emitHeterogeneousLists = false;
255264
private String indent = "";
256265

257266
Builder() {
@@ -326,6 +335,36 @@ public static class Builder {
326335
return this;
327336
}
328337

338+
/**
339+
* Configure whether or not the resulting IO configuration will accept heterogeneous lists.
340+
*
341+
* <p>Heterogeneous lists are lists that contain multiple types of tags, such as a list containing
342+
* both strings and integers.</p>
343+
*
344+
* @param heterogeneous whether to accept heterogeneous lists
345+
* @return this builder
346+
* @since 4.22.0
347+
*/
348+
public @NotNull Builder acceptHeterogeneousLists(final boolean heterogeneous) {
349+
this.acceptHeterogeneousLists = heterogeneous;
350+
return this;
351+
}
352+
353+
/**
354+
* Configure whether or not the resulting IO configuration will emit heterogeneous lists.
355+
*
356+
* <p>Heterogeneous lists are lists that contain multiple types of tags, such as a list containing
357+
* both strings and integers.</p>
358+
*
359+
* @param heterogeneous whether to emit heterogeneous lists
360+
* @return this builder
361+
* @since 4.22.0
362+
*/
363+
public @NotNull Builder emitHeterogeneousLists(final boolean heterogeneous) {
364+
this.emitHeterogeneousLists = heterogeneous;
365+
return this;
366+
}
367+
329368
/**
330369
* Create a new IO configuration from this builder.
331370
*

nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final class TagStringReader {
4040

4141
private final CharBuffer buffer;
4242
private boolean acceptLegacy;
43-
private boolean acceptHeterogenousLists;
43+
private boolean acceptHeterogeneousLists;
4444
private int depth;
4545

4646
TagStringReader(final CharBuffer buffer) {
@@ -64,7 +64,8 @@ public CompoundBinaryTag compound() throws StringTagParseException {
6464
}
6565

6666
public ListBinaryTag list() throws StringTagParseException {
67-
final ListBinaryTag.Builder<BinaryTag> builder = ListBinaryTag.builder();
67+
final ListBinaryTag.Builder<BinaryTag> builder = this.acceptHeterogeneousLists
68+
? ListBinaryTag.heterogeneousListBinaryTag() : ListBinaryTag.builder();
6869
this.buffer.expect(Tokens.ARRAY_BEGIN);
6970
final boolean prefixedIndex = this.acceptLegacy && this.buffer.peek() == '0' && this.buffer.peek(1) == ':';
7071
if (!prefixedIndex && this.buffer.takeIf(Tokens.ARRAY_END)) {
@@ -432,4 +433,8 @@ private static String unescape(final String withEscapes) {
432433
public void legacy(final boolean acceptLegacy) {
433434
this.acceptLegacy = acceptLegacy;
434435
}
436+
437+
public void heterogeneousLists(final boolean acceptHeterogeneousLists) {
438+
this.acceptHeterogeneousLists = acceptHeterogeneousLists;
439+
}
435440
}

nbt/src/main/java/net/kyori/adventure/nbt/TagStringWriter.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ final class TagStringWriter implements AutoCloseable {
4141
*/
4242
private boolean needsSeparator;
4343
private boolean legacy;
44+
private boolean heterogeneousLists;
4445

4546
TagStringWriter(final Appendable out, final String indent) {
4647
this.out = out;
@@ -52,6 +53,11 @@ public TagStringWriter legacy(final boolean legacy) {
5253
return this;
5354
}
5455

56+
public TagStringWriter heterogeneousLists(final boolean emitHeterogeneousLists) {
57+
this.heterogeneousLists = emitHeterogeneousLists;
58+
return this;
59+
}
60+
5561
// NBT-specific
5662

5763
public TagStringWriter writeTag(final BinaryTag tag) throws IOException {
@@ -96,7 +102,8 @@ private TagStringWriter writeCompound(final CompoundBinaryTag tag) throws IOExce
96102
return this;
97103
}
98104

99-
private TagStringWriter writeList(final ListBinaryTag tag) throws IOException {
105+
private TagStringWriter writeList(final ListBinaryTag rawTag) throws IOException {
106+
final ListBinaryTag tag = this.heterogeneousLists ? rawTag.unwrapHeterogeneity() : rawTag.wrapHeterogeneity();
100107
this.beginList();
101108
int idx = 0;
102109
final boolean lineBreaks = this.prettyPrinting() && this.breakListElement(tag.elementType());

nbt/src/test/java/net/kyori/adventure/nbt/ListBinaryTagTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,24 @@ void testBoxHeterogeneous() {
126126
assertEquals(expected, input.wrapHeterogeneity());
127127
}
128128

129+
@Test
130+
void testBoxHeterogeneousDoubleWrap() {
131+
// For backwards compatibility, wrapping a tag which is already {"": value} should wrap it again.
132+
final ListBinaryTag input = ListBinaryTag.listBinaryTag(
133+
BinaryTagTypes.LIST_WILDCARD,
134+
Arrays.asList(
135+
CompoundBinaryTag.from(Collections.singletonMap("", longBinaryTag(5))),
136+
StringBinaryTag.stringBinaryTag("five")
137+
)
138+
);
139+
final ListBinaryTag expected = ListBinaryTag.builder()
140+
.add(CompoundBinaryTag.from(Collections.singletonMap("", CompoundBinaryTag.from(Collections.singletonMap("", longBinaryTag(5))))))
141+
.add(CompoundBinaryTag.from(Collections.singletonMap("", stringBinaryTag("five"))))
142+
.build();
143+
144+
assertEquals(expected, input.wrapHeterogeneity());
145+
}
146+
129147
@Test
130148
void testBoxingReversible() {
131149
final ListBinaryTag input = ListBinaryTag.listBinaryTag(

nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,29 @@ void testLegacyListTag() throws IOException {
252252
assertEquals(builder.build(), tag);
253253
}
254254

255+
@Test
256+
void testHeterogeneousListTag() throws IOException {
257+
final String input = "[\"Tag #1\",2]";
258+
final BinaryTag tag = this.stringToTag(input, false, true);
259+
assertEquals("[{:\"Tag #1\"},{:2}]", this.tagToString(tag));
260+
final StringWriter output = new StringWriter();
261+
try (final TagStringWriter writer = new TagStringWriter(output, "").heterogeneousLists(true)) {
262+
writer.writeTag(tag);
263+
}
264+
assertEquals(input, output.toString());
265+
266+
final ListTagBuilder<BinaryTag> builder = new ListTagBuilder<>(true);
267+
builder.add(StringBinaryTag.stringBinaryTag("Tag #1"));
268+
builder.add(IntBinaryTag.intBinaryTag(2));
269+
assertEquals(builder.build(), tag);
270+
}
271+
272+
@Test
273+
void testFailHeterogeneousListTag() {
274+
final String input = "[\"Tag #1\",2]";
275+
assertThrows(IllegalArgumentException.class, () -> this.stringToTag(input, false, false));
276+
}
277+
255278
@Test
256279
void testReadsLegacyCompoundKey() throws IOException {
257280
final String input = "{test*compound: \"hello world\"}";
@@ -351,9 +374,14 @@ private BinaryTag stringToTag(final String input) throws StringTagParseException
351374
}
352375

353376
private BinaryTag stringToTag(final String input, final boolean acceptLegacy) throws StringTagParseException {
377+
return this.stringToTag(input, acceptLegacy, false);
378+
}
379+
380+
private BinaryTag stringToTag(final String input, final boolean acceptLegacy, final boolean acceptHeterogeneousLists) throws StringTagParseException {
354381
final CharBuffer buffer = new CharBuffer(input);
355382
final TagStringReader parser = new TagStringReader(buffer);
356383
parser.legacy(acceptLegacy);
384+
parser.heterogeneousLists(acceptHeterogeneousLists);
357385
final BinaryTag ret = parser.tag();
358386
if (buffer.skipWhitespace().hasMore()) {
359387
throw buffer.makeError("Trailing content after parse!");

0 commit comments

Comments
 (0)