package com.supaham.commons.bukkit.items; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.supaham.commons.bukkit.ItemBuilder; import com.supaham.commons.bukkit.serializers.ItemEnchantmentSerializer; import com.supaham.commons.bukkit.utils.ChatColorUtils; import com.supaham.commons.bukkit.utils.EnchantmentUtils; import com.supaham.commons.bukkit.utils.OBCUtils; import com.supaham.commons.bukkit.utils.SerializationUtils; import org.apache.commons.lang.StringUtils; import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.Repairable; import org.bukkit.inventory.meta.SkullMeta; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import pluginbase.config.serializers.Serializer; import pluginbase.config.serializers.SerializerSet; /** * This is not an actual {@link Serializer}, merely a deserializer for {@link ItemMeta} with * additional parameters. * <p /> * The deserialization basically checks for keys with {@link ItemBuilder}'s method names, such as * {@link ItemBuilder#name(String)}, {@link ItemBuilder#lore(String...)}, {@link * ItemBuilder#bookTitle(String)}, etc. * <p /> * The following table describes what properties can be of what type: <br /> * * <table> * <thead> * <tr> * <th>Property</th> * <th>Applicable Type(s)</th> * </tr> * </thead> * * <tr> * <td>name</td> * <td>String</td> * </tr> * <tr> * <td>lore</td> * <td>String<br>List<String></td> * </tr> * <tr> * <td>glow</td> * <td>boolean</td> * </tr> * <tr> * <td>unbreakable</td> * <td>boolean</td> * </tr> * <tr> * <td>flags</td> * <td>String<br>List<String><br>See: {@link ItemFlag#valueOf(String)}</td> * </tr> * <tr> * <td>enchant<br>enchants</td> * <td>String<br>List<String><br>See: {@link ItemEnchantmentSerializer}</td> * </tr> * <tr> * <td>repairCost</td> * <td>int</td> * </tr> * <tr> * <td>bookTitle</td> * <td>String</td> * </tr> * <tr> * <td>bookAuthor</td> * <td>String</td> * </tr> * <tr> * <td>bookPage<br>bookPages</td> * <td>String<br>List<String></td> * </tr> * <tr> * <td>fw</td> * <td>not implemented yet.</td> * </tr> * <tr> * <td>color</td> * <td>String<br>int</td> * </tr> * <tr> * <td>mapScale</td> * <td>boolean</td> * </tr> * <tr> * <td>potion</td> * <td>not implemented yet.</td> * </tr> * <tr> * <td>skull</td> * <td>String</td> * </tr> * <tr> * <td>banner</td> * <td>not implemented yet.</td> * </tr> * </table> * * @see #deserialize(ItemStack, Map) * @see #deserialize(ItemBuilder, Map) * @since 0.1 */ public class ItemMetaSerializer { /** * Serializes an {@link ItemStack} into a {@link Map} of {@link String} and {@link Object}s. * * @param item itemstack to serialize * * @return map of serialized data * * @see #deserialize(ItemBuilder, Map) */ public static Map<String, Object> serialize(ItemStack item) { Map<String, Object> map = new LinkedHashMap<>(); ItemMeta im = item.getItemMeta(); if (im.hasDisplayName()) { map.put("name", ChatColorUtils.serialize(im.getDisplayName())); } if (im.hasLore()) { List<String> result = new ArrayList<>(); for (String s : im.getLore()) { result.add(ChatColorUtils.serialize(s).toString()); } map.put("lore", result.size() == 1 ? result.get(0) : result); } if (im.hasEnchant(EnchantmentUtils.GLOW_ENCHANTMENT)) { map.put("glow", true); } if (im.spigot().isUnbreakable()) { map.put("unbreakable", true); } if (im.getItemFlags().size() > 0) { Set<ItemFlag> flags = im.getItemFlags(); map.put("flags", flags.size() == 1 ? Iterables.get(flags, 0) : flags); } { ItemEnchantmentSerializer ser = SerializationUtils.getSerializer(ItemEnchantmentSerializer.class); List<Object> result = new ArrayList<>(); for (Entry<Enchantment, Integer> entry : im.getEnchants().entrySet()) { result.add(ser.serialize(new ItemEnchantment(entry.getKey(), entry.getValue()), SerializerSet.defaultSet())); } if (!result.isEmpty()) { map.put("enchants", result.size() == 1 ? result.get(0) : result); } } if (((Repairable) im).getRepairCost() != 0) { map.put("repairCost", ((Repairable) im).getRepairCost()); } if (im instanceof BookMeta) { BookMeta bm = ((BookMeta) im); map.put("bookTitle", bm.getTitle()); map.put("bookAuthor", bm.getAuthor()); map.put("bookPages", bm.getPages()); } if (im instanceof LeatherArmorMeta) { LeatherArmorMeta lam = ((LeatherArmorMeta) im); map.put("color", lam.getColor().asRGB()); } if (im instanceof MapMeta) { map.put("mapScale", ((MapMeta) im).isScaling()); } if (im instanceof SkullMeta) { map.put("skull", ((SkullMeta) im).getOwner()); } return map; } /** * Deserializes a {@link Map} of Strings and Objects which represents a serialized {@link * ItemMeta} metadata, which is later applied to the given {@link ItemStack}. * * @param item item to append deserialized data to * @param map serialized metadata * * @return {@link ItemBuilder#build()} * * @see #deserialize(ItemBuilder, Map) */ public static ItemStack deserialize(ItemStack item, Map<String, Object> map) { Preconditions.checkNotNull(item, "item cannot be null."); Preconditions.checkNotNull(map, "map cannot be null."); if (map.isEmpty() || item.getType() == Material.AIR) { return item; } return deserialize(ItemBuilder.builder(new ItemStack(item.getType(), item.getAmount(), item.getDurability())), map); } /** * Deserializes a {@link Map} of Strings and Objects which represents a serialized {@link * ItemMeta} metadata, which is later applied to the given {@link ItemBuilder}. * * @param builder item builder to append deserialized data to * @param map serialized metadata * * @return {@link ItemBuilder#build()} */ public static ItemStack deserialize(ItemBuilder builder, Map<String, Object> map) { Preconditions.checkNotNull(builder, "builder cannot be null."); Preconditions.checkNotNull(map, "map cannot be null."); if (map.isEmpty()) { return builder.build(); } for (Entry<String, Object> entry : map.entrySet()) { Object val = entry.getValue(); switch (entry.getKey()) { case "name": builder.name(ChatColorUtils.deserialize(val.toString())); break; case "lore": if (val instanceof String) { builder.lore(ChatColorUtils.deserialize(val.toString())); } else if (val instanceof List) { List<String> result = new ArrayList<>(); for (String s : (List<String>) val) { result.add(ChatColorUtils.deserialize(s)); } builder.lore(result); } else { throw new UnsupportedOperationException("lore is of type " + val.getClass()); } break; case "glow": builder.glow(Boolean.valueOf(val.toString())); break; case "unbreakable": builder.unbreakable(Boolean.valueOf(val.toString())); break; case "flags": if (val instanceof String) { builder.flag(ItemFlag.valueOf(val.toString().toUpperCase())); } else if (val instanceof List) { for (Object o : ((List) val)) { builder.flag(ItemFlag.valueOf(o.toString())); } } else { throw new UnsupportedOperationException("flags is of type " + val.getClass()); } break; case "enchants": ItemEnchantmentSerializer serializer = SerializationUtils.getSerializer(ItemEnchantmentSerializer.class); if (val instanceof String) { builder.enchant(serializer.deserialize(val.toString(), ItemEnchantment.class, SerializerSet.defaultSet())); } else if (val instanceof List) { for (String str : ((List<String>) val)) { builder.enchant(serializer.deserialize(str, ItemEnchantment.class, SerializerSet.defaultSet())); } } else { throw new UnsupportedOperationException("enchants is of type " + val.getClass()); } break; case "repairCost": builder.repairCost((int) val); break; case "bookTitle": builder.bookTitle(val.toString()); break; case "bookAuthor": builder.bookAuthor(val.toString()); break; case "bookPage": case "bookPages": if (val instanceof String) { builder.bookAdd(val.toString()); } else if (val instanceof List) { for (String page : ((List<String>) val)) { builder.bookAdd(page); } } else { throw new UnsupportedOperationException("book pages is of type " + val.getClass()); } break; case "fw": throw new UnsupportedOperationException("not implemented yet"); // TODO implement case "color": if (val instanceof String) { String[] split = ((String) val).split("\\s*,\\s*"); if (split.length > 1) { Preconditions.checkArgument(split.length >= 3, "Invalid R,G,B syntax '" + val + "'."); builder .armorColor(Color.fromRGB(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]))); } else { if (StringUtils.isNumeric(split[0])) { // in case the single rgb value was defined as a string builder.armorColor(Color.fromRGB(Integer.parseInt(split[0]))); } else { // String representation builder.armorColor(OBCUtils.getColorByName(split[0])); } } } else if (val instanceof Integer) { builder.armorColor(Color.fromRGB(((int) val))); } else { throw new UnsupportedOperationException("armor color is of type " + val.getClass()); } break; case "mapScale": builder.mapScale((boolean) val); break; case "potion": throw new UnsupportedOperationException("not implemented yet"); // TODO implement case "skull": builder.skull(val.toString()); break; case "banner": throw new UnsupportedOperationException("not implemented yet"); // TODO implement } } return builder.build(); } }