Bukkit-API  1.7.9-R0.2
The inofficial Bukkit-API
ItemStack.java
1 package org.bukkit.inventory;
2 
3 import com.google.common.collect.ImmutableMap;
4 import java.util.LinkedHashMap;
5 import java.util.Map;
6 
7 import org.apache.commons.lang.Validate;
8 import org.bukkit.Bukkit;
9 import org.bukkit.Material;
10 import org.bukkit.Utility;
15 
16 /**
17  * Represents a stack of items
18  */
19 public class ItemStack implements Cloneable, ConfigurationSerializable {
20  private int type = 0;
21  private int amount = 0;
22  private MaterialData data = null;
23  private short durability = 0;
24  private ItemMeta meta;
25 
26  @Utility
27  protected ItemStack() {}
28 
29  /**
30  * Defaults stack size to 1, with no extra data
31  *
32  * @param type item material id
33  * @deprecated Magic value
34  */
35  @Deprecated
36  public ItemStack(final int type) {
37  this(type, 1);
38  }
39 
40  /**
41  * Defaults stack size to 1, with no extra data
42  *
43  * @param type item material
44  */
45  public ItemStack(final Material type) {
46  this(type, 1);
47  }
48 
49  /**
50  * An item stack with no extra data
51  *
52  * @param type item material id
53  * @param amount stack size
54  * @deprecated Magic value
55  */
56  @Deprecated
57  public ItemStack(final int type, final int amount) {
58  this(type, amount, (short) 0);
59  }
60 
61  /**
62  * An item stack with no extra data
63  *
64  * @param type item material
65  * @param amount stack size
66  */
67  public ItemStack(final Material type, final int amount) {
68  this(type.getId(), amount);
69  }
70 
71  /**
72  * An item stack with the specified damage / durability
73  *
74  * @param type item material id
75  * @param amount stack size
76  * @param damage durability / damage
77  * @deprecated Magic value
78  */
79  @Deprecated
80  public ItemStack(final int type, final int amount, final short damage) {
81  this.type = type;
82  this.amount = amount;
83  this.durability = damage;
84  }
85 
86  /**
87  * An item stack with the specified damage / durabiltiy
88  *
89  * @param type item material
90  * @param amount stack size
91  * @param damage durability / damage
92  */
93  public ItemStack(final Material type, final int amount, final short damage) {
94  this(type.getId(), amount, damage);
95  }
96 
97  /**
98  * @deprecated this method uses an ambiguous data byte object
99  */
100  @Deprecated
101  public ItemStack(final int type, final int amount, final short damage, final Byte data) {
102  this.type = type;
103  this.amount = amount;
104  this.durability = damage;
105  if (data != null) {
106  createData(data);
107  this.durability = data;
108  }
109  }
110 
111  /**
112  * @deprecated this method uses an ambiguous data byte object
113  */
114  @Deprecated
115  public ItemStack(final Material type, final int amount, final short damage, final Byte data) {
116  this(type.getId(), amount, damage, data);
117  }
118 
119  /**
120  * Creates a new item stack derived from the specified stack
121  *
122  * @param stack the stack to copy
123  * @throws IllegalArgumentException if the specified stack is null or
124  * returns an item meta not created by the item factory
125  */
126  public ItemStack(final ItemStack stack) throws IllegalArgumentException {
127  Validate.notNull(stack, "Cannot copy null stack");
128  this.type = stack.getTypeId();
129  this.amount = stack.getAmount();
130  this.durability = stack.getDurability();
131  this.data = stack.getData();
132  if (stack.hasItemMeta()) {
133  setItemMeta0(stack.getItemMeta(), getType0());
134  }
135  }
136 
137  /**
138  * Gets the type of this item
139  *
140  * @return Type of the items in this stack
141  */
142  @Utility
143  public Material getType() {
144  return getType0(getTypeId());
145  }
146 
147  private Material getType0() {
148  return getType0(this.type);
149  }
150 
151  private static Material getType0(int id) {
152  Material material = Material.getMaterial(id);
153  return material == null ? Material.AIR : material;
154  }
155 
156  /**
157  * Sets the type of this item
158  * <p>
159  * Note that in doing so you will reset the MaterialData for this stack
160  *
161  * @param type New type to set the items in this stack to
162  */
163  @Utility
164  public void setType(Material type) {
165  Validate.notNull(type, "Material cannot be null");
166  setTypeId(type.getId());
167  }
168 
169  /**
170  * Gets the type id of this item
171  *
172  * @return Type Id of the items in this stack
173  * @deprecated Magic value
174  */
175  @Deprecated
176  public int getTypeId() {
177  return type;
178  }
179 
180  /**
181  * Sets the type id of this item
182  * <p>
183  * Note that in doing so you will reset the MaterialData for this stack
184  *
185  * @param type New type id to set the items in this stack to
186  * @deprecated Magic value
187  */
188  @Deprecated
189  public void setTypeId(int type) {
190  this.type = type;
191  if (this.meta != null) {
192  this.meta = Bukkit.getItemFactory().asMetaFor(meta, getType0());
193  }
194  createData((byte) 0);
195  }
196 
197  /**
198  * Gets the amount of items in this stack
199  *
200  * @return Amount of items in this stick
201  */
202  public int getAmount() {
203  return amount;
204  }
205 
206  /**
207  * Sets the amount of items in this stack
208  *
209  * @param amount New amount of items in this stack
210  */
211  public void setAmount(int amount) {
212  this.amount = amount;
213  }
214 
215  /**
216  * Gets the MaterialData for this stack of items
217  *
218  * @return MaterialData for this item
219  */
221  Material mat = getType();
222  if (data == null && mat != null && mat.getData() != null) {
223  data = mat.getNewData((byte) this.getDurability());
224  }
225 
226  return data;
227  }
228 
229  /**
230  * Sets the MaterialData for this stack of items
231  *
232  * @param data New MaterialData for this item
233  */
234  public void setData(MaterialData data) {
235  Material mat = getType();
236 
237  if (data == null || mat == null || mat.getData() == null) {
238  this.data = data;
239  } else {
240  if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) {
241  this.data = data;
242  } else {
243  throw new IllegalArgumentException("Provided data is not of type " + mat.getData().getName() + ", found " + data.getClass().getName());
244  }
245  }
246  }
247 
248  /**
249  * Sets the durability of this item
250  *
251  * @param durability Durability of this item
252  */
253  public void setDurability(final short durability) {
254  this.durability = durability;
255  }
256 
257  /**
258  * Gets the durability of this item
259  *
260  * @return Durability of this item
261  */
262  public short getDurability() {
263  return durability;
264  }
265 
266  /**
267  * Get the maximum stacksize for the material hold in this ItemStack.
268  * (Returns -1 if it has no idea)
269  *
270  * @return The maximum you can stack this material to.
271  */
272  @Utility
273  public int getMaxStackSize() {
274  Material material = getType();
275  if (material != null) {
276  return material.getMaxStackSize();
277  }
278  return -1;
279  }
280 
281  private void createData(final byte data) {
282  Material mat = Material.getMaterial(type);
283 
284  if (mat == null) {
285  this.data = new MaterialData(type, data);
286  } else {
287  this.data = mat.getNewData(data);
288  }
289  }
290 
291  @Override
292  @Utility
293  public String toString() {
294  StringBuilder toString = new StringBuilder("ItemStack{").append(getType().name()).append(" x ").append(getAmount());
295  if (hasItemMeta()) {
296  toString.append(", ").append(getItemMeta());
297  }
298  return toString.append('}').toString();
299  }
300 
301  @Override
302  @Utility
303  public boolean equals(Object obj) {
304  if (this == obj) {
305  return true;
306  }
307  if (!(obj instanceof ItemStack)) {
308  return false;
309  }
310 
311  ItemStack stack = (ItemStack) obj;
312  return getAmount() == stack.getAmount() && isSimilar(stack);
313  }
314 
315  /**
316  * This method is the same as equals, but does not consider stack size
317  * (amount).
318  *
319  * @param stack the item stack to compare to
320  * @return true if the two stacks are equal, ignoring the amount
321  */
322  @Utility
323  public boolean isSimilar(ItemStack stack) {
324  if (stack == null) {
325  return false;
326  }
327  if (stack == this) {
328  return true;
329  }
330  return getTypeId() == stack.getTypeId() && getDurability() == stack.getDurability() && hasItemMeta() == stack.hasItemMeta() && (hasItemMeta() ? Bukkit.getItemFactory().equals(getItemMeta(), stack.getItemMeta()) : true);
331  }
332 
333  @Override
334  public ItemStack clone() {
335  try {
336  ItemStack itemStack = (ItemStack) super.clone();
337 
338  if (this.meta != null) {
339  itemStack.meta = this.meta.clone();
340  }
341 
342  if (this.data != null) {
343  itemStack.data = this.data.clone();
344  }
345 
346  return itemStack;
347  } catch (CloneNotSupportedException e) {
348  throw new Error(e);
349  }
350  }
351 
352  @Override
353  @Utility
354  public final int hashCode() {
355  int hash = 1;
356 
357  hash = hash * 31 + getTypeId();
358  hash = hash * 31 + getAmount();
359  hash = hash * 31 + (getDurability() & 0xffff);
360  hash = hash * 31 + (hasItemMeta() ? (meta == null ? getItemMeta().hashCode() : meta.hashCode()) : 0);
361 
362  return hash;
363  }
364 
365  /**
366  * Checks if this ItemStack contains the given {@link Enchantment}
367  *
368  * @param ench Enchantment to test
369  * @return True if this has the given enchantment
370  */
371  public boolean containsEnchantment(Enchantment ench) {
372  return meta == null ? false : meta.hasEnchant(ench);
373  }
374 
375  /**
376  * Gets the level of the specified enchantment on this item stack
377  *
378  * @param ench Enchantment to check
379  * @return Level of the enchantment, or 0
380  */
381  public int getEnchantmentLevel(Enchantment ench) {
382  return meta == null ? 0 : meta.getEnchantLevel(ench);
383  }
384 
385  /**
386  * Gets a map containing all enchantments and their levels on this item.
387  *
388  * @return Map of enchantments.
389  */
390  public Map<Enchantment, Integer> getEnchantments() {
391  return meta == null ? ImmutableMap.<Enchantment, Integer>of() : meta.getEnchants();
392  }
393 
394  /**
395  * Adds the specified enchantments to this item stack.
396  * <p>
397  * This method is the same as calling {@link
398  * #addEnchantment(org.bukkit.enchantments.Enchantment, int)} for each
399  * element of the map.
400  *
401  * @param enchantments Enchantments to add
402  * @throws IllegalArgumentException if the specified enchantments is null
403  * @throws IllegalArgumentException if any specific enchantment or level
404  * is null. <b>Warning</b>: Some enchantments may be added before this
405  * exception is thrown.
406  */
407  @Utility
408  public void addEnchantments(Map<Enchantment, Integer> enchantments) {
409  Validate.notNull(enchantments, "Enchantments cannot be null");
410  for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
411  addEnchantment(entry.getKey(), entry.getValue());
412  }
413  }
414 
415  /**
416  * Adds the specified {@link Enchantment} to this item stack.
417  * <p>
418  * If this item stack already contained the given enchantment (at any
419  * level), it will be replaced.
420  *
421  * @param ench Enchantment to add
422  * @param level Level of the enchantment
423  * @throws IllegalArgumentException if enchantment null, or enchantment is
424  * not applicable
425  */
426  @Utility
427  public void addEnchantment(Enchantment ench, int level) {
428  Validate.notNull(ench, "Enchantment cannot be null");
429  if ((level < ench.getStartLevel()) || (level > ench.getMaxLevel())) {
430  throw new IllegalArgumentException("Enchantment level is either too low or too high (given " + level + ", bounds are " + ench.getStartLevel() + " to " + ench.getMaxLevel() + ")");
431  } else if (!ench.canEnchantItem(this)) {
432  throw new IllegalArgumentException("Specified enchantment cannot be applied to this itemstack");
433  }
434 
435  addUnsafeEnchantment(ench, level);
436  }
437 
438  /**
439  * Adds the specified enchantments to this item stack in an unsafe manner.
440  * <p>
441  * This method is the same as calling {@link
442  * #addUnsafeEnchantment(org.bukkit.enchantments.Enchantment, int)} for
443  * each element of the map.
444  *
445  * @param enchantments Enchantments to add
446  */
447  @Utility
448  public void addUnsafeEnchantments(Map<Enchantment, Integer> enchantments) {
449  for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
450  addUnsafeEnchantment(entry.getKey(), entry.getValue());
451  }
452  }
453 
454  /**
455  * Adds the specified {@link Enchantment} to this item stack.
456  * <p>
457  * If this item stack already contained the given enchantment (at any
458  * level), it will be replaced.
459  * <p>
460  * This method is unsafe and will ignore level restrictions or item type.
461  * Use at your own discretion.
462  *
463  * @param ench Enchantment to add
464  * @param level Level of the enchantment
465  */
466  public void addUnsafeEnchantment(Enchantment ench, int level) {
467  (meta == null ? meta = Bukkit.getItemFactory().getItemMeta(getType0()) : meta).addEnchant(ench, level, true);
468  }
469 
470  /**
471  * Removes the specified {@link Enchantment} if it exists on this
472  * ItemStack
473  *
474  * @param ench Enchantment to remove
475  * @return Previous level, or 0
476  */
477  public int removeEnchantment(Enchantment ench) {
478  int level = getEnchantmentLevel(ench);
479  if (level == 0 || meta == null) {
480  return level;
481  }
482  meta.removeEnchant(ench);
483  return level;
484  }
485 
486  @Utility
487  public Map<String, Object> serialize() {
488  Map<String, Object> result = new LinkedHashMap<String, Object>();
489 
490  result.put("type", getType().name());
491 
492  if (getDurability() != 0) {
493  result.put("damage", getDurability());
494  }
495 
496  if (getAmount() != 1) {
497  result.put("amount", getAmount());
498  }
499 
500  ItemMeta meta = getItemMeta();
501  if (!Bukkit.getItemFactory().equals(meta, null)) {
502  result.put("meta", meta);
503  }
504 
505  return result;
506  }
507 
508  /**
509  * Required method for configuration serialization
510  *
511  * @param args map to deserialize
512  * @return deserialized item stack
513  * @see ConfigurationSerializable
514  */
515  public static ItemStack deserialize(Map<String, Object> args) {
516  Material type = Material.getMaterial((String) args.get("type"));
517  short damage = 0;
518  int amount = 1;
519 
520  if (args.containsKey("damage")) {
521  damage = ((Number) args.get("damage")).shortValue();
522  }
523 
524  if (args.containsKey("amount")) {
525  amount = (Integer) args.get("amount");
526  }
527 
528  ItemStack result = new ItemStack(type, amount, damage);
529 
530  if (args.containsKey("enchantments")) { // Backward compatiblity, @deprecated
531  Object raw = args.get("enchantments");
532 
533  if (raw instanceof Map) {
534  Map<?, ?> map = (Map<?, ?>) raw;
535 
536  for (Map.Entry<?, ?> entry : map.entrySet()) {
537  Enchantment enchantment = Enchantment.getByName(entry.getKey().toString());
538 
539  if ((enchantment != null) && (entry.getValue() instanceof Integer)) {
540  result.addUnsafeEnchantment(enchantment, (Integer) entry.getValue());
541  }
542  }
543  }
544  } else if (args.containsKey("meta")) { // We cannot and will not have meta when enchantments (pre-ItemMeta) exist
545  Object raw = args.get("meta");
546  if (raw instanceof ItemMeta) {
547  result.setItemMeta((ItemMeta) raw);
548  }
549  }
550 
551  return result;
552  }
553 
554  /**
555  * Get a copy of this ItemStack's {@link ItemMeta}.
556  *
557  * @return a copy of the current ItemStack's ItemData
558  */
560  return this.meta == null ? Bukkit.getItemFactory().getItemMeta(getType0()) : this.meta.clone();
561  }
562 
563  /**
564  * Checks to see if any meta data has been defined.
565  *
566  * @return Returns true if some meta data has been set for this item
567  */
568  public boolean hasItemMeta() {
569  return !Bukkit.getItemFactory().equals(meta, null);
570  }
571 
572  /**
573  * Set the ItemMeta of this ItemStack.
574  *
575  * @param itemMeta new ItemMeta, or null to indicate meta data be cleared.
576  * @return True if successfully applied ItemMeta, see {@link
577  * ItemFactory#isApplicable(ItemMeta, ItemStack)}
578  * @throws IllegalArgumentException if the item meta was not created by
579  * the {@link ItemFactory}
580  */
581  public boolean setItemMeta(ItemMeta itemMeta) {
582  return setItemMeta0(itemMeta, getType0());
583  }
584 
585  /*
586  * Cannot be overridden, so it's safe for constructor call
587  */
588  private boolean setItemMeta0(ItemMeta itemMeta, Material material) {
589  if (itemMeta == null) {
590  this.meta = null;
591  return true;
592  }
593  if (!Bukkit.getItemFactory().isApplicable(itemMeta, material)) {
594  return false;
595  }
596  this.meta = Bukkit.getItemFactory().asMetaFor(itemMeta, material);
597  if (this.meta == itemMeta) {
598  this.meta = itemMeta.clone();
599  }
600 
601  return true;
602  }
603 }
boolean isApplicable(final ItemMeta meta, final ItemStack stack)
boolean containsEnchantment(Enchantment ench)
Definition: ItemStack.java:371
void setType(Material type)
Definition: ItemStack.java:164
void addEnchantments(Map< Enchantment, Integer > enchantments)
Definition: ItemStack.java:408
ItemStack(final int type, final int amount)
Definition: ItemStack.java:57
boolean setItemMeta(ItemMeta itemMeta)
Definition: ItemStack.java:581
ItemStack(final int type, final int amount, final short damage, final Byte data)
Definition: ItemStack.java:101
MaterialData getNewData(final byte raw)
Definition: Material.java:506
void addUnsafeEnchantments(Map< Enchantment, Integer > enchantments)
Definition: ItemStack.java:448
ItemStack(final Material type, final int amount, final short damage, final Byte data)
Definition: ItemStack.java:115
ItemStack(final ItemStack stack)
Definition: ItemStack.java:126
static Material getMaterial(final int id)
Definition: Material.java:575
abstract boolean canEnchantItem(ItemStack item)
ItemStack(final int type, final int amount, final short damage)
Definition: ItemStack.java:80
void setData(MaterialData data)
Definition: ItemStack.java:234
Class<?extends MaterialData > getData()
Definition: Material.java:493
int getEnchantmentLevel(Enchantment ench)
Definition: ItemStack.java:381
ItemMeta asMetaFor(final ItemMeta meta, final ItemStack stack)
static Enchantment getByName(String name)
void addEnchantment(Enchantment ench, int level)
Definition: ItemStack.java:427
static ItemFactory getItemFactory()
Definition: Bukkit.java:682
ItemStack(final Material type, final int amount, final short damage)
Definition: ItemStack.java:93
int removeEnchantment(Enchantment ench)
Definition: ItemStack.java:477
boolean equals(final ItemMeta meta1, final ItemMeta meta2)
void addUnsafeEnchantment(Enchantment ench, int level)
Definition: ItemStack.java:466
Map< Enchantment, Integer > getEnchantments()
Definition: ItemStack.java:390
void setDurability(final short durability)
Definition: ItemStack.java:253
ItemStack(final Material type)
Definition: ItemStack.java:45
Map< String, Object > serialize()
Definition: ItemStack.java:487
boolean isSimilar(ItemStack stack)
Definition: ItemStack.java:323
ItemMeta getItemMeta(final Material material)
ItemStack(final Material type, final int amount)
Definition: ItemStack.java:67
static ItemStack deserialize(Map< String, Object > args)
Definition: ItemStack.java:515