From 46cc901c2324507819a013d580bb53d2b25d2852 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 28 Jan 2019 18:29:17 -0800 Subject: [PATCH] Adds support for ItemStack storage in non-YAML databases. Fixes https://github.com/BentoBoxWorld/BentoBox/issues/501 --- .../json/AbstractJSONDatabaseHandler.java | 13 +- .../json/adapters/ItemStackTypeAdapter.java | 52 ++++++++ .../adapters/ItemStackTypeAdapterTest.java | 122 ++++++++++++++++++ 3 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapter.java create mode 100644 src/test/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapterTest.java diff --git a/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java index c6fb3c445..97f431a82 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/json/AbstractJSONDatabaseHandler.java @@ -1,15 +1,19 @@ package world.bentobox.bentobox.database.json; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.potion.PotionEffectType; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.database.AbstractDatabaseHandler; import world.bentobox.bentobox.database.DatabaseConnector; import world.bentobox.bentobox.database.json.adapters.FlagAdapter; +import world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter; import world.bentobox.bentobox.database.json.adapters.LocationAdapter; import world.bentobox.bentobox.database.json.adapters.PotionEffectTypeAdapter; import world.bentobox.bentobox.database.json.adapters.WorldAdapter; @@ -18,7 +22,7 @@ import world.bentobox.bentobox.database.json.adapters.WorldAdapter; * Abstract class that handles insert/select-operations into/from a database. * It also provides {@link #getGson()}. * - * @author Poslovitch + * @author Poslovitch, tastybento * * @param */ @@ -47,6 +51,7 @@ public abstract class AbstractJSONDatabaseHandler extends AbstractDatabaseHan builder.registerTypeAdapter(World.class, new WorldAdapter(plugin)); builder.registerTypeAdapter(Flag.class, new FlagAdapter(plugin)); builder.registerTypeAdapter(PotionEffectType.class, new PotionEffectTypeAdapter()); + builder.registerTypeAdapter(ItemStack.class, new ItemStackTypeAdapter()); // Keep null in the database builder.serializeNulls(); // Allow characters like < or > without escaping them diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapter.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapter.java new file mode 100644 index 000000000..00a8c6401 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapter.java @@ -0,0 +1,52 @@ +/** + * + */ +package world.bentobox.bentobox.database.json.adapters; + +import java.io.IOException; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * Serializes ItemStack to JSON and back. + * I'm going to cheat and use Bukkit's built in YAML serializer/deserializer. + * This will have the best chance of backwards compatibility with new server versions. + * @author tastybento + * + */ +public class ItemStackTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, ItemStack value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + YamlConfiguration c = new YamlConfiguration(); + c.set("is", value); + out.value(c.saveToString()); + } + + @Override + public ItemStack read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + YamlConfiguration c = new YamlConfiguration(); + try { + c.loadFromString(reader.nextString()); + return c.getItemStack("is"); + } catch (InvalidConfigurationException e) { + throw new IOException(e.getMessage()); + } + } + +} diff --git a/src/test/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapterTest.java b/src/test/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapterTest.java new file mode 100644 index 000000000..5d0a6c243 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/database/json/adapters/ItemStackTypeAdapterTest.java @@ -0,0 +1,122 @@ +/** + * + */ +package world.bentobox.bentobox.database.json.adapters; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * Tests the ItemStack type adapter for GSON + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest( {Bukkit.class} ) +public class ItemStackTypeAdapterTest { + + private ItemStackTypeAdapter isa; + private JsonWriter out; + private JsonReader reader; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + Server server = mock(Server.class); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + + ItemMeta meta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + UnsafeValues unsafe = mock(UnsafeValues.class); + when(unsafe.getDataVersion()).thenReturn(777); + when(Bukkit.getUnsafe()).thenReturn(unsafe); + isa = new ItemStackTypeAdapter(); + out = mock(JsonWriter.class); + reader = mock(JsonReader.class); + when(reader.peek()).thenReturn(JsonToken.STRING); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter#write(com.google.gson.stream.JsonWriter, org.bukkit.inventory.ItemStack)}. + * @throws IOException + */ + @Test + public void testWriteJsonWriterItemStack() throws IOException { + ItemStack stack = new ItemStack(Material.STICK, 4); + isa.write(out, stack); + Mockito.verify(out).value(Mockito.contains("==: org.bukkit.inventory.ItemStack")); + Mockito.verify(out).value(Mockito.contains("type: STICK")); + Mockito.verify(out).value(Mockito.contains("amount: 4")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter#write(com.google.gson.stream.JsonWriter, org.bukkit.inventory.ItemStack)}. + * @throws IOException + */ + @Test + public void testWriteJsonWriterItemStackNull() throws IOException { + isa.write(out, null); + Mockito.verify(out).nullValue(); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter#read(com.google.gson.stream.JsonReader)}. + * @throws IOException + */ + @Test + public void testReadJsonReaderNull() throws IOException { + when(reader.peek()).thenReturn(JsonToken.NULL); + assertNull(isa.read(reader)); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter#read(com.google.gson.stream.JsonReader)}. + * @throws IOException + */ + @Test + public void testReadJsonReader() throws IOException { + when(reader.nextString()).thenReturn("is:\n ==: org.bukkit.inventory.ItemStack\n v: 777\n type: STICK\n amount: 4\n"); + ItemStack i = isa.read(reader); + assertTrue(i.getType().equals(Material.STICK)); + assertTrue(i.getAmount() == 4); + } + + +}