package org.shininet.bukkit.itemrenamer; import java.util.Map; import java.util.WeakHashMap; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.inventory.ItemStack; import org.shininet.bukkit.itemrenamer.utils.CollectionsUtil; public class SelectedItemTracker { // The selected pack and item for each sender private final Map<CommandSender, ItemStack> selectedItem = new WeakHashMap<CommandSender, ItemStack>(); // The Bukkit listener private Listener bukkitListener; /** * Retrieve the listener that is registered to interface with the Bukkit event systen. * @return The Bukkit event listener. */ public Listener getBukkitListener() { if (bukkitListener == null) { bukkitListener = new Listener() { @EventHandler public void onPlayerInventory(InventoryClickEvent e) { if (e.getWhoClicked() instanceof Player) { Player clicker = (Player) e.getWhoClicked(); onNewSelected(clicker, clicker.getItemInHand()); } } @EventHandler public void onPlayerItemHeldEvent(PlayerItemHeldEvent e) { Player player = e.getPlayer(); onNewSelected(player, e.getPlayer().getInventory().getItem(e.getNewSlot())); } }; } return bukkitListener; } /** * Invoked when a player changes the selected item. * @param clicker - the player. * @param changed - the new selected item, or NULL if we deselected. */ private void onNewSelected(Player clicker, ItemStack changed) { ItemStack selected = getSelected(clicker); if (selected != null && !selected.equals(changed)) { ItemStack deselected = deselectCurrent(clicker); // Inform this player if (isValid(deselected)) { clicker.sendMessage(ChatColor.GOLD + "Deselected " + deselected); } } } /** * Retrieve the selected item stack. * @param sender - the command sender. * @return The selected item stack. */ public ItemStack getSelected(CommandSender sender) { return selectedItem.get(sender); } /** * Select the item currently hold by the command sender. * @param sender - the sender. * @return The previously selected item, or NULL if none was previously selected. * @throws CommandErrorException If the sender cannot select an item, or has no selected item. */ public ItemStack selectCurrent(CommandSender sender) { validateSender(sender); Player player = (Player) sender; ItemStack stack = getItemToSelect(player); // Ensure that this item is selected if (isValid(stack)) { return selectedItem.put(sender, stack); } else { throw new CommandErrorException("Must hold an item to select."); } } /** * Determine if the current item can only be matched by an exact lookup. * @return TRUE if it does, FALSE otherwise. */ public boolean hasExactSelector(CommandSender sender) { ItemStack stack = getSelected(sender); // This is the case if the item has any additional data (NBT) or enchantments if (isValid(stack)) { return !CollectionsUtil.isEmpty(stack.getEnchantments()) || stack.hasItemMeta(); } return false; } /** * Retrieve the item that will be selected. * @param sender - the sender. * @return The item that will be selected, or NULL if none can be selected. */ public ItemStack getItemToSelect(CommandSender sender) { validateSender(sender); return ((Player) sender).getItemInHand(); } /** * Deselect any currently selected item. * @param sender - the sender. * @return The previously selected item, or NULL. * @throws CommandErrorException If this sender cannot select anything. */ public ItemStack deselectCurrent(CommandSender sender) { validateSender(sender); return selectedItem.remove(sender); } /** * Determine if a given item stack is non-null and not AIR. * @param stack - the stack to test. * @return TRUE if the stack is non-null and non-air, FALSE otherwise. */ private boolean isValid(ItemStack stack) { return stack != null && !Material.AIR.equals(stack.getType()); } /** * Throw an exception if the given command sender is not a player. * @param sender - the sender to test. */ private void validateSender(CommandSender sender) { if (!(sender instanceof Player)) throw new CommandErrorException("Select item can only be called by players."); } }