package org.bukkit.inventory; import com.google.common.collect.ImmutableMap; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Utility; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.material.MaterialData; /** * Represents a stack of items */ public class ItemStack implements Cloneable, ConfigurationSerializable { private int type = 0; private int amount = 0; private MaterialData data = null; private short durability = 0; private ItemMeta meta; @Utility protected ItemStack() {} /** * Defaults stack size to 1, with no extra data * * @param type item material id */ public ItemStack(final int type) { this(type, 1); } /** * Defaults stack size to 1, with no extra data * * @param type item material */ public ItemStack(final Material type) { this(type, 1); } /** * An item stack with no extra data * * @param type item material id * @param amount stack size */ public ItemStack(final int type, final int amount) { this(type, amount, (short) 0); } /** * An item stack with no extra data * * @param type item material * @param amount stack size */ public ItemStack(final Material type, final int amount) { this(type.getId(), amount); } /** * An item stack with the specified damage / durability * * @param type item material id * @param amount stack size * @param damage durability / damage */ public ItemStack(final int type, final int amount, final short damage) { this.type = type; this.amount = amount; this.durability = damage; } /** * An item stack with the specified damage / durabiltiy * * @param type item material * @param amount stack size * @param damage durability / damage */ public ItemStack(final Material type, final int amount, final short damage) { this(type.getId(), amount, damage); } /** * @deprecated this method uses an ambiguous data byte object */ @Deprecated public ItemStack(final int type, final int amount, final short damage, final Byte data) { this.type = type; this.amount = amount; this.durability = damage; if (data != null) { createData(data); this.durability = data; } } /** * @deprecated this method uses an ambiguous data byte object */ @Deprecated public ItemStack(final Material type, final int amount, final short damage, final Byte data) { this(type.getId(), amount, damage, data); } /** * Creates a new item stack derived from the specified stack * * @param stack the stack to copy * @throws IllegalArgumentException if the specified stack is null or returns an item meta not created by the item factory */ public ItemStack(final ItemStack stack) throws IllegalArgumentException { Validate.notNull(stack, "Cannot copy null stack"); this.type = stack.getTypeId(); this.amount = stack.getAmount(); this.durability = stack.getDurability(); this.data = stack.getData(); if (stack.hasItemMeta()) { setItemMeta0(stack.getItemMeta(), getType0()); } } /** * Gets the type of this item * * @return Type of the items in this stack */ @Utility public Material getType() { return getType0(getTypeId()); } private Material getType0() { return getType0(this.type); } private static Material getType0(int id) { Material material = Material.getMaterial(id); return material == null ? Material.AIR : material; } /** * Sets the type of this item *
* Note that in doing so you will reset the MaterialData for this stack * * @param type New type to set the items in this stack to */ @Utility public void setType(Material type) { Validate.notNull(type, "Material cannot be null"); setTypeId(type.getId()); } /** * Gets the type id of this item * * @return Type Id of the items in this stack */ public int getTypeId() { return type; } /** * Sets the type id of this item *
* Note that in doing so you will reset the MaterialData for this stack
*
* @param type New type id to set the items in this stack to
*/
public void setTypeId(int type) {
this.type = type;
if (this.meta != null) {
this.meta = Bukkit.getItemFactory().asMetaFor(meta, getType0());
}
createData((byte) 0);
}
/**
* Gets the amount of items in this stack
*
* @return Amount of items in this stick
*/
public int getAmount() {
return amount;
}
/**
* Sets the amount of items in this stack
*
* @param amount New amount of items in this stack
*/
public void setAmount(int amount) {
this.amount = amount;
}
/**
* Gets the MaterialData for this stack of items
*
* @return MaterialData for this item
*/
public MaterialData getData() {
Material mat = getType();
if (data == null && mat != null && mat.getData() != null) {
data = mat.getNewData((byte) this.getDurability());
}
return data;
}
/**
* Sets the MaterialData for this stack of items
*
* @param data New MaterialData for this item
*/
public void setData(MaterialData data) {
Material mat = getType();
if (data == null || mat == null || mat.getData() == null) {
this.data = data;
} else {
if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) {
this.data = data;
} else {
throw new IllegalArgumentException("Provided data is not of type " + mat.getData().getName() + ", found " + data.getClass().getName());
}
}
}
/**
* Sets the durability of this item
*
* @param durability Durability of this item
*/
public void setDurability(final short durability) {
this.durability = durability;
}
/**
* Gets the durability of this item
*
* @return Durability of this item
*/
public short getDurability() {
return durability;
}
/**
* Get the maximum stacksize for the material hold in this ItemStack
* Returns -1 if it has no idea.
*
* @return The maximum you can stack this material to.
*/
@Utility
public int getMaxStackSize() {
Material material = getType();
if (material != null) {
return material.getMaxStackSize();
}
return -1;
}
private void createData(final byte data) {
Material mat = Material.getMaterial(type);
if (mat == null) {
this.data = new MaterialData(type, data);
} else {
this.data = mat.getNewData(data);
}
}
@Override
@Utility
public String toString() {
StringBuilder toString = new StringBuilder("ItemStack{").append(getType().name()).append(" x ").append(getAmount());
if (hasItemMeta()) {
toString.append(", ").append(getItemMeta());
}
return toString.append('}').toString();
}
@Override
@Utility
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ItemStack)) {
return false;
}
ItemStack stack = (ItemStack) obj;
return getAmount() == stack.getAmount() && isSimilar(stack);
}
/**
* This method is the same as equals, but does not consider stack size (amount).
*
* @param stack the item stack to compare to
* @return true if the two stacks are equal, ignoring the amount
*/
@Utility
public boolean isSimilar(ItemStack stack) {
if (stack == null) {
return false;
}
if (stack == this) {
return true;
}
return getTypeId() == stack.getTypeId() && getDurability() == stack.getDurability() && hasItemMeta() == stack.hasItemMeta() && (hasItemMeta() ? Bukkit.getItemFactory().equals(getItemMeta(), stack.getItemMeta()) : true);
}
@Override
public ItemStack clone() {
try {
ItemStack itemStack = (ItemStack) super.clone();
if (this.meta != null) {
itemStack.meta = this.meta.clone();
}
if (this.data != null) {
itemStack.data = this.data.clone();
}
return itemStack;
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
@Override
@Utility
public final int hashCode() {
int hash = 1;
hash = hash * 31 + getTypeId();
hash = hash * 31 + getAmount();
hash = hash * 31 + (getDurability() & 0xffff);
hash = hash * 31 + (hasItemMeta() ? (meta == null ? getItemMeta().hashCode() : meta.hashCode()) : 0);
return hash;
}
/**
* Checks if this ItemStack contains the given {@link Enchantment}
*
* @param ench Enchantment to test
* @return True if this has the given enchantment
*/
public boolean containsEnchantment(Enchantment ench) {
return meta == null ? false : meta.hasEnchant(ench);
}
/**
* Gets the level of the specified enchantment on this item stack
*
* @param ench Enchantment to check
* @return Level of the enchantment, or 0
*/
public int getEnchantmentLevel(Enchantment ench) {
return meta == null ? 0 : meta.getEnchantLevel(ench);
}
/**
* Gets a map containing all enchantments and their levels on this item.
*
* @return Map of enchantments.
*/
public Map
* This method is the same as calling {@link #addEnchantment(org.bukkit.enchantments.Enchantment, int)}
* for each element of the map.
*
* @param enchantments Enchantments to add
* @throws IllegalArgumentException if the specified enchantments is null
* @throws IllegalArgumentException if any specific enchantment or level is null.
* Warning: Some enchantments may be added before this exception is thrown.
*/
@Utility
public void addEnchantments(Map
* If this item stack already contained the given enchantment (at any level), it will be replaced.
*
* @param ench Enchantment to add
* @param level Level of the enchantment
* @throws IllegalArgumentException if enchantment null, or enchantment is not applicable
*/
@Utility
public void addEnchantment(Enchantment ench, int level) {
Validate.notNull(ench, "Enchantment cannot be null");
if ((level < ench.getStartLevel()) || (level > ench.getMaxLevel())) {
throw new IllegalArgumentException("Enchantment level is either too low or too high (given " + level + ", bounds are " + ench.getStartLevel() + " to " + ench.getMaxLevel());
} else if (!ench.canEnchantItem(this)) {
throw new IllegalArgumentException("Specified enchantment cannot be applied to this itemstack");
}
addUnsafeEnchantment(ench, level);
}
/**
* Adds the specified enchantments to this item stack in an unsafe manner.
*
* This method is the same as calling {@link #addUnsafeEnchantment(org.bukkit.enchantments.Enchantment, int)}
* for each element of the map.
*
* @param enchantments Enchantments to add
*/
@Utility
public void addUnsafeEnchantments(Map
* If this item stack already contained the given enchantment (at any level), it will be replaced.
*
* This method is unsafe and will ignore level restrictions or item type. Use at your own
* discretion.
*
* @param ench Enchantment to add
* @param level Level of the enchantment
*/
public void addUnsafeEnchantment(Enchantment ench, int level) {
(meta == null ? meta = Bukkit.getItemFactory().getItemMeta(getType0()) : meta).addEnchant(ench, level, true);
}
/**
* Removes the specified {@link Enchantment} if it exists on this item stack
*
* @param ench Enchantment to remove
* @return Previous level, or 0
*/
public int removeEnchantment(Enchantment ench) {
int level = getEnchantmentLevel(ench);
if (level == 0 || meta == null) {
return level;
}
meta.removeEnchant(ench);
return level;
}
@Utility
public Map