Skip to content

Commit 3b40259

Browse files
committed
Add tag copy method
1 parent ca62dbe commit 3b40259

File tree

7 files changed

+128
-0
lines changed

7 files changed

+128
-0
lines changed

mapper/mapper-bukkit/src/main/java/com/saicone/nbt/mapper/BukkitTagMapper.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public class BukkitTagMapper implements TagMapper<Object> {
8282
private static final Class<?> TAG_TYPE;
8383

8484
private static final MethodHandle TAG_ID;
85+
private static final MethodHandle TAG_COPY;
8586
private static final MethodHandle TAG_SIZE;
8687

8788
private static final MethodHandle NEW_END;
@@ -227,6 +228,7 @@ public class BukkitTagMapper implements TagMapper<Object> {
227228
}
228229

229230
MethodHandle tag$id = null;
231+
MethodHandle tag$copy = null;
230232
MethodHandle tag$size = null;
231233
MethodHandle new$EndTag = null;
232234
MethodHandle new$ByteTag = null;
@@ -261,16 +263,20 @@ public class BukkitTagMapper implements TagMapper<Object> {
261263

262264
if (isMojangMapped) {
263265
tag$id = lookup.findVirtual(Tag, "getId", MethodType.methodType(byte.class));
266+
tag$copy = lookup.findVirtual(Tag, "copy", MethodType.methodType(Tag));
264267
if (version >= V_1_19_3) {
265268
tag$size = lookup.findVirtual(Tag, "sizeInBytes", MethodType.methodType(int.class));
266269
}
267270
} else if (version >= V_1_19_3) {
268271
tag$id = lookup.findVirtual(Tag, "b", MethodType.methodType(byte.class));
272+
tag$copy = lookup.findVirtual(Tag, "d", MethodType.methodType(Tag));
269273
tag$size = lookup.findVirtual(Tag, "a", MethodType.methodType(int.class));
270274
} else if (version >= V_1_18) {
271275
tag$id = lookup.findVirtual(Tag, "a", MethodType.methodType(byte.class));
276+
tag$copy = lookup.findVirtual(Tag, "c", MethodType.methodType(Tag));
272277
} else {
273278
tag$id = lookup.findVirtual(Tag, "getTypeId", MethodType.methodType(byte.class));
279+
tag$copy = lookup.findVirtual(Tag, "clone", MethodType.methodType(Tag));
274280
}
275281

276282
if (isMojangMapped && version >= V_1_15) {
@@ -437,6 +443,7 @@ public class BukkitTagMapper implements TagMapper<Object> {
437443
TAG_TYPE = Tag;
438444

439445
TAG_ID = tag$id;
446+
TAG_COPY = tag$copy;
440447
TAG_SIZE = tag$size;
441448

442449
NEW_END = new$EndTag;
@@ -710,6 +717,15 @@ public Object extract(@Nullable Object object) {
710717
}
711718
}
712719

720+
@Override
721+
public @NotNull Object copy(@NotNull Object o) {
722+
try {
723+
return TAG_COPY.invoke(o);
724+
} catch (Throwable t) {
725+
throw new RuntimeException(t);
726+
}
727+
}
728+
713729
@Override
714730
public int size(@Nullable Object object) {
715731
if (TAG_SIZE != null) {

mapper/mapper-cloudburst/src/main/java/com/saicone/nbt/mapper/CloudburstTagMapper.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
import org.jetbrains.annotations.NotNull;
1010
import org.jetbrains.annotations.Nullable;
1111

12+
import java.util.ArrayList;
13+
import java.util.Arrays;
1214
import java.util.Collection;
1315
import java.util.List;
1416
import java.util.Map;
17+
import java.util.stream.Collectors;
1518

1619
/**
1720
* TagMapper implementation to handle NBT values as CloudburstMC code abstraction.
@@ -50,6 +53,36 @@ public Object extract(@Nullable Object object) {
5053
return object;
5154
}
5255

56+
@Override
57+
public @NotNull Object copy(@NotNull Object object) {
58+
switch (typeId(object)) {
59+
case Tag.END:
60+
case Tag.BYTE:
61+
case Tag.SHORT:
62+
case Tag.INT:
63+
case Tag.LONG:
64+
case Tag.FLOAT:
65+
case Tag.DOUBLE:
66+
case Tag.STRING:
67+
// Immutable type
68+
return object;
69+
case Tag.BYTE_ARRAY:
70+
return Arrays.copyOf((byte[]) object, ((byte[]) object).length);
71+
case Tag.LIST:
72+
final List<Object> list = ((NbtList<?>) object).stream().map(this::copy).collect(Collectors.toCollection(ArrayList::new));
73+
return new NbtList(((NbtList<?>) object).getType(), list);
74+
case Tag.COMPOUND:
75+
final Map<String, Object> map = ((NbtMap) object).entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> copy(e.getValue())));
76+
return NbtMap.fromMap(map);
77+
case Tag.INT_ARRAY:
78+
return Arrays.copyOf((int[]) object, ((int[]) object).length);
79+
case Tag.LONG_ARRAY:
80+
return Arrays.copyOf((long[]) object, ((long[]) object).length);
81+
default:
82+
throw new IllegalArgumentException("Invalid tag type: " + object);
83+
}
84+
}
85+
5386
@Override
5487
@SuppressWarnings("unchecked")
5588
public int size(@Nullable Object object) {

mapper/mapper-jo-nbt/src/main/java/com/saicone/nbt/mapper/JoNbtTagMapper.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import se.llbit.nbt.StringTag;
2121
import se.llbit.nbt.Tag;
2222

23+
import java.util.ArrayList;
2324
import java.util.HashMap;
2425
import java.util.List;
2526
import java.util.Map;
27+
import java.util.stream.Collectors;
2628

2729
/**
2830
* TagMapper implementation to handle NBT values as jo-nbt code abstraction.
@@ -130,6 +132,21 @@ public Object extract(@Nullable Tag tag) {
130132
}
131133
}
132134

135+
@Override
136+
public @NotNull Tag copy(@NotNull Tag tag) {
137+
if (tag instanceof ListTag) {
138+
final List<SpecificTag> items = ((ListTag) tag).items.stream().map(t -> (SpecificTag) copy(t)).collect(Collectors.toCollection(ArrayList::new));
139+
return new ListTag(((ListTag) tag).getType(), items);
140+
} else if (tag instanceof CompoundTag) {
141+
final List<NamedTag> items = new ArrayList<>();
142+
for (NamedTag namedTag : tag.asCompound()) {
143+
items.add(new NamedTag(namedTag.name(), (SpecificTag) copy(namedTag.getTag())));
144+
}
145+
return new CompoundTag(items);
146+
}
147+
return TagMapper.super.copy(tag);
148+
}
149+
133150
@Override
134151
public int size(@Nullable Tag tag) {
135152
if (tag instanceof ListTag) {

mapper/mapper-minecraft/src/main/java/com/saicone/nbt/mapper/MinecraftTagMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ public Object extract(@Nullable Tag tag) {
169169
}
170170
}
171171

172+
@Override
173+
public @NotNull Tag copy(@NotNull Tag tag) {
174+
return tag.copy();
175+
}
176+
172177
@Override
173178
public int size(@Nullable Tag tag) {
174179
return tag == null ? EndTag.INSTANCE.sizeInBytes() : tag.sizeInBytes();

mapper/mapper-nukkit/src/main/java/com/saicone/nbt/mapper/NukkitTagMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ public Object extract(@Nullable Tag tag) {
149149
}
150150
}
151151

152+
@Override
153+
public @NotNull Tag copy(@NotNull Tag tag) {
154+
return tag.copy();
155+
}
156+
152157
@Override
153158
public @NotNull <A> TagType<A> type(@Nullable Tag tag) {
154159
return TagType.getType(typeId(tag));

mapper/mapper-vianbt/src/main/java/com/saicone/nbt/mapper/ViaNbtTagMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ public Object extract(@Nullable Tag tag) {
115115
}
116116
}
117117

118+
@Override
119+
public @NotNull Tag copy(@NotNull Tag tag) {
120+
return tag.copy();
121+
}
122+
118123
@Override
119124
public @NotNull <A> TagType<A> type(@Nullable Tag tag) {
120125
return tag == null ? TagType.getType(TagRegistry.END) : TagType.getType(tag.getTagId());

src/main/java/com/saicone/nbt/TagMapper.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Iterator;
99
import java.util.List;
1010
import java.util.Map;
11+
import java.util.stream.Collectors;
1112

1213
/**
1314
* <b>Tag Mapper</b><br>
@@ -226,6 +227,52 @@ default Object deepExtract(@Nullable T t) {
226227
}
227228
}
228229

230+
/**
231+
* Copy the provided tag object and any of its elements.
232+
*
233+
* @param t the tag to copy.
234+
* @return a deep copy of tag.
235+
*/
236+
@NotNull
237+
@SuppressWarnings("unchecked")
238+
default T copy(@NotNull T t) {
239+
switch (typeId(t)) {
240+
case Tag.END:
241+
case Tag.BYTE:
242+
case Tag.SHORT:
243+
case Tag.INT:
244+
case Tag.LONG:
245+
case Tag.FLOAT:
246+
case Tag.DOUBLE:
247+
case Tag.STRING:
248+
// Immutable type
249+
return t;
250+
case Tag.BYTE_ARRAY:
251+
final byte[] fromBytes = (byte[]) extract(t);
252+
final byte[] toBytes = new byte[fromBytes.length];
253+
System.arraycopy(fromBytes, 0, toBytes, 0, fromBytes.length);
254+
return build(TagType.BYTE_ARRAY, toBytes);
255+
case Tag.LIST:
256+
final List<T> list = ((List<T>) extract(t)).stream().map(this::copy).collect(Collectors.toCollection(ArrayList::new));
257+
return build(TagType.LIST, list);
258+
case Tag.COMPOUND:
259+
final Map<String, T> map = ((Map<String, T>) extract(t)).entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> copy(e.getValue())));
260+
return build(TagType.COMPOUND, map);
261+
case Tag.INT_ARRAY:
262+
final int[] fromInts = (int[]) extract(t);
263+
final int[] toInts = new int[fromInts.length];
264+
System.arraycopy(fromInts, 0, toInts, 0, fromInts.length);
265+
return build(TagType.INT_ARRAY, toInts);
266+
case Tag.LONG_ARRAY:
267+
final long[] fromLongs = (long[]) extract(t);
268+
final long[] toLongs = new long[fromLongs.length];
269+
System.arraycopy(fromLongs, 0, toLongs, 0, fromLongs.length);
270+
return build(TagType.LONG_ARRAY, toLongs);
271+
default:
272+
throw new IllegalArgumentException("Invalid tag type: " + t);
273+
}
274+
}
275+
229276
/**
230277
* Get the size of bytes from tag object implementation.
231278
*

0 commit comments

Comments
 (0)