#933: Define native persistent data types for lists

By: Bjarne Koll <lynxplay101@gmail.com>
This commit is contained in:
Bukkit/Spigot
2024-01-06 16:03:53 +11:00
parent b48b712546
commit cfcc75669d
4 changed files with 309 additions and 32 deletions

View File

@@ -0,0 +1,22 @@
package org.bukkit.persistence;
import java.util.List;
import org.jetbrains.annotations.NotNull;
/**
* The list persistent data represents a data type that is capable of storing a
* list of other data types in a {@link PersistentDataContainer}.
*
* @param <P> the primitive type of the list element.
* @param <C> the complex type of the list elements.
*/
public interface ListPersistentDataType<P, C> extends PersistentDataType<List<P>, List<C>> {
/**
* Provides the persistent data type of the elements found in the list.
*
* @return the persistent data type.
*/
@NotNull
PersistentDataType<P, C> elementType();
}

View File

@@ -0,0 +1,229 @@
package org.bukkit.persistence;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import java.util.List;
import org.jetbrains.annotations.NotNull;
/**
* A provider for list persistent data types that represent the known primitive
* types exposed by {@link PersistentDataType}.
*/
public final class ListPersistentDataTypeProvider {
private static final ListPersistentDataType<Byte, Byte> BYTE = new ListPersistentDataTypeImpl<>(PersistentDataType.BYTE);
private static final ListPersistentDataType<Short, Short> SHORT = new ListPersistentDataTypeImpl<>(PersistentDataType.SHORT);
private static final ListPersistentDataType<Integer, Integer> INTEGER = new ListPersistentDataTypeImpl<>(PersistentDataType.INTEGER);
private static final ListPersistentDataType<Long, Long> LONG = new ListPersistentDataTypeImpl<>(PersistentDataType.LONG);
private static final ListPersistentDataType<Float, Float> FLOAT = new ListPersistentDataTypeImpl<>(PersistentDataType.FLOAT);
private static final ListPersistentDataType<Double, Double> DOUBLE = new ListPersistentDataTypeImpl<>(PersistentDataType.DOUBLE);
private static final ListPersistentDataType<Byte, Boolean> BOOLEAN = new ListPersistentDataTypeImpl<>(PersistentDataType.BOOLEAN);
private static final ListPersistentDataType<String, String> STRING = new ListPersistentDataTypeImpl<>(PersistentDataType.STRING);
private static final ListPersistentDataType<byte[], byte[]> BYTE_ARRAY = new ListPersistentDataTypeImpl<>(PersistentDataType.BYTE_ARRAY);
private static final ListPersistentDataType<int[], int[]> INTEGER_ARRAY = new ListPersistentDataTypeImpl<>(PersistentDataType.INTEGER_ARRAY);
private static final ListPersistentDataType<long[], long[]> LONG_ARRAY = new ListPersistentDataTypeImpl<>(PersistentDataType.LONG_ARRAY);
private static final ListPersistentDataType<PersistentDataContainer, PersistentDataContainer> DATA_CONTAINER = new ListPersistentDataTypeImpl<>(
PersistentDataType.TAG_CONTAINER
);
ListPersistentDataTypeProvider() {
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of bytes.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Byte, Byte> bytes() {
return BYTE;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of shorts.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Short, Short> shorts() {
return SHORT;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of integers.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Integer, Integer> integers() {
return INTEGER;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of longs.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Long, Long> longs() {
return LONG;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of floats.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Float, Float> floats() {
return FLOAT;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of doubles.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Double, Double> doubles() {
return DOUBLE;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of booleans.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<Byte, Boolean> booleans() {
return BOOLEAN;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of strings.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<String, String> strings() {
return STRING;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of byte arrays.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<byte[], byte[]> byteArrays() {
return BYTE_ARRAY;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of int arrays.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<int[], int[]> integerArrays() {
return INTEGER_ARRAY;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* storing lists of long arrays.
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<long[], long[]> longArrays() {
return LONG_ARRAY;
}
/**
* Provides a shared {@link ListPersistentDataType} that is capable of
* persistent data containers..
*
* @return the persistent data type.
*/
@NotNull
public ListPersistentDataType<PersistentDataContainer, PersistentDataContainer> dataContainers() {
return DATA_CONTAINER;
}
/**
* Constructs a new list persistent data type given any persistent data type
* for its elements.
*
* @param elementType the persistent data type that is capable of
* writing/reading the elements of the list.
* @param <P> the generic type of the primitives stored in the list.
* @param <C> the generic type of the complex values yielded back by the
* persistent data types.
* @return the created list persistent data type.
*/
@NotNull
public <P, C> ListPersistentDataType<P, C> listTypeFrom(@NotNull final PersistentDataType<P, C> elementType) {
return new ListPersistentDataTypeImpl<>(elementType);
}
/**
* A private implementation of the {@link ListPersistentDataType} that uses
* {@link Collections2} for conversion from/to the primitive list.
*
* @param <P> the generic type of the primitives stored in the list.
* @param <C> the generic type of the complex values yielded back by the
* persistent data types.
*/
private static final class ListPersistentDataTypeImpl<P, C> implements ListPersistentDataType<P, C> {
@NotNull
private final PersistentDataType<P, C> innerType;
private ListPersistentDataTypeImpl(@NotNull final PersistentDataType<P, C> innerType) {
this.innerType = innerType;
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public Class<List<P>> getPrimitiveType() {
return (Class<List<P>>) (Object) List.class;
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public Class<List<C>> getComplexType() {
return (Class<List<C>>) (Object) List.class;
}
@NotNull
@Override
public List<P> toPrimitive(@NotNull final List<C> complex, @NotNull final PersistentDataAdapterContext context) {
return Lists.transform(complex, s -> innerType.toPrimitive(s, context));
}
@NotNull
@Override
public List<C> fromPrimitive(@NotNull final List<P> primitive, @NotNull final PersistentDataAdapterContext context) {
return Lists.transform(primitive, s -> innerType.fromPrimitive(s, context));
}
@NotNull
@Override
public PersistentDataType<P, C> elementType() {
return this.innerType;
}
}
}

View File

@@ -22,8 +22,8 @@ public interface PersistentDataContainer {
* @param key the key this value will be stored under * @param key the key this value will be stored under
* @param type the type this tag uses * @param type the type this tag uses
* @param value the value to store in the tag * @param value the value to store in the tag
* @param <T> the generic java type of the tag value * @param <P> the generic java type of the tag value
* @param <Z> the generic type of the object to store * @param <C> the generic type of the object to store
* *
* @throws IllegalArgumentException if the key is null * @throws IllegalArgumentException if the key is null
* @throws IllegalArgumentException if the type is null * @throws IllegalArgumentException if the type is null
@@ -32,7 +32,7 @@ public interface PersistentDataContainer {
* @throws IllegalArgumentException if no suitable adapter was found for * @throws IllegalArgumentException if no suitable adapter was found for
* the {@link PersistentDataType#getPrimitiveType()} * the {@link PersistentDataType#getPrimitiveType()}
*/ */
<T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value); <P, C> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type, @NotNull C value);
/** /**
* Returns if the persistent metadata provider has metadata registered * Returns if the persistent metadata provider has metadata registered
@@ -54,8 +54,8 @@ public interface PersistentDataContainer {
* *
* @param key the key the value is stored under * @param key the key the value is stored under
* @param type the type the primative stored value has to match * @param type the type the primative stored value has to match
* @param <T> the generic type of the stored primitive * @param <P> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object * @param <C> the generic type of the eventually created complex object
* *
* @return if a value with the provided key and type exists * @return if a value with the provided key and type exists
* *
@@ -63,7 +63,7 @@ public interface PersistentDataContainer {
* @throws IllegalArgumentException if the type to cast the found object to is * @throws IllegalArgumentException if the type to cast the found object to is
* null * null
*/ */
<T, Z> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type); <P, C> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type);
/** /**
* Returns if the persistent metadata provider has metadata registered matching * Returns if the persistent metadata provider has metadata registered matching
@@ -90,8 +90,8 @@ public interface PersistentDataContainer {
* *
* @param key the key to look up in the custom tag map * @param key the key to look up in the custom tag map
* @param type the type the value must have and will be casted to * @param type the type the value must have and will be casted to
* @param <T> the generic type of the stored primitive * @param <P> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object * @param <C> the generic type of the eventually created complex object
* *
* @return the value or {@code null} if no value was mapped under the given * @return the value or {@code null} if no value was mapped under the given
* value * value
@@ -106,7 +106,7 @@ public interface PersistentDataContainer {
* PersistentDataType#getPrimitiveType()} * PersistentDataType#getPrimitiveType()}
*/ */
@Nullable @Nullable
<T, Z> Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type); <P, C> C get(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type);
/** /**
* Returns the metadata value that is stored on the * Returns the metadata value that is stored on the
@@ -117,8 +117,8 @@ public interface PersistentDataContainer {
* @param type the type the value must have and will be casted to * @param type the type the value must have and will be casted to
* @param defaultValue the default value to return if no value was found for * @param defaultValue the default value to return if no value was found for
* the provided key * the provided key
* @param <T> the generic type of the stored primitive * @param <P> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object * @param <C> the generic type of the eventually created complex object
* *
* @return the value or the default value if no value was mapped under the * @return the value or the default value if no value was mapped under the
* given key * given key
@@ -132,7 +132,7 @@ public interface PersistentDataContainer {
* the {@link PersistentDataType#getPrimitiveType()} * the {@link PersistentDataType#getPrimitiveType()}
*/ */
@NotNull @NotNull
<T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue); <P, C> C getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type, @NotNull C defaultValue);
/** /**
* Get the set of keys present on this {@link PersistentDataContainer} * Get the set of keys present on this {@link PersistentDataContainer}

View File

@@ -1,5 +1,6 @@
package org.bukkit.persistence; package org.bukkit.persistence;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@@ -13,17 +14,17 @@ import org.jetbrains.annotations.NotNull;
* {@code * {@code
* public class UUIDTagType implements PersistentDataType<byte[], UUID> { * public class UUIDTagType implements PersistentDataType<byte[], UUID> {
* *
* {@literal @Override} * @Override
* public Class<byte[]> getPrimitiveType() { * public Class<byte[]> getPrimitiveType() {
* return byte[].class; * return byte[].class;
* } * }
* *
* {@literal @Override} * @Override
* public Class<UUID> getComplexType() { * public Class<UUID> getComplexType() {
* return UUID.class; * return UUID.class;
* } * }
* *
* {@literal @Override} * @Override
* public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) { * public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) {
* ByteBuffer bb = ByteBuffer.wrap(new byte[16]); * ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
* bb.putLong(complex.getMostSignificantBits()); * bb.putLong(complex.getMostSignificantBits());
@@ -31,19 +32,24 @@ import org.jetbrains.annotations.NotNull;
* return bb.array(); * return bb.array();
* } * }
* *
* {@literal @Override} * @Override
* public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) { * public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
* ByteBuffer bb = ByteBuffer.wrap(primitive); * ByteBuffer bb = ByteBuffer.wrap(primitive);
* long firstLong = bb.getLong(); * long firstLong = bb.getLong();
* long secondLong = bb.getLong(); * long secondLong = bb.getLong();
* return new UUID(firstLong, secondLong); * return new UUID(firstLong, secondLong);
* } * }
* }}</pre> * }
*}</pre>
* *
* @param <T> the primary object type that is stored in the given tag * Any plugin owned implementation of this interface is required to define one
* @param <Z> the retrieved object type when applying this tag type * of the existing primitive types found in this interface. Notably
* {@link #BOOLEAN} is not a primitive type but a convenience type.
*
* @param <P> the primary object type that is stored in the given tag
* @param <C> the retrieved object type when applying this tag type
*/ */
public interface PersistentDataType<T, Z> { public interface PersistentDataType<P, C> {
/* /*
The primitive one value types. The primitive one value types.
@@ -80,6 +86,10 @@ public interface PersistentDataType<T, Z> {
/* /*
Complex Arrays. Complex Arrays.
*/ */
/**
* @deprecated Use {@link #LIST}'s {@link ListPersistentDataTypeProvider#dataContainers()} instead.
*/
@ApiStatus.Obsolete
PersistentDataType<PersistentDataContainer[], PersistentDataContainer[]> TAG_CONTAINER_ARRAY = new PrimitivePersistentDataType<>(PersistentDataContainer[].class); PersistentDataType<PersistentDataContainer[], PersistentDataContainer[]> TAG_CONTAINER_ARRAY = new PrimitivePersistentDataType<>(PersistentDataContainer[].class);
/* /*
@@ -87,13 +97,29 @@ public interface PersistentDataType<T, Z> {
*/ */
PersistentDataType<PersistentDataContainer, PersistentDataContainer> TAG_CONTAINER = new PrimitivePersistentDataType<>(PersistentDataContainer.class); PersistentDataType<PersistentDataContainer, PersistentDataContainer> TAG_CONTAINER = new PrimitivePersistentDataType<>(PersistentDataContainer.class);
/**
* A data type provider type that itself cannot be used as a
* {@link PersistentDataType}.
*
* {@link ListPersistentDataTypeProvider} exposes shared persistent data
* types for storing lists of other data types, however.
* <p>
* Its existence in the {@link PersistentDataType} interface does not permit
* {@link java.util.List} as a primitive type in combination with a plain
* {@link PersistentDataType}. {@link java.util.List}s are only valid
* primitive types when used via a {@link ListPersistentDataType}.
*
* @see ListPersistentDataTypeProvider
*/
ListPersistentDataTypeProvider LIST = new ListPersistentDataTypeProvider();
/** /**
* Returns the primitive data type of this tag. * Returns the primitive data type of this tag.
* *
* @return the class * @return the class
*/ */
@NotNull @NotNull
Class<T> getPrimitiveType(); Class<P> getPrimitiveType();
/** /**
* Returns the complex object type the primitive value resembles. * Returns the complex object type the primitive value resembles.
@@ -101,7 +127,7 @@ public interface PersistentDataType<T, Z> {
* @return the class type * @return the class type
*/ */
@NotNull @NotNull
Class<Z> getComplexType(); Class<C> getComplexType();
/** /**
* Returns the primitive data that resembles the complex object passed to * Returns the primitive data that resembles the complex object passed to
@@ -112,7 +138,7 @@ public interface PersistentDataType<T, Z> {
* @return the primitive value * @return the primitive value
*/ */
@NotNull @NotNull
T toPrimitive(@NotNull Z complex, @NotNull PersistentDataAdapterContext context); P toPrimitive(@NotNull C complex, @NotNull PersistentDataAdapterContext context);
/** /**
* Creates a complex object based of the passed primitive value * Creates a complex object based of the passed primitive value
@@ -122,7 +148,7 @@ public interface PersistentDataType<T, Z> {
* @return the complex object instance * @return the complex object instance
*/ */
@NotNull @NotNull
Z fromPrimitive(@NotNull T primitive, @NotNull PersistentDataAdapterContext context); C fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context);
/** /**
* A default implementation that simply exists to pass on the retrieved or * A default implementation that simply exists to pass on the retrieved or
@@ -131,37 +157,37 @@ public interface PersistentDataType<T, Z> {
* This implementation does not add any kind of logic, but is used to * This implementation does not add any kind of logic, but is used to
* provide default implementations for the primitive types. * provide default implementations for the primitive types.
* *
* @param <T> the generic type of the primitive objects * @param <P> the generic type of the primitive objects
*/ */
class PrimitivePersistentDataType<T> implements PersistentDataType<T, T> { class PrimitivePersistentDataType<P> implements PersistentDataType<P, P> {
private final Class<T> primitiveType; private final Class<P> primitiveType;
PrimitivePersistentDataType(@NotNull Class<T> primitiveType) { PrimitivePersistentDataType(@NotNull Class<P> primitiveType) {
this.primitiveType = primitiveType; this.primitiveType = primitiveType;
} }
@NotNull @NotNull
@Override @Override
public Class<T> getPrimitiveType() { public Class<P> getPrimitiveType() {
return primitiveType; return primitiveType;
} }
@NotNull @NotNull
@Override @Override
public Class<T> getComplexType() { public Class<P> getComplexType() {
return primitiveType; return primitiveType;
} }
@NotNull @NotNull
@Override @Override
public T toPrimitive(@NotNull T complex, @NotNull PersistentDataAdapterContext context) { public P toPrimitive(@NotNull P complex, @NotNull PersistentDataAdapterContext context) {
return complex; return complex;
} }
@NotNull @NotNull
@Override @Override
public T fromPrimitive(@NotNull T primitive, @NotNull PersistentDataAdapterContext context) { public P fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context) {
return primitive; return primitive;
} }
} }