From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 12 Jun 2024 10:29:30 -0700
Subject: [PATCH] Make a PDC view accessible directly from ItemStack


diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.persistence;
+
+import java.util.Set;
+import org.bukkit.NamespacedKey;
+import org.bukkit.persistence.PersistentDataAdapterContext;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataHolder;
+import org.bukkit.persistence.PersistentDataType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * This represents a view of a persistent data container. No
+ * methods on this interface mutate the container.
+ * @see PersistentDataContainer
+ */
+@ApiStatus.NonExtendable
+public interface PersistentDataContainerView {
+
+    /**
+     * Returns if the persistent metadata provider has metadata registered
+     * matching the provided parameters.
+     * <p>
+     * This method will only return true if the found value has the same primitive
+     * data type as the provided key.
+     * <p>
+     * Storing a value using a custom {@link PersistentDataType} implementation
+     * will not store the complex data type. Therefore storing a UUID (by
+     * storing a byte[]) will match has("key" ,
+     * {@link PersistentDataType#BYTE_ARRAY}). Likewise a stored byte[] will
+     * always match your UUID {@link PersistentDataType} even if it is not 16
+     * bytes long.
+     * <p>
+     * This method is only usable for custom object keys. Overwriting existing
+     * tags, like the display name, will not work as the values are stored
+     * using your namespace.
+     *
+     * @param key the key the value is stored under
+     * @param type the type the primative stored value has to match
+     * @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
+     *
+     * @throws IllegalArgumentException if the key to look up is null
+     * @throws IllegalArgumentException if the type to cast the found object to is
+     * null
+     */
+    <P, C> boolean has(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type);
+
+    /**
+     * Returns if the persistent metadata provider has metadata registered matching
+     * the provided parameters.
+     * <p>
+     * This method will return true as long as a value with the given key exists,
+     * regardless of its type.
+     * <p>
+     * This method is only usable for custom object keys. Overwriting existing tags,
+     * like the display name, will not work as the values are stored using your
+     * namespace.
+     *
+     * @param key the key the value is stored under
+     *
+     * @return if a value with the provided key exists
+     *
+     * @throws IllegalArgumentException if the key to look up is null
+     */
+    boolean has(@NonNull NamespacedKey key);
+
+    /**
+     * Returns the metadata value that is stored on the
+     * {@link PersistentDataHolder} instance.
+     *
+     * @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 <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
+     *
+     * @throws IllegalArgumentException if the key to look up is null
+     * @throws IllegalArgumentException if the type to cast the found object to is
+     * null
+     * @throws IllegalArgumentException if a value exists under the given key,
+     * but cannot be accessed using the given type
+     * @throws IllegalArgumentException if no suitable adapter was found for
+     * the {@link
+     * PersistentDataType#getPrimitiveType()}
+     */
+    <P, C> @Nullable C get(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type);
+
+    /**
+     * Returns the metadata value that is stored on the
+     * {@link PersistentDataHolder} instance. If the value does not exist in the
+     * container, the default value provided is returned.
+     *
+     * @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 defaultValue the default value to return if no value was found for
+     * the provided key
+     * @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
+     *
+     * @throws IllegalArgumentException if the key to look up is null
+     * @throws IllegalArgumentException if the type to cast the found object to is
+     * null
+     * @throws IllegalArgumentException if a value exists under the given key,
+     * but cannot be accessed using the given type
+     * @throws IllegalArgumentException if no suitable adapter was found for
+     * the {@link PersistentDataType#getPrimitiveType()}
+     */
+    <P, C> @NonNull C getOrDefault(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type, @NonNull C defaultValue);
+
+    /**
+     * Get the set of keys present on this {@link PersistentDataContainer}
+     * instance.
+     *
+     * Any changes made to the returned set will not be reflected on the
+     * instance.
+     *
+     * @return the key set
+     */
+    @NonNull Set<NamespacedKey> getKeys();
+
+    /**
+     * Returns if the container instance is empty, therefore has no entries
+     * inside it.
+     *
+     * @return the boolean
+     */
+    boolean isEmpty();
+
+    /**
+     * Copies all values from this {@link PersistentDataContainer} to the provided
+     * container.
+     * <p>
+     * This method only copies custom object keys. Existing tags, like the display
+     * name, will not be copied as the values are stored using your namespace.
+     *
+     * @param other   the container to copy to
+     * @param replace whether to replace any matching values in the target container
+     *
+     * @throws IllegalArgumentException if the other container is null
+     */
+    void copyTo(@NonNull PersistentDataContainer other, boolean replace);
+
+    /**
+     * Returns the adapter context this tag container uses.
+     *
+     * @return the tag context
+     */
+    @NonNull PersistentDataAdapterContext getAdapterContext();
+
+    /**
+     * Serialize this {@link PersistentDataContainer} instance to a
+     * byte array.
+     *
+     * @return a binary representation of this container
+     * @throws java.io.IOException if we fail to write this container to a byte array
+     */
+    byte @NonNull [] serializeToBytes() throws java.io.IOException;
+}
diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.persistence;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * The {@link PersistentDataViewHolder} interface defines an object that can view
+ * custom persistent data on it.
+ */
+@ApiStatus.NonExtendable
+public interface PersistentDataViewHolder {
+
+    /**
+     * Returns a custom tag container view capable of viewing tags on the object.
+     * <p>
+     * Note that the tags stored on this container are all stored under their
+     * own custom namespace therefore modifying default tags using this
+     * {@link PersistentDataViewHolder} is impossible.
+     *
+     * @return the persistent data container view
+     */
+    @NonNull PersistentDataContainerView getPersistentDataContainer();
+}
diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/src/main/java/org/bukkit/inventory/ItemStack.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * use this class to encapsulate Materials for which {@link Material#isItem()}
  * returns false.</b>
  */
-public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable { // Paper
+public class ItemStack implements Cloneable, ConfigurationSerializable, Translatable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem>, net.kyori.adventure.translation.Translatable, io.papermc.paper.persistence.PersistentDataViewHolder { // Paper
     private ItemStack craftDelegate; // Paper - always delegate to server-backed stack
     private MaterialData data = null;
 
@@ -0,0 +0,0 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
     }
     // Paper end
 
+    // Paper start - pdc
+    @Override
+    public io.papermc.paper.persistence.@NotNull PersistentDataContainerView getPersistentDataContainer() {
+        return this.craftDelegate.getPersistentDataContainer();
+    }
+    // Paper end - pdc
+
     @Utility
     protected ItemStack() {}
 
diff --git a/src/main/java/org/bukkit/persistence/PersistentDataContainer.java b/src/main/java/org/bukkit/persistence/PersistentDataContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/persistence/PersistentDataContainer.java
+++ b/src/main/java/org/bukkit/persistence/PersistentDataContainer.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.Nullable;
  * This interface represents a map like object, capable of storing custom tags
  * in it.
  */
-public interface PersistentDataContainer {
+public interface PersistentDataContainer extends io.papermc.paper.persistence.PersistentDataContainerView { // Paper - split up view and mutable
 
     /**
      * Stores a metadata value on the {@link PersistentDataHolder} instance.
@@ -0,0 +0,0 @@ public interface PersistentDataContainer {
      * the {@link PersistentDataType#getPrimitiveType()}
      */
     <P, C> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type, @NotNull C value);
-
-    /**
-     * Returns if the persistent metadata provider has metadata registered
-     * matching the provided parameters.
-     * <p>
-     * This method will only return true if the found value has the same primitive
-     * data type as the provided key.
-     * <p>
-     * Storing a value using a custom {@link PersistentDataType} implementation
-     * will not store the complex data type. Therefore storing a UUID (by
-     * storing a byte[]) will match has("key" ,
-     * {@link PersistentDataType#BYTE_ARRAY}). Likewise a stored byte[] will
-     * always match your UUID {@link PersistentDataType} even if it is not 16
-     * bytes long.
-     * <p>
-     * This method is only usable for custom object keys. Overwriting existing
-     * tags, like the display name, will not work as the values are stored
-     * using your namespace.
-     *
-     * @param key the key the value is stored under
-     * @param type the type the primative stored value has to match
-     * @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
-     *
-     * @throws IllegalArgumentException if the key to look up is null
-     * @throws IllegalArgumentException if the type to cast the found object to is
-     * null
-     */
-    <P, C> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type);
-
-    /**
-     * Returns if the persistent metadata provider has metadata registered matching
-     * the provided parameters.
-     * <p>
-     * This method will return true as long as a value with the given key exists,
-     * regardless of its type.
-     * <p>
-     * This method is only usable for custom object keys. Overwriting existing tags,
-     * like the display name, will not work as the values are stored using your
-     * namespace.
-     *
-     * @param key the key the value is stored under
-     *
-     * @return if a value with the provided key exists
-     *
-     * @throws IllegalArgumentException if the key to look up is null
-     */
-    boolean has(@NotNull NamespacedKey key);
-
-    /**
-     * Returns the metadata value that is stored on the
-     * {@link PersistentDataHolder} instance.
-     *
-     * @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 <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
-     *
-     * @throws IllegalArgumentException if the key to look up is null
-     * @throws IllegalArgumentException if the type to cast the found object to is
-     * null
-     * @throws IllegalArgumentException if a value exists under the given key,
-     * but cannot be accessed using the given type
-     * @throws IllegalArgumentException if no suitable adapter was found for
-     * the {@link
-     * PersistentDataType#getPrimitiveType()}
-     */
-    @Nullable
-    <P, C> C get(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type);
-
-    /**
-     * Returns the metadata value that is stored on the
-     * {@link PersistentDataHolder} instance. If the value does not exist in the
-     * container, the default value provided is returned.
-     *
-     * @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 defaultValue the default value to return if no value was found for
-     * the provided key
-     * @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
-     *
-     * @throws IllegalArgumentException if the key to look up is null
-     * @throws IllegalArgumentException if the type to cast the found object to is
-     * null
-     * @throws IllegalArgumentException if a value exists under the given key,
-     * but cannot be accessed using the given type
-     * @throws IllegalArgumentException if no suitable adapter was found for
-     * the {@link PersistentDataType#getPrimitiveType()}
-     */
-    @NotNull
-    <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}
-     * instance.
-     *
-     * Any changes made to the returned set will not be reflected on the
-     * instance.
-     *
-     * @return the key set
-     */
-    @NotNull
-    Set<NamespacedKey> getKeys();
+    // Paper - move to PersistentDataContainerView
 
     /**
      * Removes a custom key from the {@link PersistentDataHolder} instance.
@@ -0,0 +0,0 @@ public interface PersistentDataContainer {
      * @throws IllegalArgumentException if the provided key is null
      */
     void remove(@NotNull NamespacedKey key);
-
-    /**
-     * Returns if the container instance is empty, therefore has no entries
-     * inside it.
-     *
-     * @return the boolean
-     */
-    boolean isEmpty();
-
-    /**
-     * Copies all values from this {@link PersistentDataContainer} to the provided
-     * container.
-     * <p>
-     * This method only copies custom object keys. Existing tags, like the display
-     * name, will not be copied as the values are stored using your namespace.
-     *
-     * @param other   the container to copy to
-     * @param replace whether to replace any matching values in the target container
-     *
-     * @throws IllegalArgumentException if the other container is null
-     */
-    void copyTo(@NotNull PersistentDataContainer other, boolean replace);
-
-    /**
-     * Returns the adapter context this tag container uses.
-     *
-     * @return the tag context
-     */
-    @NotNull
-    PersistentDataAdapterContext getAdapterContext();
+    // Paper - move to PersistentDataContainerView
 
     // Paper start - byte array serialization
-    /**
-     * Serialize this {@link PersistentDataContainer} instance to a
-     * byte array.
-     *
-     * @return a binary representation of this container
-     * @throws java.io.IOException if we fail to write this container to a byte array
-     */
-    byte @NotNull [] serializeToBytes() throws java.io.IOException;
-
+    // Paper - move to PersistentDataContainerView
     /**
      * Read values from a serialised byte array into this
      * {@link PersistentDataContainer} instance.
diff --git a/src/main/java/org/bukkit/persistence/PersistentDataHolder.java b/src/main/java/org/bukkit/persistence/PersistentDataHolder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/persistence/PersistentDataHolder.java
+++ b/src/main/java/org/bukkit/persistence/PersistentDataHolder.java
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
 /**
  * The {@link PersistentDataHolder} interface defines an object that can store
  * custom persistent meta data on it.
+ * <p>Prefer using {@link io.papermc.paper.persistence.PersistentDataViewHolder} for read-only operations
+ * as it covers more types.</p>
  */
-public interface PersistentDataHolder {
+public interface PersistentDataHolder extends io.papermc.paper.persistence.PersistentDataViewHolder { // Paper
 
     /**
      * Returns a custom tag container capable of storing tags on the object.
