<file_sep>package jayms.json;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Set;

public final class Utils {
	
	public class Tuple<A, B> {
		
		private A a;
		private B b;
		
		public Tuple(A a, B b) {
			this.a = a;
			this.b = b;
		}
		
		public void setA(A a) {
			this.a = a;
		}
		
		public void setB(B b) {
			this.b = b;
		}
		
		public A getA() {
			return a;
		}
		
		public B getB() {
			return b;
		}
	}

	public static String toJSONString(Object value) {
		String result = "";
		if (value == null) {
			result = "null";
		}else if (value instanceof Boolean) {
			result = ((boolean) value) ? "true" : "false";
		}else if (value instanceof String) {
			result += "\"" + (String) value + "\"";
		}else if (value instanceof Character) {
			result += "\"" + ((Character) value).toString() + "\"";
		}else if (value instanceof Number) {
			result += value;
		}else if (value.getClass().isArray()) {
			result += new JSONArray(convertObjectToArray(value)).toJSONString();
		}else if (value instanceof JSONArray) {
			result += ((JSONArray) value).toJSONString();
		}else {
			result += new JSONObject(value).toJSONString();
		}
		return result;
	}
	
	public static String[] bools = { "false", "true" };
	public static String doubleRegExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
	public static JSONParser parser = new JSONParser();
	
	public static Object fromJSONString(String json) {
		if (startsAndEnds(json, '"', '"')) {
			return json.substring(1, json.length()-1);
		}else if (isNumeric(json)) {
			if (json.contains(Character.toString(getDecimalSeperator()))) {
				if (json.matches(doubleRegExp)) {
					return Double.parseDouble(json);
				}else {
					return Float.parseFloat(json);
				}
			}
			BigInteger bigInt = new BigInteger(json);
			long val = bigInt.longValue();
			if (val < Integer.MAX_VALUE) {
				return bigInt.intValue();
			}else {
				return val;
			}
		}else if (json.equals("false") || json.equals("true")) {
			return json.equals("true") ? true : false;
		}else if (json.equals("null")) {
			return null;
		}else if (startsAndEnds(json, '{', '}')) {
			return parser.parseJSONObject(json);
		}else if (startsAndEnds(json, '[', ']')) {
			return parser.parseJSONArray(json);
		}else {
			System.out.println("THROWING: " + json);
			throw new IllegalArgumentException("In-valid JSON parsed!");
		}
	}
	
	public static void invalidJSON() {
		throw new IllegalArgumentException("In-valid JSON parsed!");
	}
	
	public static char getMinusSign() {
		DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
		return symbols.getMinusSign();
	}
	
	public static char getDecimalSeperator() {
		DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
		return symbols.getDecimalSeparator();
	}
	
	public static boolean isNumeric(String str) {
		
		char minusSymbol = getMinusSign();
		
		if (str.charAt(0) == minusSymbol) {
			str = str.substring(1, str.length());
		}
		
		boolean dpFound = false;
		char dpChar = getDecimalSeperator();
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (c == dpChar) {
				if (dpFound) {
					return false;
				}
				continue;
			}
			if (!Character.isDigit(c)) {
				return false;
			}
		}
		return true;
	}
	
	public static boolean startsAndEnds(String str, char start, char end) {
		if (str == null || str == "") {
			return false;
		}
		return str.charAt(0) == start && str.charAt(str.length()-1) == end;
	}
	
	public static Object[] convertObjectToArray(Object o) {
		if (o.getClass().isArray()) {
			Class ofArray = o.getClass().getComponentType();
			if (ofArray.isPrimitive()) {
				ArrayList ar = new ArrayList();
				for (int i = 0; i < Array.getLength(o); i++) {
					ar.add(Array.get(o, i));
				}
				return ar.toArray();
			}else {
				return (Object[]) o;
			}
		}else {
			throw new IllegalArgumentException("Tried to pass non-array object to 'convertObjectToArray(Object o)' method!");
		}
	}
	
	public static Field[] getAllFields(Object o, boolean includeStatic) {
		Class<?> objClass = o.getClass();
		Field[] objFields = getNonStaticFields(objClass);
		Class<?> current = objClass;
		ArrayList<Field> superFields = new ArrayList<>();
		while (current.getSuperclass() != null) {
			Class<?> superClass = current.getSuperclass();
			Field[] tempFields = getNonStaticFields(superClass);
			for (int i = 0; i < tempFields.length; i++) {
				superFields.add(tempFields[i]);
			}
			current = superClass;
		}
		Field[] result = new Field[objFields.length + superFields.size()];
		for (int i = 0; i < objFields.length; i++) {
			result[i] = objFields[i];
		}
		int baseInd = objFields.length-1;
		for (int i = 0; i < superFields.size(); i++) {
			result[baseInd+i] = superFields.get(i);
		}
		return result;
	}
	
	public static Field[] getNonStaticFields(Class<?> clazz) {
		ArrayList<Field> result = new ArrayList<>();
		Field[] fields = clazz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			if (Modifier.isStatic(field.getModifiers())) {
				continue;
			}
			result.add(field);
		}
		return result.toArray(new Field[result.size()]);
	}
	
	public static String getExtension(File f) {
		String relPath = f.getPath();
		int ind = relPath.lastIndexOf(".");
		return relPath.substring(ind, relPath.length());
	}
	
	public static boolean keysMatchToFields(Class<?> clazz, Set<String> keys) {
		Field[] fields = getNonStaticFields(clazz);
		for (Field f : fields) {
			if (!keys.contains(f.getName().toLowerCase())) {
				return false;
			}
		}
		return true;
	}
	
	public static void checkNull(Object value) {
		if (value == null) {
			throw new IllegalArgumentException("This key doesn't map to a value!");
		}
	}
}
<file_sep>package jayms.json.serial;

import java.io.File;

import jayms.json.JSONFile;

public class JSONDeserializer {

	private JSONFile file;
	
	public JSONDeserializer(File file) {
		this(new JSONFile(file));
	}
	
	public JSONDeserializer(JSONFile file) {
		this.file = file;
	}
	
	public <T> T deserialize(DeserializerTransformer<T> transformer) {
		return transformer.transform(file.read());
	}
}
<file_sep>package jayms.json.serial;

import jayms.json.JSONObject;

public interface SerializerTransformer<T> {

	JSONObject transform(T obj);
	
}
<file_sep>package jayms.json;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

public class JSONObject extends LinkedHashMap<String, Object> implements JSONComponent {

	public JSONObject() {
	}
	
	public JSONObject(Object o) {
		if (o instanceof JSONObject) {
			HashMap<String, Object> map = (HashMap<String, Object>) o;
			for (Entry<String, Object> entry : map.entrySet()) {
				put(entry.getKey(), entry.getValue());
			}
			return;
		}
		if (o.getClass().isArray()) {
			throw new IllegalArgumentException("Tried to pass array to JSONObject! Use JSONArray for arrays!");
		}
		if (o.getClass().isPrimitive() || o instanceof String) {
			throw new IllegalArgumentException("Tried to pass primitive type or string to JSONObject!");
		}
		Field[] fields = Utils.getAllFields(o, false);
		for (Field f : fields) {
			String key = f.getName();
			Object value = null;
			try {
				f.setAccessible(true);
				value = f.get(o);
			} catch(Exception e) {
				e.printStackTrace();
			}
			put(key, value);
		}
	}
	
	public JSONObject(JSONObject o) {
		this((HashMap<String, Object>)o);
	}
	
	public JSONObject(HashMap<String, Object> map) {
		for (Entry<String, Object> entry : map.entrySet()) {
			put(entry.getKey(), entry.getValue());
		}
	}
	
	public UUID getUUID(Object key) {
		Object resultObj = this.get(key);
		if (resultObj == null) {
			return null;
		}
		if (!(resultObj instanceof String) && !(resultObj instanceof UUID)) {
			return null;
		}
		return resultObj instanceof UUID ? ((UUID) resultObj) : UUID.fromString((String) resultObj);
	}
	
	public String getString(Object key) {
		Object resultObj = this.get(key);
		if (resultObj == null) {
			return null;
		}
		if (!(resultObj instanceof String)) {
			return null;
		}
		return (String) resultObj;
	}
	
	public double getDouble(Object key) {
		Object resultObj = this.get(key);
		if (resultObj == null) {
			return Double.NaN;
		}
		if (!(resultObj instanceof Double)) {
			return Double.NaN;
		}
		return (double) resultObj;
	}
	
	public boolean getBoolean(Object key) {
		Object resultObj = this.get(key);
		if (resultObj == null) {
			return false;
		}
		if (!(resultObj instanceof String) && !(resultObj instanceof Boolean)) {
			return false;
		}
		
		return resultObj instanceof String ? Boolean.parseBoolean((String) resultObj) : (boolean) resultObj;
	}
	
	public <T> void replaceAsObject(String key, Class<T> clazz) {
		Object obj = get(key);
		if (!(obj instanceof JSONObject)) {
			throw new IllegalArgumentException("Tried to replace as object at key: " + key + " but that is not a JSONObject!");
		}
		put(key, ((JSONObject) obj).toObject(clazz));
	}
	
	public <T> T toObject(Class<T> clazz) {
		try {
			T result = clazz.newInstance();
			Field[] fields = Utils.getNonStaticFields(clazz);
			for (int i = 0; i < fields.length; i++) {
				Field f = fields[i];
				String fn = f.getName();
				if (!this.containsKey(fn)) {
					System.out.println("Doesn't contain everything!");
					return null;
				}
				f.setAccessible(true);
				Object toSet = get(fn);
				//TODO HANDLE JSONOBJECT
				f.set(result, toSet);
			}
			return result;
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@Override
	public String toJSONString() {
		StringBuilder sb = new StringBuilder();
		sb.append("{");
		Set<Entry<String, Object>> entries = this.entrySet();
		int iteration = 1;
		for (Entry<String, Object> entry : entries) {
			String toAppend = "\"" + entry.getKey() + "\":";
			Object value = entry.getValue();
			toAppend += Utils.toJSONString(value);
			if (!(iteration >= entries.size())) {
				toAppend += ",";
			}
			sb.append(toAppend);
			iteration++;
		}
		sb.append("}");
		return sb.toString();
	}
	
}
<file_sep>package jayms.json;

import java.util.ArrayList;

public class JSONParser {

	public JSONParser() {
	}
	
	public JSONObject parseJSONObject(String json) {
		JSONObject result = new JSONObject();
		if (!Utils.startsAndEnds(json, '{', '}')) {
			Utils.invalidJSON();
		}
		json = json.substring(1, json.length()-1);
		String[] entries = breakToJSONString(json);
		for (int i = 0; i < entries.length; i++) {
			String entry = entries[i];
			int ind = 0;
			String key = null;
			String value = null;
			boolean gettingKey = false;
			boolean gotKey = false;
			ArrayList<Character> keyChars = new ArrayList<>();
			while (ind < entry.length()) {
				char c = entry.charAt(ind);
				ind++;
				if (c == '"' && !gotKey) {
					if (gettingKey) {
						StringBuilder sb = new StringBuilder();
						for (int k = 0; k < keyChars.size(); k++) {
							sb.append(keyChars.get(k));
						}
						key = sb.toString();
						gettingKey = false;
						gotKey = true;
						c = entry.charAt(ind);
						if (c != ':') {
							Utils.invalidJSON();
						}
					}else {
						gettingKey = true;
					}
					continue;
				}
				if (gettingKey) {
					keyChars.add(c);
					continue;
				}
				if (gotKey) {
					if (c == ':') {
						continue;
					}
					value = entry.substring(ind-1, entry.length());
					break;
				}
			}
			result.put(key, Utils.fromJSONString(value));
			
		}
		return result;
	}
	
	private String[] breakToJSONString(String json) {
		int ind = 0;
		ArrayList<String> resultList = new ArrayList<>();
		ArrayList<Character> built = new ArrayList<>();
		boolean dontBreakCurl = false;
		boolean dontBreakSquare = false;
		while (ind < json.length()) {
			char c = json.charAt(ind);
			ind++;
			switch (c) {
			case '{':
				dontBreakCurl = true;
				break;
			case '}':
				dontBreakCurl = false;
				break;
			case '[':
				dontBreakSquare = true;
				break;
			case ']':
				dontBreakSquare = false;
				break;
			}
			if (c == ',' && !dontBreakCurl && !dontBreakSquare) {
				appendBuiltToResult(built, resultList);
			}else {
				built.add(c);
				if (ind == json.length()) {
					appendBuiltToResult(built, resultList);
				}
			}
		}
		return resultList.toArray(new String[resultList.size()]);
	}
	
	private void appendBuiltToResult(ArrayList<Character> built, ArrayList<String> resultList) {
		StringBuilder sb = new StringBuilder();
		for (Character chara : built) {
			sb.append(chara);
		}
		built.clear();
		resultList.add(sb.toString());
	}
	
	public JSONArray parseJSONArray(String json) {
		JSONArray result = new JSONArray();
		if (!Utils.startsAndEnds(json, '[', ']')) {
			Utils.invalidJSON();
		}
		json = json.substring(1, json.length()-1);
		String[] values = breakToJSONString(json);
		for (int i = 0; i < values.length; i++) {
			result.add(Utils.fromJSONString(values[i]));
		}
		return result;
	}
}
<file_sep>package jayms.json.node;

import java.util.Collections;
import java.util.Map;

import jayms.json.Utils;

public class JSONNode {
	
	private JSONNode parent;
	private String name;
	private Object value;
	
	public JSONNode(JSONNode parent, String name, Object value) {
		this.parent = parent;
		this.name = name;
		this.value = value;
	}
	
	public String getName() {
		return name;
	}
	
	public Object getValue() {
		return value;
	}
	
	
	public void addChild(JSONNode JSONNode) {
		if (hasChild(JSONNode.getName())) {
			throw new IllegalArgumentException("A JSONNode with this name is aleady a child of this JSONNode!");
		}
		getChildNodes().put(JSONNode.getName(), JSONNode);
	}
	
	
	public void removeChild(String JSONNodeName) {
		if (hasChild(JSONNodeName)) {
			getChildNodes().remove(JSONNodeName);
		}
	}
	
	
	public boolean hasChild(String JSONNodeName) {
		if (!isParentNode()) {
			return false;
		}
		return getChildNodes().containsKey(JSONNodeName); 
	}
	
	
	public JSONNode childNode(String JSONNodeName) {
		if (!isParentNode()) {
			throw new IllegalArgumentException("Can't get child JSONNode because this JSONNode isn't a parent!");
		}
		Map<String, JSONNode> children = getChildNodes();
		if (!children.containsKey(JSONNodeName)) {
			throw new IllegalArgumentException("Can't get child JSONNode because this JSONNode isn't a child of this JSONNode!");
		}
		return children.get(JSONNodeName);
	}
	
	public double getDoubleValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof String)) {
			throw new IllegalArgumentException("Failed to cast value to double.");
		}
		return (double) result;
	}
	
	public float getFloatValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Float)) {
			throw new IllegalArgumentException("Failed to cast value to float.");
		}
		return (float) result;
	}
	
	public boolean getBooleanValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Boolean)) {
			throw new IllegalArgumentException("Failed to cast value to boolean.");
		}
		return (boolean) result;
	}
	
	public char getCharValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Character)) {
			throw new IllegalArgumentException("Failed to cast value to char.");
		}
		return (char) result;
	}
	
	public byte getByteValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Byte)) {
			throw new IllegalArgumentException("Failed to cast value to byte.");
		}
		return (byte) result;
	}
	
	public long getLongValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Long)) {
			throw new IllegalArgumentException("Failed to cast value to long.");
		}
		return (long) result;
	}
	
	public short getShortValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Short)) {
			throw new IllegalArgumentException("Failed to cast value to short.");
		}
		return (short) result;
	}

	public int getIntegerValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof Integer)) {
			throw new IllegalArgumentException("Failed to cast value to int.");
		}
		return (int) result;
	}
	
	public String getStringValue() {
		Object result = getValue();
		checkNull(result);
		if (!(result instanceof String)) {
			throw new IllegalArgumentException("Failed to cast value to string.");
		}
		return (String) result;
	}
	
	private void checkNull(Object value) {
		Utils.checkNull(value);
	}

	
	public JSONNode getParentJSONNode() {
		return parent;
	}

	
	public Map<String, JSONNode> getChildren() {
		return Collections.unmodifiableMap(getChildNodes());
	}
	
	private Map<String, JSONNode> getChildNodes() {
		if (!isParentNode()) {
			return null;
		}
		return (Map<String, JSONNode>) value;
	}
	
	public boolean isParentNode() {
		if (!(value instanceof Map)) {
			return false;
		}
		try {
			Map<String, JSONNode> map = (Map<String, JSONNode>) value;
			return true;
		}catch (Exception e) {
			return false;
		}
	}

	
	public boolean isChildNode() {
		return parent != null;
	}
}
