Java tutorial
/* * PwnChat -- A Bukkit/Spigot plugin for multi-channel cross-server (via bungeecord) chat. * Copyright (c) 2013 Pwn9.com. Sage905 <ptoal@takeflight.ca> * * 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 3 * of the License, or (at your option) any later version. */ package com.pwn9.pwnchat.config; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.util.Vector; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; /* * SuperEasyConfig - ConfigObject * * Based off of codename_Bs EasyConfig v2.1 * which was inspired by md_5 * * An even awesomer super-duper-lazy Config lib! * * 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. * * @author MrFigg * @version 1.2 */ public abstract class ConfigObject { /* * loading and saving */ protected void onLoad(ConfigurationSection cs) throws Exception { for (Field field : getClass().getDeclaredFields()) { String path = field.getName().replaceAll("_", "."); if (doSkip(field)) { // Do nothing } else if (cs.isSet(path)) { field.set(this, loadObject(field, cs, path)); } else { cs.set(path, saveObject(field.get(this), field, cs, path)); } } } protected void onSave(ConfigurationSection cs) throws Exception { for (Field field : getClass().getDeclaredFields()) { String path = field.getName().replaceAll("_", "."); if (doSkip(field)) { // Do nothing } else { cs.set(path, saveObject(field.get(this), field, cs, path)); } } } protected Object loadObject(Field field, ConfigurationSection cs, String path) throws Exception { return loadObject(field, cs, path, 0); } protected Object saveObject(Object obj, Field field, ConfigurationSection cs, String path) throws Exception { return saveObject(obj, field, cs, path, 0); } @SuppressWarnings("rawtypes") protected Object loadObject(Field field, ConfigurationSection cs, String path, int depth) throws Exception { Class clazz = getClassAtDepth(field.getGenericType(), depth); if (ConfigObject.class.isAssignableFrom(clazz) && isConfigurationSection(cs.get(path))) { return getConfigObject(clazz, cs.getConfigurationSection(path)); } else if (Location.class.isAssignableFrom(clazz) && isJSON(cs.get(path))) { return getLocation((String) cs.get(path)); } else if (Vector.class.isAssignableFrom(clazz) && isJSON(cs.get(path))) { return getVector((String) cs.get(path)); } else if (Map.class.isAssignableFrom(clazz) && isConfigurationSection(cs.get(path))) { return getMap(field, cs.getConfigurationSection(path), path, depth); } else if (clazz.isEnum() && isString(cs.get(path))) { return getEnum(clazz, (String) cs.get(path)); } else if (List.class.isAssignableFrom(clazz) && isConfigurationSection(cs.get(path))) { Class subClazz = getClassAtDepth(field.getGenericType(), depth + 1); if (ConfigObject.class.isAssignableFrom(subClazz) || Location.class.isAssignableFrom(subClazz) || Vector.class.isAssignableFrom(subClazz) || Map.class.isAssignableFrom(subClazz) || List.class.isAssignableFrom(subClazz) || subClazz.isEnum()) { return getList(field, cs.getConfigurationSection(path), path, depth); } else { return cs.get(path); } } else { return cs.get(path); } } @SuppressWarnings("rawtypes") protected Object saveObject(Object obj, Field field, ConfigurationSection cs, String path, int depth) throws Exception { Class clazz = getClassAtDepth(field.getGenericType(), depth); if (ConfigObject.class.isAssignableFrom(clazz) && isConfigObject(obj)) { return getConfigObject((ConfigObject) obj, path, cs); } else if (Location.class.isAssignableFrom(clazz) && isLocation(obj)) { return getLocation((Location) obj); } else if (Vector.class.isAssignableFrom(clazz) && isVector(obj)) { return getVector((Vector) obj); } else if (Map.class.isAssignableFrom(clazz) && isMap(obj)) { return getMap((Map) obj, field, cs, path, depth); } else if (clazz.isEnum() && isEnum(clazz, obj)) { return getEnum((Enum) obj); } else if (List.class.isAssignableFrom(clazz) && isList(obj)) { Class subClazz = getClassAtDepth(field.getGenericType(), depth + 1); if (ConfigObject.class.isAssignableFrom(subClazz) || Location.class.isAssignableFrom(subClazz) || Vector.class.isAssignableFrom(subClazz) || Map.class.isAssignableFrom(subClazz) || List.class.isAssignableFrom(subClazz) || subClazz.isEnum()) { return getList((List) obj, field, cs, path, depth); } else { return obj; } } else { return obj; } } /* * class detection */ @SuppressWarnings("rawtypes") protected Class getClassAtDepth(Type type, int depth) throws Exception { if (depth <= 0) { String className = type.toString(); if (className.length() >= 6 && className.substring(0, 6).equalsIgnoreCase("class ")) { className = className.substring(6); } if (className.indexOf("<") >= 0) { className = className.substring(0, className.indexOf("<")); } try { return Class.forName(className); } catch (ClassNotFoundException ex) { // ugly fix for primitive data types if (className.equalsIgnoreCase("byte")) return Byte.class; if (className.equalsIgnoreCase("short")) return Short.class; if (className.equalsIgnoreCase("int")) return Integer.class; if (className.equalsIgnoreCase("long")) return Long.class; if (className.equalsIgnoreCase("float")) return Float.class; if (className.equalsIgnoreCase("double")) return Double.class; if (className.equalsIgnoreCase("char")) return Character.class; if (className.equalsIgnoreCase("boolean")) return Boolean.class; throw ex; } } depth--; ParameterizedType pType = (ParameterizedType) type; Type[] typeArgs = pType.getActualTypeArguments(); return getClassAtDepth(typeArgs[typeArgs.length - 1], depth); } protected boolean isString(Object obj) { if (obj instanceof String) { return true; } return false; } protected boolean isConfigurationSection(Object o) { try { return (ConfigurationSection) o != null; } catch (Exception e) { return false; } } protected boolean isJSON(Object obj) { try { if (obj instanceof String) { String str = (String) obj; if (str.startsWith("{")) { return new JSONParser().parse(str) != null; } } return false; } catch (Exception e) { return false; } } protected boolean isConfigObject(Object obj) { try { return (ConfigObject) obj != null; } catch (Exception e) { return false; } } protected boolean isLocation(Object obj) { try { return (Location) obj != null; } catch (Exception e) { return false; } } protected boolean isVector(Object obj) { try { return (Vector) obj != null; } catch (Exception e) { return false; } } @SuppressWarnings("rawtypes") protected boolean isMap(Object obj) { try { return (Map) obj != null; } catch (Exception e) { return false; } } @SuppressWarnings("rawtypes") protected boolean isList(Object obj) { try { return (List) obj != null; } catch (Exception e) { return false; } } @SuppressWarnings("rawtypes") protected boolean isEnum(Class clazz, Object obj) { if (!clazz.isEnum()) return false; for (Object constant : clazz.getEnumConstants()) { if (constant.equals(obj)) { return true; } } return false; } /* * loading conversion */ @SuppressWarnings("rawtypes") protected ConfigObject getConfigObject(Class clazz, ConfigurationSection cs) throws Exception { ConfigObject obj = (ConfigObject) clazz.newInstance(); obj.onLoad(cs); return obj; } protected Location getLocation(String json) throws Exception { JSONObject data = (JSONObject) new JSONParser().parse(json); // world World world = Bukkit.getWorld((String) data.get("world")); // x, y, z double x = Double.parseDouble((String) data.get("x")); double y = Double.parseDouble((String) data.get("y")); double z = Double.parseDouble((String) data.get("z")); // pitch, yaw float pitch = Float.parseFloat((String) data.get("pitch")); float yaw = Float.parseFloat((String) data.get("yaw")); // generate Location Location loc = new Location(world, x, y, z); loc.setPitch(pitch); loc.setYaw(yaw); return loc; } protected Vector getVector(String json) throws Exception { JSONObject data = (JSONObject) new JSONParser().parse(json); // x, y, z double x = Double.parseDouble((String) data.get("x")); double y = Double.parseDouble((String) data.get("y")); double z = Double.parseDouble((String) data.get("z")); // generate Vector return new Vector(x, y, z); } @SuppressWarnings({ "rawtypes", "unchecked" }) protected Map getMap(Field field, ConfigurationSection cs, String path, int depth) throws Exception { depth++; Set<String> keys = cs.getKeys(false); Map map = new HashMap(); if (keys != null && keys.size() > 0) { for (String key : keys) { Object in = cs.get(key); in = loadObject(field, cs, key, depth); map.put(key, in); } } return map; } @SuppressWarnings({ "rawtypes", "unchecked" }) protected List getList(Field field, ConfigurationSection cs, String path, int depth) throws Exception { depth++; int listSize = cs.getKeys(false).size(); String key = path; if (key.lastIndexOf(".") >= 0) { key = key.substring(key.lastIndexOf(".")); } List list = new ArrayList(); if (listSize > 0) { int loaded = 0; int i = 0; while (loaded < listSize) { if (cs.isSet(key + i)) { Object in = cs.get(key + i); in = loadObject(field, cs, key + i, depth); list.add(in); loaded++; } i++; // ugly overflow guard... should only be needed if config was manually edited very badly if (i > (listSize * 3)) loaded = listSize; } } return list; } @SuppressWarnings("rawtypes") protected Enum getEnum(Class clazz, String string) throws Exception { if (!clazz.isEnum()) throw new Exception("Class " + clazz.getName() + " is not an enum."); for (Object constant : clazz.getEnumConstants()) { if (((Enum) constant).toString().equals(string)) { return (Enum) constant; } } throw new Exception("String " + string + " not a valid enum constant for " + clazz.getName()); } /* * saving conversion */ protected ConfigurationSection getConfigObject(ConfigObject obj, String path, ConfigurationSection cs) throws Exception { ConfigurationSection subCS = cs.createSection(path); obj.onSave(subCS); return subCS; } protected String getLocation(Location loc) { String ret = "{"; ret += "\"world\":\"" + loc.getWorld().getName() + "\""; ret += ",\"x\":\"" + loc.getX() + "\""; ret += ",\"y\":\"" + loc.getY() + "\""; ret += ",\"z\":\"" + loc.getZ() + "\""; ret += ",\"pitch\":\"" + loc.getPitch() + "\""; ret += ",\"yaw\":\"" + loc.getYaw() + "\""; ret += "}"; if (!isJSON(ret)) return getLocationJSON(loc); try { getLocation(ret); } catch (Exception ex) { return getLocationJSON(loc); } return ret; } @SuppressWarnings("unchecked") protected String getLocationJSON(Location loc) { JSONObject data = new JSONObject(); // world data.put("world", loc.getWorld().getName()); // x, y, z data.put("x", String.valueOf(loc.getX())); data.put("y", String.valueOf(loc.getY())); data.put("z", String.valueOf(loc.getZ())); // pitch, yaw data.put("pitch", String.valueOf(loc.getPitch())); data.put("yaw", String.valueOf(loc.getYaw())); return data.toJSONString(); } protected String getVector(Vector vec) { String ret = "{"; ret += "\"x\":\"" + vec.getX() + "\""; ret += ",\"y\":\"" + vec.getY() + "\""; ret += ",\"z\":\"" + vec.getZ() + "\""; ret += "}"; if (!isJSON(ret)) return getVectorJSON(vec); try { getVector(ret); } catch (Exception ex) { return getVectorJSON(vec); } return ret; } @SuppressWarnings("unchecked") protected String getVectorJSON(Vector vec) { JSONObject data = new JSONObject(); // x, y, z data.put("x", String.valueOf(vec.getX())); data.put("y", String.valueOf(vec.getY())); data.put("z", String.valueOf(vec.getZ())); return data.toJSONString(); } @SuppressWarnings({ "rawtypes", "unchecked" }) protected ConfigurationSection getMap(Map map, Field field, ConfigurationSection cs, String path, int depth) throws Exception { depth++; ConfigurationSection subCS = cs.createSection(path); Set<String> keys = map.keySet(); if (keys != null && keys.size() > 0) { for (String key : keys) { Object out = map.get(key); out = saveObject(out, field, cs, path + "." + key, depth); subCS.set(key, out); } } return subCS; } @SuppressWarnings("rawtypes") protected ConfigurationSection getList(List list, Field field, ConfigurationSection cs, String path, int depth) throws Exception { depth++; ConfigurationSection subCS = cs.createSection(path); String key = path; if (key.lastIndexOf(".") >= 0) { key = key.substring(key.lastIndexOf(".")); } if (list != null && list.size() > 0) { for (int i = 0; i < list.size(); i++) { Object out = list.get(i); out = saveObject(out, field, cs, path + "." + key + (i + 1), depth); subCS.set(key + (i + 1), out); } } return subCS; } @SuppressWarnings("rawtypes") protected String getEnum(Enum enumObj) { return enumObj.toString(); } /* * utility */ protected boolean doSkip(Field field) { return Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) || Modifier.isPrivate(field.getModifiers()); } }