package com.alecgorge.minecraft.jsonapi.config; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; 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; /* * 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()); } }