Java tutorial
/* * Copyright (C) 2015 The Apocalypse MC. * * 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.theapocalypsemc.funcore.json; import com.google.gson.*; import com.google.gson.annotations.Expose; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import net.minecraft.server.v1_8_R3.MojangsonParseException; import net.minecraft.server.v1_8_R3.MojangsonParser; import net.minecraft.server.v1_8_R3.NBTBase; import net.minecraft.server.v1_8_R3.NBTTagCompound; import org.bukkit.*; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Type; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; /** * Special GSON object creator to allow for Location, ItemStack and PotionEffect serialization. * This also initializes the Gson instances lazily to save memory. * Source: http://bukkit.org/threads/gsonfactory-gson-that-works-on-itemstack-potioneffect-location-objects.331161/ * * @author Joshua Bell (RingOfStorms) */ @SuppressWarnings("unused") public class GsonFactory { private final static String CLASS_KEY = "SERIAL-ADAPTER-CLASS-KEY"; private static Gson g = new Gson(); private static Gson prettyGson; private static Gson compactGson; /** * Returns a Gson instance for use anywhere with new line pretty printing * <p> * Use @GsonIgnore in order to skip serialization and deserialization * </p> * * @return a Gson instance */ public static Gson getPrettyGson() { if (prettyGson == null) prettyGson = new GsonBuilder().addSerializationExclusionStrategy(new ExposeExlusion()) .addDeserializationExclusionStrategy(new ExposeExlusion()) .registerTypeHierarchyAdapter(ItemStack.class, new ItemStackGsonAdapter()) .registerTypeAdapter(PotionEffect.class, new PotionEffectGsonAdapter()) .registerTypeAdapter(Location.class, new LocationGsonAdapter()) .registerTypeAdapter(Date.class, new DateGsonAdapter()).setPrettyPrinting() .disableHtmlEscaping().create(); return prettyGson; } /** * Returns a Gson instance for use anywhere with one line strings * <p> * Use @GsonIgnore in order to skip serialization and deserialization * </p> * * @return a Gson instance */ public static Gson getCompactGson() { if (compactGson == null) compactGson = new GsonBuilder().addSerializationExclusionStrategy(new ExposeExlusion()) .addDeserializationExclusionStrategy(new ExposeExlusion()) .registerTypeHierarchyAdapter(ItemStack.class, new ItemStackGsonAdapter()) .registerTypeAdapter(PotionEffect.class, new PotionEffectGsonAdapter()) .registerTypeAdapter(Location.class, new LocationGsonAdapter()) .registerTypeAdapter(Date.class, new DateGsonAdapter()).disableHtmlEscaping().create(); return compactGson; } /** * Creates a new instance of Gson for use anywhere * <p> * Use @GsonIgnore in order to skip serialization and deserialization * </p> * * @return a Gson instance */ public static Gson getNewGson(boolean prettyPrinting) { GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(new ExposeExlusion()) .addDeserializationExclusionStrategy(new ExposeExlusion()) .registerTypeHierarchyAdapter(ItemStack.class, new NewItemStackAdapter()).disableHtmlEscaping(); if (prettyPrinting) builder.setPrettyPrinting(); return builder.create(); } private static Map<String, Object> recursiveSerialization(ConfigurationSerializable o) { Map<String, Object> originalMap = o.serialize(); Map<String, Object> map = new HashMap<>(); for (Entry<String, Object> entry : originalMap.entrySet()) { Object o2 = entry.getValue(); if (o2 instanceof ConfigurationSerializable) { ConfigurationSerializable serializable = (ConfigurationSerializable) o2; Map<String, Object> newMap = recursiveSerialization(serializable); newMap.put(CLASS_KEY, ConfigurationSerialization.getAlias(serializable.getClass())); map.put(entry.getKey(), newMap); } } map.put(CLASS_KEY, ConfigurationSerialization.getAlias(o.getClass())); return map; } private static Map<String, Object> recursiveDoubleToInteger(Map<String, Object> originalMap) { Map<String, Object> map = new HashMap<>(); for (Entry<String, Object> entry : originalMap.entrySet()) { Object o = entry.getValue(); if (o instanceof Double) { Double d = (Double) o; Integer i = d.intValue(); map.put(entry.getKey(), i); } else if (o instanceof Map) { Map<String, Object> subMap = (Map<String, Object>) o; map.put(entry.getKey(), recursiveDoubleToInteger(subMap)); } else { map.put(entry.getKey(), o); } } return map; } private static String nbtToString(NBTBase base) { return base.toString().replace(",}", "}").replace(",]", "]").replaceAll("[0-9]+\\:", ""); } private static net.minecraft.server.v1_8_R3.ItemStack removeSlot(ItemStack item) { if (item == null) return null; net.minecraft.server.v1_8_R3.ItemStack nmsi = CraftItemStack.asNMSCopy(item); if (nmsi == null) return null; NBTTagCompound nbtt = nmsi.getTag(); if (nbtt != null) { nbtt.remove("Slot"); nmsi.setTag(nbtt); } return nmsi; } private static ItemStack removeSlotNBT(ItemStack item) { if (item == null) return null; net.minecraft.server.v1_8_R3.ItemStack nmsi = CraftItemStack.asNMSCopy(item); if (nmsi == null) return null; NBTTagCompound nbtt = nmsi.getTag(); if (nbtt != null) { nbtt.remove("Slot"); nmsi.setTag(nbtt); } return CraftItemStack.asBukkitCopy(nmsi); } // Put @GsonIgnore before a variable that you want to be ignored. Transient is not needed. @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) public @interface Ignore { } private static class ExposeExlusion implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes fieldAttributes) { final Ignore ignore = fieldAttributes.getAnnotation(Ignore.class); if (ignore != null) return true; final Expose expose = fieldAttributes.getAnnotation(Expose.class); return expose != null && (!expose.serialize() || !expose.deserialize()); } @Override public boolean shouldSkipClass(Class<?> aClass) { return false; } } private static class NewItemStackAdapter extends TypeAdapter<ItemStack> { @Override public void write(JsonWriter jsonWriter, ItemStack itemStack) throws IOException { if (itemStack == null) { jsonWriter.nullValue(); return; } net.minecraft.server.v1_8_R3.ItemStack item = removeSlot(itemStack); if (item == null) { jsonWriter.nullValue(); return; } try { jsonWriter.beginObject(); jsonWriter.name("type"); jsonWriter.value(itemStack.getType().toString()); //I hate using this - but jsonWriter.name("amount"); jsonWriter.value(itemStack.getAmount()); jsonWriter.name("data"); jsonWriter.value(itemStack.getDurability()); jsonWriter.name("tag"); if (item != null && item.getTag() != null) { jsonWriter.value(nbtToString(item.getTag())); } else jsonWriter.value(""); jsonWriter.endObject(); } catch (Exception ex) { ex.printStackTrace(); } } @Override public ItemStack read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { return null; } jsonReader.beginObject(); jsonReader.nextName(); Material type = Material.getMaterial(jsonReader.nextString()); jsonReader.nextName(); int amount = jsonReader.nextInt(); jsonReader.nextName(); int data = jsonReader.nextInt(); net.minecraft.server.v1_8_R3.ItemStack item = new net.minecraft.server.v1_8_R3.ItemStack( CraftMagicNumbers.getItem(type), amount, data); jsonReader.nextName(); String next = jsonReader.nextString(); if (next.startsWith("{")) { NBTTagCompound compound = null; try { compound = MojangsonParser.parse(ChatColor.translateAlternateColorCodes('&', next)); } catch (MojangsonParseException e) { e.printStackTrace(); } item.setTag(compound); } jsonReader.endObject(); return CraftItemStack.asBukkitCopy(item); } } private static class ItemStackGsonAdapter extends TypeAdapter<ItemStack> { private static Type seriType = new TypeToken<Map<String, Object>>() { }.getType(); @Override public void write(JsonWriter jsonWriter, ItemStack itemStack) throws IOException { if (itemStack == null) { jsonWriter.nullValue(); return; } jsonWriter.value(getRaw(removeSlotNBT(itemStack))); } @Override public ItemStack read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } return fromRaw(jsonReader.nextString()); } private String getRaw(ItemStack item) { Map<String, Object> serial = item.serialize(); if (serial.get("meta") != null) { ItemMeta itemMeta = item.getItemMeta(); Map<String, Object> originalMeta = itemMeta.serialize(); Map<String, Object> meta = new HashMap<String, Object>(); for (Entry<String, Object> entry : originalMeta.entrySet()) meta.put(entry.getKey(), entry.getValue()); Object o; for (Entry<String, Object> entry : meta.entrySet()) { o = entry.getValue(); if (o instanceof ConfigurationSerializable) { ConfigurationSerializable serializable = (ConfigurationSerializable) o; Map<String, Object> serialized = recursiveSerialization(serializable); meta.put(entry.getKey(), serialized); } } serial.put("meta", meta); } return g.toJson(serial); } private ItemStack fromRaw(String raw) { Map<String, Object> keys = g.fromJson(raw, seriType); if (keys.get("amount") != null) { Double d = (Double) keys.get("amount"); Integer i = d.intValue(); keys.put("amount", i); } ItemStack item; try { item = ItemStack.deserialize(keys); } catch (Exception e) { return null; } if (item == null) return null; if (keys.containsKey("meta")) { Map<String, Object> itemmeta = (Map<String, Object>) keys.get("meta"); itemmeta = recursiveDoubleToInteger(itemmeta); ItemMeta meta = (ItemMeta) ConfigurationSerialization.deserializeObject(itemmeta, ConfigurationSerialization.getClassByAlias("ItemMeta")); item.setItemMeta(meta); } return item; } } private static class PotionEffectGsonAdapter extends TypeAdapter<PotionEffect> { private static Type seriType = new TypeToken<Map<String, Object>>() { }.getType(); private static String TYPE = "effect"; private static String DURATION = "duration"; private static String AMPLIFIER = "amplifier"; private static String AMBIENT = "ambient"; @Override public void write(JsonWriter jsonWriter, PotionEffect potionEffect) throws IOException { if (potionEffect == null) { jsonWriter.nullValue(); return; } jsonWriter.value(getRaw(potionEffect)); } @Override public PotionEffect read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } return fromRaw(jsonReader.nextString()); } private String getRaw(PotionEffect potion) { Map<String, Object> serial = potion.serialize(); return g.toJson(serial); } private PotionEffect fromRaw(String raw) { Map<String, Object> keys = g.fromJson(raw, seriType); return new PotionEffect(PotionEffectType.getById(((Double) keys.get(TYPE)).intValue()), ((Double) keys.get(DURATION)).intValue(), ((Double) keys.get(AMPLIFIER)).intValue(), (Boolean) keys.get(AMBIENT)); } } private static class LocationGsonAdapter extends TypeAdapter<Location> { private static Type seriType = new TypeToken<Map<String, Object>>() { }.getType(); private static String UUID = "uuid"; private static String X = "x"; private static String Y = "y"; private static String Z = "z"; private static String YAW = "yaw"; private static String PITCH = "pitch"; @Override public void write(JsonWriter jsonWriter, Location location) throws IOException { if (location == null) { jsonWriter.nullValue(); return; } jsonWriter.value(getRaw(location)); } @Override public Location read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } return fromRaw(jsonReader.nextString()); } private String getRaw(Location location) { Map<String, Object> serial = new HashMap<String, Object>(); serial.put(UUID, location.getWorld().getUID().toString()); serial.put(X, Double.toString(location.getX())); serial.put(Y, Double.toString(location.getY())); serial.put(Z, Double.toString(location.getZ())); serial.put(YAW, Float.toString(location.getYaw())); serial.put(PITCH, Float.toString(location.getPitch())); return g.toJson(serial); } private Location fromRaw(String raw) { Map<String, Object> keys = g.fromJson(raw, seriType); World w = Bukkit.getWorld(java.util.UUID.fromString((String) keys.get(UUID))); return new Location(w, Double.parseDouble((String) keys.get(X)), Double.parseDouble((String) keys.get(Y)), Double.parseDouble((String) keys.get(Z)), Float.parseFloat((String) keys.get(YAW)), Float.parseFloat((String) keys.get(PITCH))); } } private static class DateGsonAdapter extends TypeAdapter<Date> { @Override public void write(JsonWriter jsonWriter, Date date) throws IOException { if (date == null) { jsonWriter.nullValue(); return; } jsonWriter.value(date.getTime()); } @Override public Date read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } return new Date(jsonReader.nextLong()); } } }