org.spout.api.inventory.ItemStack.java Source code

Java tutorial

Introduction

Here is the source code for org.spout.api.inventory.ItemStack.java

Source

/*
 * This file is part of SpoutAPI.
 *
 * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
 * SpoutAPI is licensed under the SpoutDev License Version 1.
 *
 * SpoutAPI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the SpoutDev License Version 1.
 *
 * SpoutAPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the SpoutDev License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
 * including the MIT license.
 */
package org.spout.api.inventory;

import java.io.IOException;
import java.io.Serializable;

import org.apache.commons.lang3.builder.HashCodeBuilder;

import org.spout.api.datatable.ManagedHashMap;
import org.spout.api.datatable.SerializableMap;
import org.spout.api.map.DefaultedMap;
import org.spout.api.material.Material;
import org.spout.api.material.MaterialRegistry;
import org.spout.api.material.source.DataSource;
import org.spout.api.material.source.GenericMaterialAccess;
import org.spout.api.material.source.MaterialSource;
import org.spout.api.util.LogicUtil;

import org.spout.nbt.CompoundMap;
import org.spout.nbt.CompoundTag;
import org.spout.nbt.stream.NBTInputStream;
import org.spout.nbt.stream.NBTOutputStream;

/**
 * Represents a stack of items
 */
public class ItemStack extends GenericMaterialAccess implements Serializable, Cloneable {
    private static final long serialVersionUID = 1L;
    private int amount;
    private CompoundMap nbtData = null;
    private SerializableMap auxData;

    /**
     * Creates a new ItemStack from the specified Material of the specified
     * amount
     */
    public ItemStack(Material material, int amount) {
        this(material, material.getData(), amount);
    }

    /**
     * Creates a new ItemStack from the specified Material and data of the
     * specified amount
     */
    public ItemStack(Material material, int data, int amount) {
        this(material, data, amount, null);
    }

    /**
     * Creates a new ItemStack from the specified Material and data of the
     * specified amount, with the specified aux data
     */
    public ItemStack(Material material, int data, int amount, SerializableMap auxData) {
        super(material, data);
        this.amount = amount;
        if (auxData != null) {
            this.auxData = auxData;
        } else {
            this.auxData = new ManagedHashMap();
        }
    }

    /**
     * Gets the Material of the stack
     * @return the material
     */
    @Override
    public Material getMaterial() {
        return super.getMaterial();
    }

    /**
     * Gets the amount of the Material contained in the item stack
     * @return the amount
     */
    public int getAmount() {
        return amount;
    }

    /**
     * Sets amount of the Material contained in the item stack
     * @param amount the amount
     */
    public ItemStack setAmount(int amount) {
        this.amount = amount;
        return this;
    }

    /**
     * Gets whether this item stack is empty
     * @return whether the amount equals zero
     */
    public boolean isEmpty() {
        return this.amount == 0;
    }

    @Override
    public boolean isMaterial(Material... materials) {
        if (this.material == null) {
            for (Material material : materials) {
                if (material == null) {
                    return true;
                }
            }
            return false;
        } else {
            return this.material.isMaterial(materials);
        }
    }

    @Override
    public ItemStack setMaterial(MaterialSource material) {
        return (ItemStack) super.setMaterial(material);
    }

    @Override
    public ItemStack setMaterial(MaterialSource material, DataSource datasource) {
        return (ItemStack) super.setMaterial(material, datasource);
    }

    @Override
    public ItemStack setMaterial(MaterialSource material, int data) {
        return (ItemStack) super.setMaterial(material, data);
    }

    /**
     * Gets the map containing the aux data for this stack
     * @return the aux data
     */
    public DefaultedMap<String, Serializable> getAuxData() {
        return auxData;
    }

    /**
     * returns a copy of the map containing the aux data for this stack
     * @return the aux data
     */
    public CompoundMap getNBTData() {
        if (nbtData == null) {
            return null;
        }

        return new CompoundMap(nbtData);
    }

    /**
     * Sets the aux data for this stack
     * @return the item stack
     */
    public ItemStack setNBTData(CompoundMap nbtData) {
        if (nbtData == null) {
            this.nbtData = null;
        } else {
            this.nbtData = new CompoundMap(nbtData);
        }
        return this;
    }

    /**
     * If the item is null or empty, null is returned, otherwise the item is 
     * cloned
     * @param item to clone
     * @return null, or the cloned item
     */
    public static ItemStack cloneSpecial(ItemStack item) {
        return item == null || item.isEmpty() ? null : item.clone();
    }

    @Override
    public ItemStack clone() {
        ItemStack newStack = new ItemStack(material, data, amount, auxData);
        newStack.setNBTData(nbtData);
        return newStack;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(91, 15).append(material).append(auxData).append(nbtData).append(amount)
                .toHashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof ItemStack)) {
            return false;
        }
        ItemStack stack = (ItemStack) other;
        return equalsIgnoreSize(stack) && amount == stack.amount;
    }

    public boolean equalsIgnoreSize(ItemStack other) {
        if (other == null) {
            return false;
        }
        return material.equals(other.material) && data == other.data && auxData.equals(other.auxData)
                && LogicUtil.bothNullOrEqual(nbtData, other.nbtData);
    }

    @Override
    public String toString() {
        return "ItemStack{" + "material=" + material + ",id=" + material.getId() + ",data=" + data + ",amount="
                + amount + ",nbtData=" + nbtData + '}';
    }

    @Override
    public ItemStack setData(DataSource datasource) {
        return this.setData(datasource.getData());
    }

    @Override
    public ItemStack setData(int data) {
        this.data = (short) data;
        this.material = this.material.getRoot().getSubMaterial(this.data);
        return this;
    }

    @Override
    public short getData() {
        return this.data;
    }

    /**
     * Gets this item stack limited by the maximum stacking size<br>
     * The amount of this item stack is set to contain the remaining amount<br>
     * The amount of the returned stack is set to be this amount or the maximum 
     * stacking size<br><br>
     * <p/>
     * For example, limiting a stack of amount 120 to a max stacking size of 64 
     * will:
     * <ul>
     * <li>Set the amount of this item stack to 56
     * <li>Return an item stack with amount 64
     * </ul>
     * @return the limited stack
     */
    public ItemStack limitStackSize() {
        return this.limitSize(this.getMaxStackSize());
    }

    /**
     * Gets this item stack limited by the maximum size specified<br>
     * The amount of this item stack is set to contain the remaining amount<br>
     * The amount of the returned stack is set to be this amount or the maximum 
     * amount<br><br>
     * <p/>
     * For example, limiting a stack of amount 5 to a max size of 2 will:
     * <ul>
     * <li>Set the amount of this item stack to 3
     * <li>Return an item stack with amount 2
     * </ul>
     * @return the limited stack
     */
    public ItemStack limitSize(int maxSize) {
        ItemStack stack = this.clone();
        if (stack.getAmount() <= maxSize) {
            this.setAmount(0);
        } else {
            stack.setAmount(maxSize);
            this.setAmount(this.getAmount() - maxSize);
        }
        return stack;
    }

    /**
     * Tries to stack an item on top of this item<br>
     * The item must have the same properties as this item stack<br>
     * The amount of this item is kept below the max stacking size<br><br>
     * <p/>
     * The input item amount is affected<br>
     * If true is returned, this amount is 0, otherwise it is the amount it 
     * didn't stack into this item
     * @param item to stack
     * @return True if stacking was successful, False otherwise
     */
    public boolean stack(ItemStack item) {
        if (!this.equalsIgnoreSize(item)) {
            return false;
        }

        final int maxsize = this.getMaxStackSize();
        final int combinedSize = this.getAmount() + item.getAmount();
        if (combinedSize > maxsize) {
            this.setAmount(maxsize);
            item.setAmount(combinedSize - maxsize);
            return false;
        }

        this.setAmount(combinedSize);
        item.setAmount(0);
        return true;
    }

    /**
     * Gets the maximum size this {@link ItemStack} can be using the material 
     * it has
     * @return the max stack size
     */
    public int getMaxStackSize() {
        if (!auxData.isEmpty() || (nbtData != null && !nbtData.isEmpty())) {
            return 1;
        }
        return this.getMaterial().getMaxStackSize();
    }

    /**
     * Gets the maximum data this {@link ItemStack} can have using the material 
     * it has
     * @return the max data
     */
    public short getMaxData() {
        return this.getMaterial().getMaxData();
    }

    //Custom serialization logic because material & auxData can not be made 
    // serializable
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        out.writeShort(material.getId());
        out.writeShort(material.getData());
        out.writeInt(amount);
        out.writeShort(data);
        byte[] auxData = this.auxData.serialize();
        if (auxData != null) {
            out.writeInt(auxData.length);
            out.write(auxData);
        } else {
            out.writeInt(0);
        }

        if (nbtData != null && !nbtData.isEmpty()) {
            out.writeBoolean(true);
            NBTOutputStream os = new NBTOutputStream(out, false);
            os.writeTag(new CompoundTag("nbtData", nbtData));
            os.close();
        } else {
            out.writeBoolean(false);
        }
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        short matId = in.readShort();
        short matData = in.readShort();
        material = MaterialRegistry.get(matId);
        if (matData != 0 && material != null) {
            material = material.getSubMaterial(matData);
        }
        amount = in.readInt();
        data = in.readShort();
        int auxDataSize = in.readInt();
        if (auxDataSize > 0) {
            byte[] auxData = new byte[auxDataSize];
            ManagedHashMap map = new ManagedHashMap();
            map.deserialize(auxData);
            this.auxData = map;
        }

        boolean hasNBTData = in.readBoolean();
        if (hasNBTData) {
            NBTInputStream is = new NBTInputStream(in, false);
            CompoundTag tag = (CompoundTag) is.readTag();
            nbtData = tag.getValue();
            is.close();
        }

        if (material == null) {
            throw new ClassNotFoundException("No material matching {" + matId + ", " + matData + "} was found!");
        }
    }
}