GitHub PullRequestReviewCommentEvent at 2014-05-26 18:01:01
Repository: CraftBukkit
Actor: ST-DDT
Payload: {"comment":{"url":"https://api.github.com/repos/Bukkit/CraftBukkit/pulls/comments/13053885","id":13053885,"diff_hunk":"@@ -0,0 +1,724 @@\n+package org.bukkit.craftbukkit.util;\n+\n+import com.google.common.collect.ImmutableMap;\n+import net.minecraft.server.NBTBase;\n+import net.minecraft.server.NBTTagByte;\n+import net.minecraft.server.NBTTagByteArray;\n+import net.minecraft.server.NBTTagCompound;\n+import net.minecraft.server.NBTTagDouble;\n+import net.minecraft.server.NBTTagFloat;\n+import net.minecraft.server.NBTTagInt;\n+import net.minecraft.server.NBTTagIntArray;\n+import net.minecraft.server.NBTTagList;\n+import net.minecraft.server.NBTTagLong;\n+import net.minecraft.server.NBTTagShort;\n+import net.minecraft.server.NBTTagString;\n+import org.bukkit.Bukkit;\n+import org.bukkit.configuration.serialization.ConfigurationSerializable;\n+import org.bukkit.configuration.serialization.ConfigurationSerialization;\n+import org.bukkit.metadata.MetadataValue;\n+import org.bukkit.metadata.PersistentMetadataValue;\n+import org.bukkit.plugin.Plugin;\n+import org.bukkit.plugin.PluginManager;\n+import org.bukkit.util.NumberConversions;\n+\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+\n+/**\n+ * Encapsulates any custom data that may be stored in an NBTTagCompound.\n+ * <p>\n+ * Handles serialization of ConfigurationSerialization objects and\n+ * basic data types to and from a persistent data store.\n+ * <p>\n+ * This is used by CraftMetaItem to implement the Metadatable interface\n+ * by mapping Plugin ownership to a standard tag structure.\n+ */\n+public class NBTMetadataStore implements Cloneable {\n+    // Theses are key strings used to store and check for custom data\n+    // Bukkit may store custom internal data under BUKKIT_DATA_KEY.\n+    // Custom Plugin data (from the Metadatable interface)\n+    // is stored under BUKKIT_DATA_KEY.PLUGIN_DATA_KEY\n+    public final static String BUKKIT_DATA_KEY = \"bukkit\";\n+    public final static String PLUGIN_DATA_KEY = \"plugins\";\n+\n+    // This is copied from CraftMetaItem.SerializableMeta\n+    // It is needed for filtering, and I wasn't sure how to cleanly share\n+    // this without a lot of refactoring.\n+    static final String TYPE_FIELD = \"meta-type\";\n+\n+    protected NBTTagCompound tag;\n+\n+    /**\n+     * Returns a filtered store, such that it may not contain any of\n+     * the specified keys.\n+     * <p>\n+     * If there is no unfiltered data in the given tag, this method will\n+     * not create a new store, and will return null.\n+     *\n+     * @param tag The tag to scan for data\n+     * @param filterKeys A Set of keys to filter out\n+     * @return A new NBTMetadataStore containing a copy of the data in\n+     *   tag, minus what was filtered. If the end result is empty,\n+     *   null will be returned.\n+     */\n+    public static NBTMetadataStore getFilteredStore(NBTTagCompound tag, Set<String> filterKeys) {\n+        NBTTagCompound filteredTag = null;\n+        Set<String> keys = getAllKeys(tag);\n+        for (String key : keys) {\n+            if (filterKeys.contains(key)) {\n+                continue;\n+            }\n+\n+            if (filteredTag == null) {\n+                filteredTag = new NBTTagCompound();\n+            }\n+\n+            filteredTag.set(key, tag.get(key).clone());\n+        }\n+        return filteredTag == null ? null : new NBTMetadataStore(filteredTag);\n+    }\n+\n+    /**\n+     * Returns a filtered store, such that it may not contain any of\n+     * the specified keys.\n+     * <p>\n+     * If there is no unfiltered data in the given Map, this method will\n+     * not create a new store, and will return null.\n+     *\n+     * @param dataMap The Ma[ to scan for data\n+     * @param filterKeys A Set of keys to filter out\n+     * @return A new NBTMetadataStore containing a copy of the data in\n+     *   dataMap, minus what was filtered. If the end result is empty,\n+     *   null will be returned.\n+     */\n+    public static NBTMetadataStore getFilteredStore(Map<String, Object> dataMap, Set<String> filterKeys) {\n+        NBTTagCompound filteredTag = null;\n+        Set<String> keys = dataMap.keySet();\n+        for (String key : keys) {\n+            if (filterKeys.contains(key)) {\n+                continue;\n+            }\n+\n+            // Filter out special ConfigurationSerialization and\n+            // SerializeableMeta tags.\n+            // These seem like they shouldn't make it this far down the pipeline,\n+            // but it seems like they will be in the root after deserialization.\n+            if (key.equals(ConfigurationSerialization.SERIALIZED_TYPE_KEY) || key.equals(TYPE_FIELD)) {\n+                continue;\n+            }\n+\n+            if (filteredTag == null) {\n+                filteredTag = new NBTTagCompound();\n+            }\n+            filteredTag.set(key, convert(dataMap.get(key)));\n+        }\n+        return filteredTag == null ? null : new NBTMetadataStore(filteredTag);\n+    }\n+\n+    /**\n+     * Check to see if a tag has a specific key\n+     * registered to any Plugin.\n+     *\n+     * @param tag The tag to scan for data\n+     * @param key The key to check for\n+     * @return True if the tag has a non-empty\n+     *   BUKKIT_DATA_KEY.PLUGIN_DATA_KEY compound.\n+     */\n+    public static boolean hasPluginMetadata(NBTTagCompound tag, String key) {\n+        NBTTagCompound bukkitRoot = tag.getCompound(BUKKIT_DATA_KEY);\n+        if (bukkitRoot == null) return false;\n+        NBTTagCompound pluginsRoot = bukkitRoot.getCompound(PLUGIN_DATA_KEY);\n+        if (pluginsRoot == null) return false;\n+\n+        // We must scan for data here. This could be optimized with a\n+        // ref-counted Map of keys, but I think the (String, Plugin)\n+        // method should be the more common use case.\n+        Set<String> plugins = getAllKeys(pluginsRoot);\n+        for (String plugin : plugins) {\n+            NBTTagCompound pluginRoot = pluginsRoot.getCompound(plugin);\n+            if (pluginRoot.hasKey(key)) {\n+                return true;\n+            }\n+        }\n+        return false;\n+    }\n+\n+    /**\n+     * Check to see if a tag has any plugin data on it.\n+     *\n+     * @param tag The tag to scan for data\n+     * @param key The key to check for\n+     * @param owningPlugin The plugin to check for data\n+     * @return True if the tag has a non-empty\n+     *   BUKKIT_DATA_KEY.PLUGIN_DATA_KEY compound.\n+     */\n+    public static boolean hasPluginMetadata(NBTTagCompound tag, String key, Plugin owningPlugin) {\n+        NBTTagCompound bukkitRoot = tag.getCompound(BUKKIT_DATA_KEY);\n+        if (bukkitRoot == null) return false;\n+        NBTTagCompound pluginsRoot = bukkitRoot.getCompound(PLUGIN_DATA_KEY);\n+        String pluginName = owningPlugin.getName();\n+        if (pluginsRoot == null || !pluginsRoot.hasKey(pluginName)) return false;\n+        NBTTagCompound pluginRoot = pluginsRoot.getCompound(pluginName);\n+        return pluginRoot != null && pluginRoot.hasKey(key);\n+    }\n+\n+    /**\n+     * Create an empty data store.\n+     */\n+    public NBTMetadataStore() {\n+        this.tag = new NBTTagCompound();\n+    }\n+\n+    /**\n+     * Wrap a data store around an existing NBTTagCompound.\n+     * <p>\n+     * It is expected that this tag will contain a structure\n+     * that follows the \"<datakey>.<plugin> = <value>\" format.\n+     *\n+     * @param tag The root of this datastore.\n+     */\n+    private NBTMetadataStore(NBTTagCompound tag) {\n+        this.tag = tag;\n+    }\n+\n+    /**\n+     * Retrieve the NBTTagCompound that holds all of the Plugin metadata.\n+     *\n+     * @param create If True, the path to the root will be created if it does not exist.\n+     * @return The NBTTagCompound that holds this data\n+     */\n+    protected NBTTagCompound getPluginMetadataRoot(boolean create) {\n+        NBTTagCompound bukkitRoot = getBukkitDataRoot(create);\n+        NBTTagCompound pluginsRoot = bukkitRoot.getCompound(PLUGIN_DATA_KEY);\n+        if (create) {\n+            bukkitRoot.set(PLUGIN_DATA_KEY, pluginsRoot);\n+        }\n+        return pluginsRoot;\n+    }\n+\n+    /**\n+     * Store a MetadataValue.\n+     * <p>\n+     * Throws an IllegalArgumentException if setting anything\n+     * other than a PersistentMetadataValue value.\n+     *\n+     * @param metadataKey The metadata key to store\n+     * @param newMetadataValue The value to store, must be PersistentMetadataValue\n+     */\n+    public void setPluginMetadata(String metadataKey, MetadataValue newMetadataValue) {\n+        if (!(newMetadataValue instanceof PersistentMetadataValue)) {\n+            throw new IllegalArgumentException(\"This store can only hold PersistentMetadataValue\");\n+        }\n+\n+        NBTTagCompound metadataRoot = getPluginMetadataRoot(true);\n+        String pluginName = newMetadataValue.getOwningPlugin().getName();\n+        NBTTagCompound pluginTag = metadataRoot.getCompound(pluginName);\n+        pluginTag.set(metadataKey, convert(newMetadataValue.value()));\n+        metadataRoot.set(pluginName, pluginTag);\n+    }\n+\n+    /**\n+     * Retrieve all stored metadata for all plugins.\n+     *\n+     * @param metadataKey The metadata to look up\n+     * @return A List of values found, or an empty List.\n+     */\n+    public List<MetadataValue> getPluginMetadata(String metadataKey) {\n+        NBTTagCompound metadataRoot = getPluginMetadataRoot(false);\n+        PluginManager pm = Bukkit.getPluginManager();\n+        List<MetadataValue> metadata = new ArrayList<MetadataValue>();\n+\n+        Set<String> pluginKeys = getAllKeys(metadataRoot);\n+        for (String pluginKey : pluginKeys) {\n+            NBTTagCompound pluginData = metadataRoot.getCompound(pluginKey);\n+            if (pluginData.hasKey(metadataKey)) {\n+                MetadataValue value = convertToMetadata(pm.getPlugin(pluginKey), pluginData.get(metadataKey));\n+                if (value != null) {\n+                    metadata.add(value);\n+                }\n+            }\n+        }\n+        return Collections.unmodifiableList(metadata);\n+    }\n+\n+    /**\n+     * Retrieve a single key of stored metadata for a specific Plugin\n+     *\n+     * @param metadataKey The metadata to look up\n+     * @param owningPlugin The Plugin to look for data\n+     * @return A List of values found, or an empty List.\n+     */\n+    public MetadataValue getPluginMetadata(String metadataKey, Plugin owningPlugin) {\n+        NBTTagCompound metadataRoot = getPluginMetadataRoot(false);\n+        String pluginName = owningPlugin.getName();\n+        if (!metadataRoot.hasKey(pluginName)) {\n+            return null;\n+        }\n+        NBTTagCompound pluginTag = metadataRoot.getCompound(pluginName);\n+        if (!pluginTag.hasKey(metadataKey)) {\n+            return null;\n+        }\n+        return convertToMetadata(owningPlugin, pluginTag.get(metadataKey));\n+    }\n+\n+    public static PersistentMetadataValue convertToMetadata(Plugin plugin, NBTBase tag) {\n+        if (plugin == null || tag == null) return null;\n+\n+        Object converted = convert(tag);\n+        // Note that we don't currently export arrays as metadata\n+        if (converted instanceof String) {\n+            return new PersistentMetadataValue(plugin, (String)converted);\n+        } else if (converted instanceof Integer) {\n+            return new PersistentMetadataValue(plugin, (Integer)converted);\n+        } else if (converted instanceof Short) {\n+            return new PersistentMetadataValue(plugin, (Short)converted);\n+        } else if (converted instanceof Byte) {\n+            return new PersistentMetadataValue(plugin, (Byte)converted);\n+        } else if (converted instanceof Long) {\n+            return new PersistentMetadataValue(plugin, (Long)converted);\n+        } else if (converted instanceof Float) {\n+            return new PersistentMetadataValue(plugin, (Float)converted);\n+        } else if (converted instanceof Double) {\n+            return new PersistentMetadataValue(plugin, (Double)converted);\n+        } else if (converted instanceof Map) {\n+            return new PersistentMetadataValue(plugin, (Map<String, ?>)converted);\n+        } else if (converted instanceof List) {\n+            return new PersistentMetadataValue(plugin, (List<?>)converted);\n+        } else if (converted instanceof ConfigurationSerializable) {\n+            return new PersistentMetadataValue(plugin, (ConfigurationSerializable)converted);\n+        }\n+\n+        return null;\n+    }\n+\n+    /**\n+     * Check for existing metadata.\n+     *\n+     * @param metadataKey The key to check for\n+     * @return True if the key is present in this store\n+     */\n+    public boolean hasPluginMetadata(String metadataKey) {\n+        return hasPluginMetadata(tag, metadataKey);\n+    }\n+\n+    /**\n+     * Check for existing metadata registered to a specific Plugin.\n+     *\n+     * @param metadataKey The key to remove\n+     * @param owningPlugin the plugin that owns the data\n+     */\n+    public boolean hasPluginMetadata(String metadataKey, Plugin owningPlugin) {\n+        return hasPluginMetadata(tag, metadataKey, owningPlugin);\n+    }\n+\n+    /**\n+     * Remove data from this store.\n+     *\n+     * @param metadataKey The key to remove\n+     * @param owningPlugin The Plugin that owns this data.\n+     */\n+    public void removePluginMetadata(String metadataKey, Plugin owningPlugin) {\n+        NBTTagCompound metadataRoot = getPluginMetadataRoot(false);\n+        String pluginName = owningPlugin.getName();\n+\n+        if (!metadataRoot.hasKey(pluginName)) return;\n+\n+        NBTTagCompound pluginTag = metadataRoot.getCompound(pluginName);\n+        pluginTag.remove(metadataKey);\n+        if (pluginTag.isEmpty()) {\n+            metadataRoot.remove(pluginName);\n+            if (metadataRoot.isEmpty()) {\n+                NBTTagCompound bukkitRoot = tag.getCompound(BUKKIT_DATA_KEY);\n+                bukkitRoot.remove(PLUGIN_DATA_KEY);\n+                if (bukkitRoot.isEmpty()) {\n+                    tag.remove(BUKKIT_DATA_KEY);\n+                }\n+            }\n+        }\n+    }\n+\n+    /**\n+     * Retrieve the NBTTagCompound that holds all of the custom Bukkit data.\n+     *\n+     * @param create If True, the path to the root will be created if it does not exist.\n+     * @return The NBTTagCompound that holds this data\n+     */\n+    protected NBTTagCompound getBukkitDataRoot(boolean create) {\n+        NBTTagCompound bukkitRoot = tag.getCompound(BUKKIT_DATA_KEY);\n+        if (create) {\n+            tag.set(BUKKIT_DATA_KEY, bukkitRoot);\n+        }\n+        return bukkitRoot;\n+    }\n+\n+    /**\n+     * Check for a specific key of custom Bukkit data in this store.\n+     *\n+     * @param key The key to look for\n+     * @return True if the key is in this store under the bukkit root.\n+     */\n+    public boolean hasBukkitData(String key) {\n+        NBTTagCompound bukkitRoot = getBukkitDataRoot(false);\n+        return bukkitRoot.hasKey(key);\n+    }\n+\n+    /**\n+     * Store a raw Object in the custom Bukkit data store\n+     *\n+     * @param key The key to store\n+     * @param value The data to store\n+     */\n+    public void setBukkitData(String key, Object value) {\n+        NBTTagCompound bukkitRoot = getBukkitDataRoot(true);\n+        bukkitRoot.set(key, convert(value));\n+    }\n+\n+    /**\n+     * Retrieve a raw Object stored in the Bukkit custom data.\n+     *\n+     * @param key The key to retrieve\n+     * @return The object, if it exists in the store, else null.\n+     */\n+    public Object getBukkitData(String key) {\n+        NBTTagCompound bukkitRoot = getBukkitDataRoot(false);\n+        return convert(bukkitRoot.get(key));\n+    }\n+\n+    public int getBukkitDataAsInt(String key) {\n+        return NumberConversions.toInt(getBukkitData(key));\n+    }\n+\n+    public float getBukkitDataAsFloat(String key) {\n+        return NumberConversions.toFloat(getBukkitData(key));\n+    }\n+\n+    public double getBukkitDataAsDouble(String key) {\n+        return NumberConversions.toDouble(getBukkitData(key));\n+    }\n+\n+    public long getBukkitDataAsLong(String key) {\n+        return NumberConversions.toLong(getBukkitData(key));\n+    }\n+\n+    public short getBukkitDataAsShort(String key) {\n+        return NumberConversions.toShort(getBukkitData(key));\n+    }\n+\n+    public byte getBukkitDataAsByte(String key) {\n+        return NumberConversions.toByte(getBukkitData(key));\n+    }\n+\n+    public boolean getBukkitDataAsBoolean(String key) {\n+        Object value = getBukkitData(key);\n+        if (value instanceof Boolean) {\n+            return (Boolean) value;\n+        }\n+\n+        if (value instanceof Number) {\n+            return ((Number) value).intValue() != 0;\n+        }\n+\n+        if (value instanceof String) {\n+            return Boolean.parseBoolean((String) value);\n+        }\n+\n+        return value != null;\n+    }\n+\n+    public String getBukkitDataAsString(String key) {\n+        Object value = getBukkitData(key);\n+\n+        if (value == null) {\n+            return \"\";\n+        }\n+        return value.toString();\n+    }\n+\n+    /**\n+     * Remove a specific key of Bukkit data from this store.\n+     *\n+     * @param key The key to remove\n+     */\n+    public void removeBukkitData(String key) {\n+        NBTTagCompound bukkitRoot = getBukkitDataRoot(false);\n+        bukkitRoot.remove(key);\n+        if (bukkitRoot.isEmpty()) {\n+            tag.remove(BUKKIT_DATA_KEY);\n+        }\n+    }\n+\n+    /**\n+     * Apply the contents of this data store on top of an item tag.\n+     * <p>\n+     * This will overwrite any keys in the tag that are also in this\n+     * store.\n+     *\n+     * @param other The tag to write this data store to\n+     */\n+    public void applyToTag(NBTTagCompound other) {\n+        if (other == null) return;\n+\n+        Set<String> keys = getAllKeys(tag);\n+        for (String key : keys) {\n+            other.set(key, tag.get(key).clone());\n+        }\n+    }\n+\n+    /**\n+     * This will serialize all data into an ImmutableMap.Builder.\n+     *\n+     * @param builder An ImmutableMap builder to serialize this data store.\n+     * @return The same Map Builder\n+     */\n+    public ImmutableMap.Builder<String, Object> serialize(ImmutableMap.Builder<String, Object> builder) {\n+        Set<String> pluginKeys = getAllKeys(tag);\n+        for (String pluginKey : pluginKeys) {\n+            builder.put(pluginKey, convert(tag.get(pluginKey)));\n+        }\n+        return builder;\n+    }\n+\n+    /**\n+     * Convert an NBTBase object to an object of the appropriate type for\n+     * inclusion in our data map.\n+     * <p>\n+     * This will convert a compound tag into either a Map (used for object\n+     * deserialization) or a ConfigurationSection.\n+     * <p>\n+     * It is not possible to store a Map directly.\n+     *\n+     * @param tag The tag to convert and store\n+     * @return The converted object, or null if nothing was stored\n+     */\n+    private static Object convert(NBTBase tag) {\n+        if (tag == null) return null;\n+\n+        Object value = null;\n+        // This adds some extra reaching into NBT internals.\n+        if (tag instanceof NBTTagCompound) {\n+            NBTTagCompound compound = (NBTTagCompound)tag;\n+            Collection<String> keys = getAllKeys(compound);\n+\n+            // Check for Map, ConfigurationSection or SerliazebleObject creation\n+            boolean isSerializedObject = compound.hasKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY);\n+            Map<String, Object> dataMap = new HashMap<String, Object>();\n+            for (String tagKey : keys) {\n+                dataMap.put(tagKey, convert(compound.get(tagKey)));\n+            }\n+            if (isSerializedObject) {\n+                try {\n+                    value = ConfigurationSerialization.deserializeObject(dataMap);\n+                    if (value == null) {\n+                        throw new IllegalArgumentException(\"Failed to deserialize object of class \" + compound.get(ConfigurationSerialization.SERIALIZED_TYPE_KEY));\n+                    }\n+                } catch (Exception ex) {\n+                    ex.printStackTrace();\n+                    throw new IllegalArgumentException(\"Failed to deserialize object of class \" + compound.get(ConfigurationSerialization.SERIALIZED_TYPE_KEY) + \", \" + ex.getMessage());\n+                }\n+            } else {\n+                value = dataMap;\n+            }\n+        } else if (tag instanceof NBTTagString) {\n+            value = ((NBTTagString) tag).a_();\n+        } else if (tag instanceof NBTTagList) {\n+            NBTTagList list = (NBTTagList)tag;\n+            int tagSize = list.size();\n+            List<Object> convertedList = new ArrayList<Object>(tagSize);\n+            int listType = list.d();\n+            for (int i = 0; i < tagSize; i++) {\n+                // Convert to appropriate NBT object type\n+                Object listValue = null;\n+                switch (listType) {\n+                    case 10: // TagCompound\n+                        listValue = convert(list.get(i));\n+                        break;\n+                    case 1: // Byte\n+                        listValue = list.getByte(i);\n+                        break;\n+                    case 2: // Short\n+                        listValue = list.getShort(i);\n+                        break;\n+                    case 3: // Int\n+                        listValue = list.getInt(i);\n+                        break;\n+                    case 4: // Long\n+                        listValue = list.getLong(i);\n+                        break;\n+                    case 6: // Double","path":"src/main/java/org/bukkit/craftbukkit/util/NBTMetadataStore.java","position":553,"original_position":553,"commit_id":"60b18fbd63ed7cbca9859dbf1784668adce2e18c","original_commit_id":"60b18fbd63ed7cbca9859dbf1784668adce2e18c","user":{"login":"ST-DDT","id":1579362,"avatar_url":"https://avatars.githubusercontent.com/u/1579362?","gravatar_id":"9168201aca5c88c3545cfd442d772ee6","url":"https://api.github.com/users/ST-DDT","html_url":"https://github.com/ST-DDT","followers_url":"https://api.github.com/users/ST-DDT/followers","following_url":"https://api.github.com/users/ST-DDT/following{/other_user}","gists_url":"https://api.github.com/users/ST-DDT/gists{/gist_id}","starred_url":"https://api.github.com/users/ST-DDT/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ST-DDT/subscriptions","organizations_url":"https://api.github.com/users/ST-DDT/orgs","repos_url":"https://api.github.com/users/ST-DDT/repos","events_url":"https://api.github.com/users/ST-DDT/events{/privacy}","received_events_url":"https://api.github.com/users/ST-DDT/received_events","type":"User","site_admin":false},"body":"Why are Double and Float flipped?","created_at":"2014-05-26T18:01:01Z","updated_at":"2014-05-26T18:01:01Z","html_url":"https://github.com/Bukkit/CraftBukkit/pull/1376#discussion_r13053885","pull_request_url":"https://api.github.com/repos/Bukkit/CraftBukkit/pulls/1376","_links":{"self":{"href":"https://api.github.com/repos/Bukkit/CraftBukkit/pulls/comments/13053885"},"html":{"href":"https://github.com/Bukkit/CraftBukkit/pull/1376#discussion_r13053885"},"pull_request":{"href":"https://api.github.com/repos/Bukkit/CraftBukkit/pulls/1376"}}}}