package com.supaham.commons.bukkit; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Objects; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BannerMeta; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.FireworkEffectMeta; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.Repairable; import org.bukkit.inventory.meta.SkullMeta; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Represents an {@link ItemStack} matcher. This class operates around {@link ItemMatcherPredicate} * to produce the boolean value that states whether two item stacks match. This class already comes * with many predicates that is basically a broken down version of each {@link ItemMeta} class. * All of the provided predicates in this class are static fields and are suffixed with * {@code _PREDICATE} * * @see #ItemMatcher(boolean) * @since 0.1 */ public class ItemMatcher { /** * This predicate checks whether two items are similar. * * @see ItemStack#isSimilar(ItemStack) */ public static final ItemMatcherPredicate IS_SIMILAR_PREDICATE = new IsSimilarPredicate(); /** * This predicate checks whether two item types are equal. * * @see ItemStack#getType() */ public static final ItemMatcherPredicate TYPE_PREDICATE = new TypePredicate(); /** * This predicate checks whether two items are of the same amount. * * @see ItemStack#getAmount() */ public static final ItemMatcherPredicate AMOUNT_PREDICATE = new AmountPredicate(); /** * This predicate checks whether two items are of the same durability. * * @see ItemStack#getDurability() */ public static final ItemMatcherPredicate DURABILITY_PREDICATE = new DurabilityPredicate(); /** * This predicate checks whether two items are of the same display name. This returns true * if both items don't have the display name set. * * @see ItemMeta#getDisplayName() */ public static final ItemMatcherPredicate NAME_PREDICATE = new NamePredicate(); /** * This predicate checks whether two items are of the same lore. This returns true if both * items don't have the lore set. * * @see ItemMeta#getLore() */ public static final ItemMatcherPredicate LORE_PREDICATE = new LorePredicate(); /** * This predicate checks whether two items have the same enchantments. This returns true * if both items don't have any enchantments. * * @see ItemMeta#getEnchants() */ public static final ItemMatcherPredicate ENCHANTMENTS_PREDICATE = new EnchantmentsPredicate(); /** * This predicate checks whether two items have the same repair cost. * * @see Repairable#getRepairCost() */ public static final ItemMatcherPredicate REPAIR_COST_PREDICATE = new RepairCostPredicate(); /** * This predicate checks whether two books have the same title. * * @see BookMeta#getTitle() */ public static final ItemMatcherPredicate BOOK_TITLE_PREDICATE = new BookTitlePredicate(); /** * This predicate checks whether two books have the same author. * * @see BookMeta#getAuthor() */ public static final ItemMatcherPredicate BOOK_AUTHOR_PREDICATE = new BookAuthorPredicate(); /** * This predicate checks whether two books have the same pages. * * @see BookMeta#getPages() */ public static final ItemMatcherPredicate BOOK_PAGES_PREDICATE = new BookPagesPredicate(); /** * This predicate checks whether two items have the same firework effects. Supports both * {@link FireworkMeta} and {@link FireworkEffectMeta}. * * @see FireworkMeta#getEffects() * @see FireworkEffectMeta#getEffect() */ public static final ItemMatcherPredicate FIREWORK_EFFECTS_PREDICATE = new FireworkEffectsPredicate(); /** * This predicate checks whether two fireworks have the same power. * * @see FireworkMeta#getPower() */ public static final ItemMatcherPredicate FIREWORK_POWER_PREDICATE = new FireworkPowerPredicate(); /** * This predicate checks whether two leather armor have the same color. * * @see LeatherArmorMeta#getColor() */ public static final ItemMatcherPredicate ARMOR_COLOR_PREDICATE = new ArmorColorPredicate(); /** * This predicate checks whether two maps scale. * * @see MapMeta#isScaling() */ public static final ItemMatcherPredicate MAP_SCALE_PREDICATE = new MapScalePredicate(); /** * This predicate checks whether two potions have the same custom effects. * * @see PotionMeta#getCustomEffects() */ public static final ItemMatcherPredicate POTION_EFFECTS_PREDICATE = new PotionEffectsPredicate(); /** * This predicate checks whether two skulls have the same owner. * * @see SkullMeta#getOwner() */ public static final ItemMatcherPredicate SKULL_OWNER_PREDICATE = new SkullOwnerPredicate(); /** * This predicate checks whether two banners have the same base color. * * @see BannerMeta#getBaseColor() */ public static final ItemMatcherPredicate BANNER_COLOR_PREDICATE = new BannerColorPredicate(); /** * This predicate checks whether two banners have the same patterns. * * @see BannerMeta#getPatterns() */ public static final ItemMatcherPredicate BANNER_PATTERNS_PREDICATE = new BannerPatternsPredicate(); private final Set<ItemMatcherPredicate> predicates = new HashSet<>(); // Used by our predicates. private static Boolean precondition(@Nullable ItemStack item1, @Nullable ItemStack item2, boolean meta) { if (item1 == null && item2 == null) { return true; } else if ((item1 == null || item2 == null)) { return false; } else if (meta) { if (!item1.hasItemMeta() && !item2.hasItemMeta()) { return true; } else if (!item1.hasItemMeta() || !item2.hasItemMeta()) { return false; } } return null; } /** * Constructs a new item matcher. * * @param addDefaultPredicates whether to add the default predicates see {@link * #addDefaultPredicates()} */ public ItemMatcher(boolean addDefaultPredicates) { if (addDefaultPredicates) { addDefaultPredicates(); } } /** * Adds the following default predicates: * <ul> * <li>{@link #IS_SIMILAR_PREDICATE}</li> * </ul> * * @return this item match instance for chaining */ public ItemMatcher addDefaultPredicates() { addPredicates(IS_SIMILAR_PREDICATE); return this; } /** * Adds an array of {@link ItemMatcherPredicate}s to this item matcher. * * @param predicates predicates to add * * @return this item match instance for chaining */ public ItemMatcher addPredicates(@Nonnull ItemMatcherPredicate... predicates) { addPredicates(Arrays.asList(predicates)); return this; } /** * Adds multiple {@link ItemMatcherPredicate}s to this item matcher. * * @param predicates predicates to add * * @return this item match instance for chaining */ public ItemMatcher addPredicates(Iterable<ItemMatcherPredicate> predicates) { for (ItemMatcherPredicate predicate : predicates) { checkNotNull(predicate, "predicate cannot be null."); this.predicates.add(predicate); } return this; } /** * Checks whether two given {@link ItemStack}s match each other. This returns false the * moment any of the added predicates return false when * {@link ItemMatcherPredicate#apply(ItemStack, ItemStack)} is called. * <p /> * If this item matcher doesn't have any {@link ItemMatcherPredicate}s false is always returned. * * @param i1 first item to match with {@code i2} * @param i2 second item to match with {@code i1} * * @return whether both item stacks match each other */ public boolean match(@Nullable ItemStack i1, @Nullable ItemStack i2) { if (this.predicates.isEmpty()) { return false; } for (ItemMatcherPredicate predicate : this.predicates) { if (!predicate.apply(i1, i2)) { return false; } } return true; } /** * Represents a predicate interface that passes two {@link ItemStack}s. */ public static interface ItemMatcherPredicate { /** * Returns the result of applying this predicate to {@code item1} and {@code item2}. This * method is <i>generally expected</i>, but not absolutely required, to have the following * properties: * * <ul> * <li>Its execution does not cause any observable side effects. * <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal * Objects.equal}{@code (a, b)} implies that {@code predicate.apply(a) == * predicate.apply(b))}. * </ul> * * @throws NullPointerException if {@code input} is null and this predicate does not accept * null * arguments */ boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2); } private static final class IsSimilarPredicate implements ItemMatcherPredicate { private IsSimilarPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, false); return b != null ? b : item1.isSimilar(item2); } } private static final class TypePredicate implements ItemMatcherPredicate { private TypePredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, false); return b != null ? b : item1.getType() == item2.getType(); } } private static final class AmountPredicate implements ItemMatcherPredicate { private AmountPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, false); return b != null ? b : item1.getAmount() == item2.getAmount(); } } private static final class DurabilityPredicate implements ItemMatcherPredicate { private DurabilityPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, false); return b != null ? b : item1.getDurability() == item2.getDurability(); } } private static final class NamePredicate implements ItemMatcherPredicate { private NamePredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); return b != null ? b : item1.getItemMeta().getDisplayName() .equals(item2.getItemMeta().getDisplayName()); } } private static final class LorePredicate implements ItemMatcherPredicate { private LorePredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); return b != null ? b : item1.getItemMeta().getLore().equals(item2.getItemMeta().getLore()); } } private static final class EnchantmentsPredicate implements ItemMatcherPredicate { private EnchantmentsPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); return b != null ? b : item1.getItemMeta().getEnchants() .equals(item2.getItemMeta().getEnchants()); } } private static final class RepairCostPredicate implements ItemMatcherPredicate { private RepairCostPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); return b != null ? b : ((Repairable) item1.getItemMeta()).getRepairCost() == ((Repairable) item2.getItemMeta()).getRepairCost(); } } private static final class BookTitlePredicate implements ItemMatcherPredicate { private BookTitlePredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } else if ((!(item1.getItemMeta() instanceof BookMeta) || !(item2.getItemMeta() instanceof BookMeta))) { return false; } return ((BookMeta) item1.getItemMeta()).getTitle() .equals(((BookMeta) item2.getItemMeta()).getTitle()); } } private static final class BookAuthorPredicate implements ItemMatcherPredicate { private BookAuthorPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } else if ((!(item1.getItemMeta() instanceof BookMeta) || !(item2.getItemMeta() instanceof BookMeta))) { return false; } return ((BookMeta) item1.getItemMeta()).getAuthor() .equals(((BookMeta) item2.getItemMeta()).getAuthor()); } } private static final class BookPagesPredicate implements ItemMatcherPredicate { private BookPagesPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } else if ((!(item1.getItemMeta() instanceof BookMeta) || !(item2.getItemMeta() instanceof BookMeta))) { return false; } return ((BookMeta) item1.getItemMeta()).getPages() .equals(((BookMeta) item2.getItemMeta()).getPages()); } } private static final class FireworkEffectsPredicate implements ItemMatcherPredicate { private FireworkEffectsPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof FireworkMeta && item2.getItemMeta() instanceof FireworkMeta) { return ((FireworkMeta) item1.getItemMeta()).getEffects() .equals(((FireworkMeta) item2.getItemMeta()).getEffects()); } else if (item1.getItemMeta() instanceof FireworkEffectMeta && item2.getItemMeta() instanceof FireworkEffectMeta) { return ((FireworkEffectMeta) item1.getItemMeta()).getEffect() .equals(((FireworkEffectMeta) item2.getItemMeta()).getEffect()); } return false; } } private static final class FireworkPowerPredicate implements ItemMatcherPredicate { private FireworkPowerPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof FireworkMeta && item2.getItemMeta() instanceof FireworkMeta) { return ((FireworkMeta) item1.getItemMeta()).getPower() == ((FireworkMeta) item2.getItemMeta()).getPower(); } return false; } } private static final class ArmorColorPredicate implements ItemMatcherPredicate { private ArmorColorPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof LeatherArmorMeta && item2.getItemMeta() instanceof LeatherArmorMeta) { return ((LeatherArmorMeta) item1.getItemMeta()).getColor() .equals(((LeatherArmorMeta) item2.getItemMeta()).getColor()); } return false; } } private static final class MapScalePredicate implements ItemMatcherPredicate { private MapScalePredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof MapMeta && item2.getItemMeta() instanceof MapMeta) { return ((MapMeta) item1.getItemMeta()).isScaling() == ((MapMeta) item2.getItemMeta()).isScaling(); } return false; } } private static final class PotionEffectsPredicate implements ItemMatcherPredicate { private PotionEffectsPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof PotionMeta && item2.getItemMeta() instanceof PotionMeta) { return ((PotionMeta) item1.getItemMeta()).getCustomEffects() .equals(((PotionMeta) item2.getItemMeta()).getCustomEffects()); } return false; } } private static final class SkullOwnerPredicate implements ItemMatcherPredicate { private SkullOwnerPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof SkullMeta && item2.getItemMeta() instanceof SkullMeta) { return ((SkullMeta) item1.getItemMeta()).getOwner() .equals(((SkullMeta) item2.getItemMeta()).getOwner()); } return false; } } private static final class BannerColorPredicate implements ItemMatcherPredicate { private BannerColorPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof BannerMeta && item2.getItemMeta() instanceof BannerMeta) { return ((BannerMeta) item1.getItemMeta()).getBaseColor() .equals(((BannerMeta) item2.getItemMeta()).getBaseColor()); } return false; } } private static final class BannerPatternsPredicate implements ItemMatcherPredicate { private BannerPatternsPredicate() { } @Override public boolean apply(@Nullable ItemStack item1, @Nullable ItemStack item2) { Boolean b = precondition(item1, item2, true); if (b != null) { return b; } if (item1.getItemMeta() instanceof BannerMeta && item2.getItemMeta() instanceof BannerMeta) { return ((BannerMeta) item1.getItemMeta()).getPatterns() .equals(((BannerMeta) item2.getItemMeta()).getPatterns()); } return false; } } }