summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java')
-rw-r--r--src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java510
1 files changed, 510 insertions, 0 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
new file mode 100644
index 00000000..87709d73
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
@@ -0,0 +1,510 @@
+package org.bukkit.craftbukkit.inventory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import net.minecraft.server.NBTBase;
+import net.minecraft.server.NBTTagCompound;
+import net.minecraft.server.NBTTagList;
+import net.minecraft.server.NBTTagString;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Material;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.configuration.serialization.DelegateDeserialization;
+import org.bukkit.configuration.serialization.SerializableAs;
+import org.bukkit.craftbukkit.Overridden;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.Repairable;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Children must include the following:
+ *
+ * <li> Constructor(CraftMetaItem meta)
+ * <li> Constructor(NBTTagCompound tag)
+ * <li> Constructor(Map<String, Object> map)
+ * <br><br>
+ * <li> void applyToItem(NBTTagCompound tag)
+ * <li> boolean applicableTo(Material type)
+ * <br><br>
+ * <li> boolean notUncommon(CraftMetaItem meta)
+ * <li> boolean equalsCommon(CraftMetaItem meta)
+ * <br><br>
+ * <li> boolean isEmpty()
+ * <li> boolean is{Type}Empty()
+ * <br><br>
+ * <li> int applyHash()
+ * <li> public Class clone()
+ * <br><br>
+ * <li> Builder<String, Object> serialize(Builder<String, Object> builder)
+ * <li> SerializableMeta.Deserializers deserializer()
+ */
+@DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
+class CraftMetaItem implements ItemMeta, Repairable {
+ static class ItemMetaKey {
+ final String BUKKIT;
+ final String NBT;
+
+ ItemMetaKey(final String both) {
+ this(both, both);
+ }
+
+ ItemMetaKey(final String nbt, final String bukkit) {
+ this.NBT = nbt;
+ this.BUKKIT = bukkit;
+ }
+ }
+
+ @SerializableAs("ItemMeta")
+ public static class SerializableMeta implements ConfigurationSerializable {
+ static final String TYPE_FIELD = "meta-type";
+
+ enum Deserializers {
+ BOOK {
+ @Override
+ CraftMetaBook deserialize(Map<String, Object> map) {
+ return new CraftMetaBook(map);
+ }
+ },
+ SKULL {
+ @Override
+ CraftMetaSkull deserialize(Map<String, Object> map) {
+ return new CraftMetaSkull(map);
+ }
+ },
+ LEATHER_ARMOR {
+ @Override
+ CraftMetaLeatherArmor deserialize(Map<String, Object> map) {
+ return new CraftMetaLeatherArmor(map);
+ }
+ },
+ MAP {
+ @Override
+ CraftMetaMap deserialize(Map<String, Object> map) {
+ return new CraftMetaMap(map);
+ }
+ },
+ POTION {
+ @Override
+ CraftMetaPotion deserialize(Map<String, Object> map) {
+ return new CraftMetaPotion(map);
+ }
+ },
+ UNSPECIFIC {
+ @Override
+ CraftMetaItem deserialize(Map<String, Object> map) {
+ return new CraftMetaItem(map);
+ }
+ };
+
+ abstract CraftMetaItem deserialize(Map<String, Object> map);
+ }
+
+ private SerializableMeta() {
+ }
+
+ public static ItemMeta deserialize(Map<String, Object> map) {
+ Validate.notNull(map, "Cannot deserialize null map");
+
+ String type = getString(map, TYPE_FIELD, false);
+ Deserializers deserializer = Deserializers.valueOf(type);
+
+ if (deserializer == null) {
+ throw new IllegalArgumentException(type + " is not a valid " + TYPE_FIELD);
+ }
+
+ return deserializer.deserialize(map);
+ }
+
+ public Map<String, Object> serialize() {
+ throw new AssertionError();
+ }
+
+ static String getString(Map<?, ?> map, Object field, boolean nullable) {
+ return getObject(String.class, map, field, nullable);
+ }
+
+ static boolean getBoolean(Map<?, ?> map, Object field) {
+ Boolean value = getObject(Boolean.class, map, field, true);
+ return value != null && value;
+ }
+
+ static <T> T getObject(Class<T> clazz, Map<?, ?> map, Object field, boolean nullable) {
+ final Object object = map.get(field);
+
+ if (clazz.isInstance(object)) {
+ return clazz.cast(object);
+ }
+ if (object == null) {
+ if (!nullable) {
+ throw new NoSuchElementException(map + " does not contain " + field);
+ }
+ return null;
+ }
+ throw new IllegalArgumentException(field + "(" + object + ") is not a valid " + clazz);
+ }
+ }
+
+ static final ItemMetaKey NAME = new ItemMetaKey("Name", "display-name");
+ static final ItemMetaKey DISPLAY = new ItemMetaKey("display");
+ static final ItemMetaKey LORE = new ItemMetaKey("Lore", "lore");
+ static final ItemMetaKey ENCHANTMENTS = new ItemMetaKey("ench", "enchants");
+ static final ItemMetaKey ENCHANTMENTS_ID = new ItemMetaKey("id");
+ static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl");
+ static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost");
+
+ private String displayName;
+ private List<String> lore;
+ private Map<Enchantment, Integer> enchantments;
+ private int repairCost;
+
+ CraftMetaItem(CraftMetaItem meta) {
+ if (meta == null) {
+ return;
+ }
+
+ this.displayName = meta.displayName;
+
+ if (meta.hasLore()) {
+ this.lore = new ArrayList<String>(meta.lore);
+ }
+
+ if (meta.hasEnchants()) {
+ this.enchantments = new HashMap<Enchantment, Integer>(meta.enchantments);
+ }
+
+ this.repairCost = meta.repairCost;
+ }
+
+ CraftMetaItem(NBTTagCompound tag) {
+ if (tag.hasKey(DISPLAY.NBT)) {
+ NBTTagCompound display = tag.getCompound(DISPLAY.NBT);
+
+ if (display.hasKey(NAME.NBT)) {
+ displayName = display.getString(NAME.NBT);
+ }
+
+ if (display.hasKey(LORE.NBT)) {
+ NBTTagList list = display.getList(LORE.NBT);
+ lore = new ArrayList<String>(list.size());
+
+ for (int index = 0; index < list.size(); index++) {
+ String line = ((NBTTagString) list.get(index)).data;
+ lore.add(line);
+ }
+ }
+ }
+
+ if (tag.hasKey(ENCHANTMENTS.NBT)) {
+ NBTTagList ench = tag.getList(ENCHANTMENTS.NBT);
+ enchantments = new HashMap<Enchantment, Integer>(ench.size());
+
+ for (int i = 0; i < ench.size(); i++) {
+ short id = ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_ID.NBT);
+ short level = ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_LVL.NBT);
+
+ addEnchant(Enchantment.getById(id), (int) level, true);
+ }
+ }
+
+ if (tag.hasKey(REPAIR.NBT)) {
+ repairCost = tag.getInt(REPAIR.NBT);
+ }
+ }
+
+ CraftMetaItem(Map<String, Object> map) {
+ setDisplayName(SerializableMeta.getString(map, NAME.BUKKIT, true));
+
+ if (map.containsKey(LORE.BUKKIT)) {
+ lore = (List<String>) map.get(LORE.BUKKIT);
+ }
+
+ Map<?, ?> ench = SerializableMeta.getObject(Map.class, map, ENCHANTMENTS.BUKKIT, true);
+ if (ench != null) {
+ enchantments = new HashMap<Enchantment, Integer>(ench.size());
+ for (Map.Entry<?, ?> entry : ench.entrySet()) {
+ Enchantment enchantment = Enchantment.getByName(entry.getKey().toString());
+
+ if ((enchantment != null) && (entry.getValue() instanceof Integer)) {
+ addEnchant(enchantment, (Integer) entry.getValue(), true);
+ }
+ }
+ }
+
+ if (map.containsKey(REPAIR.BUKKIT)) {
+ repairCost = (Integer) map.get(REPAIR.BUKKIT);
+ }
+ }
+
+ @Overridden
+ void applyToItem(NBTTagCompound itemTag) {
+ if (hasDisplayName()) {
+ setDisplayTag(itemTag, NAME.NBT, new NBTTagString(NAME.NBT, displayName));
+ }
+
+ if (hasLore()) {
+ NBTTagList list = new NBTTagList(LORE.NBT);
+ for (int i = 0; i < lore.size(); i++) {
+ list.add(new NBTTagString(String.valueOf(i), lore.get(i)));
+ }
+ setDisplayTag(itemTag, LORE.NBT, list);
+ }
+
+ if (hasEnchants()) {
+ NBTTagList list = new NBTTagList(ENCHANTMENTS.NBT);
+
+ for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
+ NBTTagCompound subtag = new NBTTagCompound();
+
+ subtag.setShort(ENCHANTMENTS_ID.NBT, (short) entry.getKey().getId());
+ subtag.setShort(ENCHANTMENTS_LVL.NBT, (short) (int) entry.getValue());
+
+ list.add(subtag);
+ }
+
+ itemTag.set(ENCHANTMENTS.NBT, list);
+ }
+
+ if (hasRepairCost()) {
+ itemTag.setInt(REPAIR.NBT, repairCost);
+ }
+ }
+
+ void setDisplayTag(NBTTagCompound tag, String key, NBTBase value) {
+ final NBTTagCompound display = tag.getCompound(DISPLAY.NBT);
+
+ if (!tag.hasKey(DISPLAY.NBT)) {
+ tag.setCompound(DISPLAY.NBT, display);
+ }
+
+ display.set(key, value);
+ }
+
+ @Overridden
+ boolean applicableTo(Material type) {
+ return type != Material.AIR;
+ }
+
+ @Overridden
+ boolean isEmpty() {
+ return !(hasDisplayName() || hasEnchants() || hasLore());
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public final void setDisplayName(String name) {
+ this.displayName = name;
+ }
+
+ public boolean hasDisplayName() {
+ return !Strings.isNullOrEmpty(displayName);
+ }
+
+ public boolean hasLore() {
+ return this.lore != null && !this.lore.isEmpty();
+ }
+
+ public boolean hasRepairCost() {
+ return repairCost > 0;
+ }
+
+ public boolean hasEnchant(Enchantment ench) {
+ return hasEnchants() ? enchantments.containsKey(ench) : false;
+ }
+
+ public int getEnchantLevel(Enchantment ench) {
+ Integer level = hasEnchants() ? enchantments.get(ench) : null;
+ if (level == null) {
+ return 0;
+ }
+ return level;
+ }
+
+ public Map<Enchantment, Integer> getEnchants() {
+ return hasEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.<Enchantment, Integer>of();
+ }
+
+ public boolean addEnchant(Enchantment ench, int level, boolean ignoreRestrictions) {
+ if (enchantments == null) {
+ enchantments = new HashMap<Enchantment, Integer>(4);
+ }
+
+ if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) {
+ Integer old = enchantments.put(ench, level);
+ return old == null || old != level;
+ }
+ return false;
+ }
+
+ public boolean removeEnchant(Enchantment ench) {
+ return hasEnchants() ? enchantments.remove(ench) != null : false;
+ }
+
+ public boolean hasEnchants() {
+ return !(enchantments == null || enchantments.isEmpty());
+ }
+
+ public List<String> getLore() {
+ return this.lore == null ? null : new ArrayList<String>(this.lore);
+ }
+
+ public void setLore(List<String> lore) { // too tired to think if .clone is better
+ if (lore == null) {
+ this.lore = null;
+ } else {
+ if (this.lore == null) {
+ safelyAdd(lore, this.lore = new ArrayList<String>(lore.size()), Integer.MAX_VALUE);
+ } else {
+ this.lore.clear();
+ safelyAdd(lore, this.lore, Integer.MAX_VALUE);
+ }
+ }
+ }
+
+ public int getRepairCost() {
+ return repairCost;
+ }
+
+ public void setRepairCost(int cost) { // TODO: Does this have limits?
+ repairCost = cost;
+ }
+
+ @Override
+ public final boolean equals(Object object) {
+ if (object == null) {
+ return false;
+ }
+ if (object == this) {
+ return true;
+ }
+ if (!(object instanceof CraftMetaItem)) {
+ return false;
+ }
+ return CraftItemFactory.instance().equals(this, (ItemMeta) object);
+ }
+
+ /**
+ * This method is almost as weird as notUncommon.
+ * Only return false if your common internals are unequal.
+ * Checking your own internals is redundant if you are not common, as notUncommon is meant for checking those 'not common' variables.
+ */
+ @Overridden
+ boolean equalsCommon(CraftMetaItem that) {
+ return ((this.hasDisplayName() ? that.hasDisplayName() && this.displayName.equals(that.displayName) : !that.hasDisplayName()))
+ && (this.hasEnchants() ? that.hasEnchants() && this.enchantments.equals(that.enchantments) : !that.hasEnchants())
+ && (this.hasLore() ? that.hasLore() && this.lore.equals(that.lore) : !that.hasLore())
+ && (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost());
+ }
+
+ /**
+ * This method is a bit weird...
+ * Return true if you are a common class OR your uncommon parts are empty.
+ * Empty uncommon parts implies the NBT data would be equivalent if both were applied to an item
+ */
+ @Overridden
+ boolean notUncommon(CraftMetaItem meta) {
+ return true;
+ }
+
+ @Override
+ public final int hashCode() {
+ return applyHash();
+ }
+
+ @Overridden
+ int applyHash() {
+ int hash = 3;
+ hash = 61 * hash + (hasDisplayName() ? this.displayName.hashCode() : 0);
+ hash = 61 * hash + (hasLore() ? this.lore.hashCode() : 0);
+ hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0);
+ hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0);
+ return hash;
+ }
+
+ @Overridden
+ @Override
+ public CraftMetaItem clone() {
+ try {
+ return (CraftMetaItem) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new Error(e);
+ }
+ }
+
+ public final Map<String, Object> serialize() {
+ ImmutableMap.Builder<String, Object> map = ImmutableMap.builder();
+ map.put(SerializableMeta.TYPE_FIELD, deserializer().name());
+ serialize(map);
+ return map.build();
+ }
+
+ @Overridden
+ ImmutableMap.Builder<String, Object> serialize(ImmutableMap.Builder<String, Object> builder) {
+ if (hasDisplayName()) {
+ builder.put(NAME.BUKKIT, displayName);
+ }
+
+ if (hasLore()) {
+ builder.put(LORE.BUKKIT, ImmutableList.copyOf(lore));
+ }
+
+ if (hasEnchants()) {
+ ImmutableMap.Builder<String, Integer> enchantments = ImmutableMap.builder();
+ for (Map.Entry<Enchantment, Integer> enchant : this.enchantments.entrySet()) {
+ enchantments.put(enchant.getKey().getName(), enchant.getValue());
+ }
+ builder.put(ENCHANTMENTS.BUKKIT, enchantments.build());
+ }
+
+ if (hasRepairCost()) {
+ builder.put(REPAIR.BUKKIT, repairCost);
+ }
+
+ return builder;
+ }
+
+ @Overridden
+ SerializableMeta.Deserializers deserializer() {
+ return SerializableMeta.Deserializers.UNSPECIFIC;
+ }
+
+ static void safelyAdd(Collection<?> addFrom, Collection<String> addTo, int maxItemLength) {
+ if (addFrom == null) {
+ return;
+ }
+
+ for (Object object : addFrom) {
+ if (!(object instanceof String)) {
+ if (object != null) {
+ throw new IllegalArgumentException(addFrom + " cannot contain non-string " + object.getClass().getName());
+ }
+
+ addTo.add("");
+ } else {
+ String page = object.toString();
+
+ if (page.length() > maxItemLength) {
+ page = page.substring(0, maxItemLength);
+ }
+
+ addTo.add(page);
+ }
+ }
+ }
+
+ @Override
+ public final String toString() {
+ return serialize().toString(); // TODO: cry
+ }
+}