package de.take_weiland.mods.commons.nbt;
import com.google.common.collect.ImmutableMap;
import de.take_weiland.mods.commons.util.SCReflector;
import net.minecraft.nbt.*;
import org.jetbrains.annotations.Contract;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* <p>Utility methods regarding NBT data.</p>
*/
@ParametersAreNonnullByDefault
public final class NBT {
public static final int TAG_END = 0;
public static final int TAG_BYTE = 1;
public static final int TAG_SHORT = 2;
public static final int TAG_INT = 3;
public static final int TAG_LONG = 4;
public static final int TAG_FLOAT = 5;
public static final int TAG_DOUBLE = 6;
public static final int TAG_BYTE_ARR = 7;
public static final int TAG_STRING = 8;
public static final int TAG_LIST = 9;
public static final int TAG_COMPOUND = 10;
public static final int TAG_INT_ARR = 11;
/**
* <p>An enumeration of all NBT tag types.</p>
*/
public enum Tag {
END(NBTTagEnd.class),
BYTE(NBTTagByte.class),
SHORT(NBTTagShort.class),
INT(NBTTagInt.class),
LONG(NBTTagLong.class),
FLOAT(NBTTagFloat.class),
DOUBLE(NBTTagDouble.class),
BYTE_ARRAY(NBTTagByteArray.class),
STRING(NBTTagString.class),
LIST(NBTTagList.class),
COMPOUND(NBTTagCompound.class),
INT_ARRAY(NBTTagIntArray.class);
private final Class<? extends NBTBase> clazz;
private Tag(Class<? extends NBTBase> clazz) {
this.clazz = clazz;
}
/**
* <p>The id for this tag type, as returned by {@link net.minecraft.nbt.NBTBase#getId()}.</p>
* @return the type id
*/
public final int id() {
return ordinal();
}
/**
* <p>Get the tag class corresponding to this tag type.</p>
* @return the class
*/
public final Class<? extends NBTBase> getTagClass() {
return clazz;
}
/**
* <p>Get the tag type specified by the given type id.</p>
* @param id the type id
* @return the tag type
*/
public static Tag byId(int id) {
checkArgument(id >= 0 && id <= 11, "NBT type id out of range");
return VALUES[id];
}
/**
* <p>Get the tag type based on the given class.</p>
* @param clazz the tag class
* @return the tag type
*/
public static Tag byClass(Class<?> clazz) {
Tag t = BY_CLAZZ.get(clazz);
if (t == null) {
checkNotNull(clazz, "clazz");
throw new IllegalArgumentException("Invalid NBT Tag class " + clazz.getName());
}
return t;
}
private static final Tag[] VALUES = values();
private static final ImmutableMap<Class<?>, Tag> BY_CLAZZ;
static {
ImmutableMap.Builder<Class<?>, Tag> b = ImmutableMap.builder();
for (Tag tag : VALUES) {
b.put(tag.clazz, tag);
}
BY_CLAZZ = b.build();
}
}
/**
* <p>Get a List view of the given NBTTagList.
* The returned List is modifiable and writes through to the underlying NBTTagList.</p>
* <p>The type-parameter {@code T} can be used to narrow the type of the List, if it is known. If a wrong type is
* provided here, a {@code ClassCastException} may be thrown at any time in the future.</p>
* <p>Care must also be taken if {@code T} is not provided and values of a wrong type are written into the List.</p>
*
* @param nbt the underlying NBTTagList
* @return a modifiable List view
*/
@Nonnull
public static <T extends NBTBase> List<T> asList(NBTTagList nbt) {
return SCReflector.instance.getWrappedList(nbt);
}
/**
* <p>Get a Map view of the given NBTTagCompound.
* The returned Map is modifiable and writes through to the underlying NBTTagCompound.</p>
* <p>Note that the returned Map does <i>not</i> create default values of any kind, as opposed to NBTTagCompound.</p>
* @param nbt the underlying NBTTagCompound
* @return a modifiable Map view
*/
@Nonnull
public static Map<String, NBTBase> asMap(NBTTagCompound nbt) {
return SCReflector.instance.getWrappedMap(nbt);
}
/**
* <p>Get the NBTTagCompound with the given key in {@code parent} or, if no entry for that key is present,
* create a new NBTTagCompound and store it in {@code parent} with the given key.</p>
* @param parent the parent NBTTagCompound
* @param key the key
* @return an NBTTagCompound
*/
@Nonnull
public static NBTTagCompound getOrCreateCompound(NBTTagCompound parent, String key) {
NBTTagCompound nbt = (NBTTagCompound) asMap(parent).get(key);
if (nbt == null) {
parent.setCompoundTag(key, (nbt = new NBTTagCompound()));
}
return nbt;
}
/**
* <p>Get the NBTTagList with the given key in {@code parent} of, if no entry for that key is present,
* create a new NBTTagList and store it in {@code parent} with the given key.</p>
* @param parent the parent NBTTagCompound
* @param key the key
* @return an NBTTagList
*/
@Nonnull
public static NBTTagList getOrCreateList(NBTTagCompound parent, String key) {
NBTTagList list = (NBTTagList) asMap(parent).get(key);
if (list == null) {
parent.setTag(key, (list = new NBTTagList()));
}
return list;
}
/**
* <p>Create a deep copy of the given NBT-Tag.</p>
* @param nbt the tag
* @return a deep copy of the tag
*/
@SuppressWarnings("unchecked")
@Nullable
@Contract("null->null")
public static <T extends NBTBase> T copy(@Nullable T nbt) {
return nbt == null ? null : (T) nbt.copy();
}
private NBT() { }
}