/**
* ShowCaseStandalone
* Copyright (C) 2013 Kellerkindt <copyright at kellerkindt.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.kellerkindt.scs.storage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import com.kellerkindt.scs.ShowCaseStandalone;
import com.kellerkindt.scs.interfaces.WorldProvider;
import com.kellerkindt.scs.internals.ItemStorage;
import com.kellerkindt.scs.internals.Storage;
import com.kellerkindt.scs.shops.BuyShop;
import com.kellerkindt.scs.shops.ExchangeShop;
import com.kellerkindt.scs.shops.Shop;
import com.kellerkindt.scs.utilities.Properties;
import com.kellerkindt.scs.utilities.Utilities;
/**
* This class is to load shops from old file format (i.e. xml file)
* @author kellerkindt <michael at kellerkindt.com>
*/
public class ShopImporter {
private static final String namedBooleanIsUnlimited = "isUnlimited";
private static final String namedDoublePrice = "price";
private static final String namedDoubleLocationX = "locX";
private static final String namedDoubleLocationY = "locY";
private static final String namedDoubleLocationZ = "locZ";
private static final String namedIntegerAmount = "amount";
private static final String namedIntegerMaxAmount = "maxAmount";
private static final String namedStringActivity = "activity";
@Deprecated
private static final String namedStringMaterial = "material"; // needed to import from old ...
@Deprecated
private static final String namedStringEnchantments = "enchantments";
private static final String namedStringOwner = "owner";
private static final String namedStringMembers = "members";
private static final String namedStringWorld = "world";
@Deprecated
private static final String namedStringItemStack = "itemStack"; // reused again as Storage key !!! - renaming?
@Deprecated
private static final String namedStringItemMeta = "itemStack-meta";
@Deprecated
private static final String namedStringItemStackNBT = "itemStack-nbt";
@Deprecated
private static final String namedStorageNBT = "nbt-storage";
private static final String namedIntegerExchangeAmount = "exchange-amount";
@Deprecated
private static final String namedStringExchangeMaterial = "exchange-material";
@Deprecated
private static final String namedStringExchangeEnchantments = "exchange-enchantments";
@Deprecated
private static final String namedStringExchangeItemStack = "exchange-itemStack";
@Deprecated
private static final String namedStringExchangeItemMeta = "exchange-itemStack-meta";
@Deprecated
private static final String namedStringExchangeItemStackNBT = "exchange-itemStack-nbt";
@Deprecated
private static final String namedStorageExchangeNBT = "exchange-nbt-storage";
private static final String namedStorageItemStack = "exchange-itemstack";
private static final String splitMember = ",";
private static final String splitItemParams = ":";
public static Shop loadShop (Storage storage, WorldProvider provider, String serverVersion) {
Map<String, Object> map = new HashMap<String, Object>();
String activity = storage.getString(namedStringActivity);
// no activity found?
if (activity == null) {
return null;
}
// load common values
loadCommon(map, storage, provider, serverVersion);
String aliasName = null;
// load specific
if ("BUY".equalsIgnoreCase(activity)) {
loadBuyShop(map, storage, provider, serverVersion);
aliasName = ShowCaseStandalone.ALIAS_SHOP_BUY;
} else if ("SELL".equalsIgnoreCase(activity)) {
loadSellShop(map, storage, provider, serverVersion);
aliasName = ShowCaseStandalone.ALIAS_SHOP_SELL;
} else if ("DISPLAY".equalsIgnoreCase(activity)) {
loadDisplayShop(map, storage, provider, serverVersion);
aliasName = ShowCaseStandalone.ALIAS_SHOP_DISPLAY;
} else if ("EXCHANGE".equalsIgnoreCase(activity)) {
loadExchangeShop(map, storage, provider, serverVersion);
aliasName = ShowCaseStandalone.ALIAS_SHOP_EXCHANGE;
}
map.put("==", aliasName);
return (Shop)ConfigurationSerialization.deserializeObject(map);
}
/**
* Adds the members from the storage to the shop
* @param storage Storage to load the members from
* @param shop Shop to add the members to
*/
public static List<String> getMembers (Storage storage) {
String members[] = storage.getString(namedStringMembers, "").split(splitMember);
if (members != null) {
return Arrays.asList(members);
} else {
return new ArrayList<String>();
}
}
/**
* @param storage Storage to load from
* @return Whether the shop is a unlimited shop
*/
public static boolean isUnlimited (Storage storage) {
return storage.getBoolean(namedBooleanIsUnlimited, false);
}
/**
* @param storage The storage to laod the price from
* @return The price for the shop
*/
public static double getPrice (Storage storage) {
return storage.getDouble(namedDoublePrice, Double.MAX_VALUE);
}
/**
* @param storage The storage to load from
* @return The amount for this shop
*/
public static int getAmount (Storage storage) {
return storage.getInteger(namedIntegerAmount, 0);
}
/**
* @param storage The storage to load from
* @return The amount for this exchange shop
*/
public static int getExchangeAmount (Storage storage) {
return storage.getInteger(namedIntegerExchangeAmount);
}
/**
* @param storage The storage to load the amount from
* @return The max amount
*/
public static int getMaxAmount (Storage storage) {
return storage.getInteger(namedIntegerMaxAmount, 0);
}
/**
* Loads the ItemStack from the given Storage and the given storage version
* @param storage Storage to load the ItemStack from
* @param provider WorldProvider to get MC informations from
* @param version Version-String of the current server version
* @param interpretationVersion Version how the Storage should be interpreted as
* @param args Parameters for the storage
* [0] = ItemStack key
* [1] = ItemMeta key
* [2] = Material key
* [3] = Enchantment key
* [4] = NBT String key
* [5] = NBTStorage key
* [6] = ItemStack Storage key
* @return The loaded ItemStack
* @throws RuntimeException If any issue occurs, a RuntimeException is thrown with additional information
* @throws IOException
* @throws ClassNotFoundException
*/
@SuppressWarnings("deprecation")
public static ItemStack getItemStack (Storage storage, WorldProvider provider, String version, int interpretationVersion, String ...args) throws RuntimeException, ClassNotFoundException, IOException {
// ItemStack to return
ItemStack itemStack = null;
/*
* Oldest interpretation
* Like "ITEM_ID:DURABILITY" ("LOG:2", "12:1")
*/
if (interpretationVersion <= 3) {
// get the string with the information
String sItemStackName = storage.getString (args[0]);
String sMaterial = storage.getString (args[2]);
String sEnchantments = storage.getString (args[3]);
String sNBTItemStack = storage.getString (args[4]);
Storage storNBT = storage.getStorage(args[5]);
// the version where the information is in the ItemStackName?
if (sItemStackName != null) {
String splitted[] = sItemStackName.split(splitItemParams);
int id = Integer.parseInt(splitted[0]);
// has durability?
if (splitted.length > 1) {
itemStack = new ItemStack(id, Properties.DEFAULT_STACK_AMOUNT, Short.parseShort(splitted[1]));
} else {
itemStack = new ItemStack(id, Properties.DEFAULT_STACK_AMOUNT);
}
// the version when the information was in the Material?
} else if (sMaterial != null) {
String splitted[] = sMaterial.split(splitItemParams);
Material material = Material.getMaterial(splitted[0].toUpperCase());
// has durability?
if (splitted.length > 1) {
itemStack = new ItemStack(material, Properties.DEFAULT_STACK_AMOUNT, Short.parseShort(splitted[1]));
} else {
itemStack = new ItemStack(material, Properties.DEFAULT_STACK_AMOUNT);
}
}
// couldn't load the ItemStack?
if (itemStack == null) {
throw new NullPointerException("ItemStack is null");
}
// any additional information in the NBT String?
if (sNBTItemStack != null) {
byte[] bytes = new HexBinaryAdapter().unmarshal(sNBTItemStack);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
DataInputStream dis = new DataInputStream(bais);
// MC 1.4.6
if (version.contains("1.4.6")) {
net.minecraft.server.v1_4_R1.NBTBase tag = net.minecraft.server.v1_4_R1.NBTBase.b(dis);
net.minecraft.server.v1_4_R1.ItemStack stack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asNMSCopy(itemStack);
stack.setTag((net.minecraft.server.v1_4_R1.NBTTagCompound)tag);
itemStack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asBukkitCopy(stack);
ShowCaseStandalone.slog(Level.INFO, "Loaded NBT from DIS - MC 1.4.6");
// MC 1.4.5
} else if (version.contains("1.4.5")) {
net.minecraft.server.v1_4_5.NBTBase tag = net.minecraft.server.v1_4_5.NBTBase.b(dis);
net.minecraft.server.v1_4_5.ItemStack stack = org.bukkit.craftbukkit.v1_4_5.inventory.CraftItemStack.asNMSCopy(itemStack);
stack.setTag((net.minecraft.server.v1_4_5.NBTTagCompound)tag);
itemStack = org.bukkit.craftbukkit.v1_4_5.inventory.CraftItemStack.asBukkitCopy(stack);
ShowCaseStandalone.slog(Level.INFO, "Loaded NBT from DIS - MC 1.4.5");
}
}
// any additional information in the NBTStorage?
if (storNBT != null) {
// MC 1.4.5
if (version.contains("1.4.5")) {
net.minecraft.server.v1_4_R1.ItemStack stack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asNMSCopy(itemStack);
stack.setTag(new com.kellerkindt.scs.internals.NBTStorage(storNBT).getNBTTagCompound());
itemStack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asBukkitCopy(stack);
ShowCaseStandalone.slog(Level.INFO, "Loaded NBT from NBTStorage - MC 1.4.5");
// MC 1.4.6
} else if (version.contains("1.4.6")) {
String tagStringAuthor = "author";
String tagStringTitle = "title";
String tagStringPages = "pages";
String tagStringPagePref = "page-";
String tagIntegerPagesSize = "pages";
String author = storNBT.getString (tagStringAuthor);
String title = storNBT.getString (tagStringTitle);
Integer pSize = storNBT.getInteger(tagIntegerPagesSize);
net.minecraft.server.v1_4_R1.NBTTagCompound compound = new net.minecraft.server.v1_4_R1.NBTTagCompound();
if (pSize != null) {
net.minecraft.server.v1_4_R1.NBTTagList list = new net.minecraft.server.v1_4_R1.NBTTagList();
for (int i = 0; i < pSize; i++) {
String page = storNBT.getString(tagStringPagePref+i);
if (page != null) {
net.minecraft.server.v1_4_R1.NBTTagString tag = new net.minecraft.server.v1_4_R1.NBTTagString(page);
tag.data = page;
list.add(tag);
}
}
compound.set(tagStringPages, list);
}
if (title != null)
compound.setString(tagStringTitle, title);
if (author != null)
compound.setString(tagStringAuthor, author);
net.minecraft.server.v1_4_R1.ItemStack stack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asNMSCopy(itemStack);
stack.setTag(compound);
itemStack = org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack.asBukkitCopy(stack);
ShowCaseStandalone.slog(Level.INFO, "Loaded NBT from NBTStorage - MC 1.4.6");
}
}
// enchantments available?
Map<Enchantment, Integer> enchantments = null;
if (sEnchantments != null) {
enchantments = new HashMap<Enchantment, Integer>();
String ench[] = sEnchantments.split(",");
for (String string : ench) {
Enchantment enchantment = Utilities.getEnchantmentFromString (string);
int level = Utilities.getEnchantmentLevelFromString (string);
if (enchantment != null) {
enchantments.put(enchantment, level);
}
}
}
// add the enchantments if there are set some
if (enchantments != null) {
for (Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
if (Properties.allowUnsafeEnchantments) {
itemStack.addUnsafeEnchantment(entry.getKey(), entry.getValue());
} else {
itemStack.addEnchantment(entry.getKey(), entry.getValue());
}
}
}
}
// storage version 4
else if (interpretationVersion == 4) {
// load binary array
String hexString = storage.getString(args[0]);
byte[] array = new HexBinaryAdapter().unmarshal(hexString);
// init streams
ByteArrayInputStream bais = new ByteArrayInputStream(array);
ObjectInputStream ois = new ObjectInputStream(bais);
// get the map and the ItemStack
@SuppressWarnings("unchecked")
HashMap<String, Object> map = (HashMap<String, Object>)ois.readObject();
itemStack = ItemStack.deserialize(map);
}
// storage version 5
else if (interpretationVersion == 5) {
// load it from the hex String
itemStack = Utilities.toItemStack(storage.getString(args[0]), Properties.DEFAULT_STACK_AMOUNT);
Validate.notNull(itemStack);
if (storage.getStorage(args[1]) != null) {
itemStack.setItemMeta( Utilities.toItemMeta(storage.getString(args[1])) );
}
}
// storage version 6
else if (interpretationVersion >= 6) {
Storage itemStorage = storage.getStorage(args[6]);
if (itemStorage != null) {
itemStack = new ItemStorage(itemStorage).getItemStack();
Validate.notNull(itemStack);
// successfully loaded?
if (Properties.stackToMaxAmount) {
itemStack.setAmount(itemStack.getMaxStackSize());
}
}
}
//
//
// // remove no longer needed entries
//// storage.removeString (args[0]); // they are
// storage.removeString (args[1]);
// storage.removeString (args[2]);
// storage.removeString (args[3]);
// storage.removeString (args[4]);
// storage.removeStorage (args[5]);
if (itemStack != null) {
return itemStack;
} else {
throw new RuntimeException("Version not recognized");
}
}
/**
* @param storage Storage to load the itemstack from
* @param provider WorldProvider to laod MC information from
* @param version The current server version
* @return the item stack for this shop
*/
public static ItemStack getItemStack (Storage storage, WorldProvider provider, String version) {
ItemStack itemStack = null;
for (int i = storage.getVersion(); i >= 0; i--) {
try {
itemStack = getItemStack(storage, provider, version, i, namedStringItemStack, namedStringItemMeta,
namedStringMaterial, namedStringEnchantments, namedStringItemStackNBT, namedStorageNBT,
namedStorageItemStack);
break;
} catch (Throwable ft) {
ShowCaseStandalone.slog(Level.WARNING, "Couldn't import ItemStack with storage.version="+i);
ft.printStackTrace();
}
}
if (itemStack == null) {
throw new NullPointerException("Couldn't import ItemStack");
}
// stack to max amount if requested
if (Properties.stackToMaxAmount && itemStack != null) {
itemStack.setAmount(itemStack.getMaxStackSize());
}
return itemStack;
}
/**
* @return The ItemStack of the exchange item
*/
public static ItemStack getExchangeItemStack (Storage storage, WorldProvider provider, String version) {
ItemStack exItemStack = null;
for (int i = storage.getVersion(); i >= 0; i--) {
try {
exItemStack = getItemStack(storage, provider, version, storage.getVersion(), namedStringExchangeItemStack, namedStringExchangeItemMeta,
namedStringExchangeMaterial, namedStringExchangeEnchantments, namedStringExchangeItemStackNBT, namedStorageExchangeNBT);
break;
} catch (Throwable ft) {
ShowCaseStandalone.slog(Level.WARNING, "Couldn't import ItemStack with storage.version="+i);
ft.printStackTrace();
}
}
if (exItemStack == null) {
throw new NullPointerException("Couldn't import ItemStack");
}
// stack to max amount if requested
if (Properties.stackToMaxAmount && exItemStack != null) {
exItemStack.setAmount(exItemStack.getMaxStackSize());
}
return exItemStack;
}
/**
* @param storage Storage to load the owner from
* @return The owner of the given storage
*/
private static String getOwner (Storage storage) {
return storage.getString(namedStringOwner);
}
/**
* @param storage Storage to load the world name from
* @return The name of the world in the given storage
*/
private static String getWorldName (Storage storage) {
return storage.getString(namedStringWorld);
}
/**
* @param storage Storage to load the world from
* @param server WorldProvider to get the world from
* @return The world for the given storage
*/
private static World getWorld (Storage storage, WorldProvider server) {
return server.getWorld(getWorldName(storage));
}
/**
* @param storage Storage to load the location from
* @param world World for the location
* @return The location for the given storage
*/
private static Location getLocation (Storage storage, World world) {
double x = storage.getDouble(namedDoubleLocationX);
double y = storage.getDouble(namedDoubleLocationY);
double z = storage.getDouble(namedDoubleLocationZ);
return new Location(world, x-.5, y, z-.5);
}
/**
* Loads the common values in the map
* @param map Map to fill with data
* @param version Server version
*/
private static void loadCommon (Map<String, Object> map, Storage storage, WorldProvider provider, String version) {
map.put(Shop.KEY_UUID, storage.getUUID().toString());
map.put(Shop.KEY_AMOUNT, getAmount(storage));
map.put(Shop.KEY_PRICE, getPrice(storage));
map.put(Shop.KEY_UNLIMITED, isUnlimited(storage));
map.put(Shop.KEY_ITEMSTACK, getItemStack(storage, provider, version));
map.put(Shop.KEY_OWNER, getOwner(storage));
map.put(Shop.KEY_MEMBERS, getMembers(storage));
List<Double> loc = new ArrayList<Double>();
List<String> worlds = new ArrayList<String>();
// load the world
World world = getWorld(storage, provider);
if (world != null) {
// world was found, add the UUID [index = 0]
worlds.add(world.getUID().toString());
Location location = getLocation(storage, world);
if (location != null) {
loc.add(location.getX());
loc.add(location.getY());
loc.add(location.getZ());
}
} else {
worlds.add(null);
}
// add the world name [index = 1]
worlds.add(getWorldName(storage));
// add the lists to the map
map.put(Shop.KEY_WORLD, worlds);
map.put(Shop.KEY_LOCATION, loc);
}
/**
* Loads the sell-shop values
* @param map Map to fill with the data
* @param storage Storage to load the data from
* @param version Server version
*/
private static void loadSellShop (Map<String, Object> map, Storage storage, WorldProvider provider, String version) {
// nothing to load here
}
/**
* Loads the display-shop values
* @param map Map to fill with the data
* @param storage Storage to load the data from
* @param version Server version
*/
private static void loadDisplayShop (Map<String, Object> map, Storage storage, WorldProvider provider, String version) {
// nothing to load here
}
/**
* Loads the buy-shop values
* @param map Map to fill with the data
* @param storage Storage to load the data from
* @param version Server version
*/
private static void loadBuyShop (Map<String, Object> map, Storage storage, WorldProvider provider, String version) {
map.put(BuyShop.KEY_MAXAMOUNT, getMaxAmount(storage));
}
/**
* Loads the exchange-shop values
* @param map Map to fill with the data
* @param storage Storage to load the data from
* @param version Server version
*/
private static void loadExchangeShop (Map<String, Object> map, Storage storage, WorldProvider provider, String version) {
map.put(ExchangeShop.KEY_EXCHANGE_AMOUNT, getExchangeAmount(storage));
map.put(ExchangeShop.KEY_EXCHANGE_ITEMSTACK, getExchangeItemStack(storage, provider, version));
}
}