#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 type the type this tag uses
* @param value the value to store in the tag
* @param <T> the generic java type of the tag value
* @param <Z> the generic type of the object to store
* @param <P> the generic java type of the tag value
* @param <C> the generic type of the object to store
*
* @throws IllegalArgumentException if the key 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
* 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
@ -54,8 +54,8 @@ public interface PersistentDataContainer {
*
* @param key the key the value is stored under
* @param type the type the primative stored value has to match
* @param <T> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object
* @param <P> the generic type of the stored primitive
* @param <C> the generic type of the eventually created complex object
*
* @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
* 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
@ -90,8 +90,8 @@ public interface PersistentDataContainer {
*
* @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 <T> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object
* @param <P> the generic type of the stored primitive
* @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
* value
@ -106,7 +106,7 @@ public interface PersistentDataContainer {
* PersistentDataType#getPrimitiveType()}
*/
@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
@ -117,8 +117,8 @@ public interface PersistentDataContainer {
* @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
* the provided key
* @param <T> the generic type of the stored primitive
* @param <Z> the generic type of the eventually created complex object
* @param <P> the generic type of the stored primitive
* @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
* given key
@ -132,7 +132,7 @@ public interface PersistentDataContainer {
* the {@link PersistentDataType#getPrimitiveType()}
*/
@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}

View File

@ -1,5 +1,6 @@
package org.bukkit.persistence;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@ -13,17 +14,17 @@ import org.jetbrains.annotations.NotNull;
* {@code
* public class UUIDTagType implements PersistentDataType<byte[], UUID> {
*
* {@literal @Override}
* @Override
* public Class<byte[]> getPrimitiveType() {
* return byte[].class;
* }
*
* {@literal @Override}
* @Override
* public Class<UUID> getComplexType() {
* return UUID.class;
* }
*
* {@literal @Override}
* @Override
* public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) {
* ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
* bb.putLong(complex.getMostSignificantBits());
@ -31,19 +32,24 @@ import org.jetbrains.annotations.NotNull;
* return bb.array();
* }
*
* {@literal @Override}
* @Override
* public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
* ByteBuffer bb = ByteBuffer.wrap(primitive);
* long firstLong = bb.getLong();
* long secondLong = bb.getLong();
* return new UUID(firstLong, secondLong);
* }
* }}</pre>
* }
*}</pre>
*
* @param <T> the primary object type that is stored in the given tag
* @param <Z> the retrieved object type when applying this tag type
* Any plugin owned implementation of this interface is required to define one
* 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.
@ -80,6 +86,10 @@ public interface PersistentDataType<T, Z> {
/*
Complex Arrays.
*/
/**
* @deprecated Use {@link #LIST}'s {@link ListPersistentDataTypeProvider#dataContainers()} instead.
*/
@ApiStatus.Obsolete
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);
/**
* 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.
*
* @return the class
*/
@NotNull
Class<T> getPrimitiveType();
Class<P> getPrimitiveType();
/**
* Returns the complex object type the primitive value resembles.
@ -101,7 +127,7 @@ public interface PersistentDataType<T, Z> {
* @return the class type
*/
@NotNull
Class<Z> getComplexType();
Class<C> getComplexType();
/**
* Returns the primitive data that resembles the complex object passed to
@ -112,7 +138,7 @@ public interface PersistentDataType<T, Z> {
* @return the primitive value
*/
@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
@ -122,7 +148,7 @@ public interface PersistentDataType<T, Z> {
* @return the complex object instance
*/
@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
@ -131,37 +157,37 @@ public interface PersistentDataType<T, Z> {
* This implementation does not add any kind of logic, but is used to
* 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;
}
@NotNull
@Override
public Class<T> getPrimitiveType() {
public Class<P> getPrimitiveType() {
return primitiveType;
}
@NotNull
@Override
public Class<T> getComplexType() {
public Class<P> getComplexType() {
return primitiveType;
}
@NotNull
@Override
public T toPrimitive(@NotNull T complex, @NotNull PersistentDataAdapterContext context) {
public P toPrimitive(@NotNull P complex, @NotNull PersistentDataAdapterContext context) {
return complex;
}
@NotNull
@Override
public T fromPrimitive(@NotNull T primitive, @NotNull PersistentDataAdapterContext context) {
public P fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context) {
return primitive;
}
}