package com.alecgorge.minecraft.jsonapi.dynamic; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.bukkit.Server; import org.bukkit.plugin.Plugin; import com.alecgorge.minecraft.jsonapi.APIException; import com.alecgorge.minecraft.jsonapi.JSONAPI; public class Call { private static Server Server = JSONAPI.instance.getServer(); private Class<?>[] signature = new Class<?>[] {}; private ArrayList<Object> stack = new ArrayList<Object>(); private ArrayList<String> flags = new ArrayList<String>(); private HashMap<Integer, String> defaults = new HashMap<Integer, String>(); public static boolean debug = false; private int expectedArgs = 0; private boolean useDirectMethod = false; private Object object; private java.lang.reflect.Method method; public Call(String input, ArgumentList args, ArrayList<String> flags) { signature = args.getTypes(); this.flags = flags; parseString(input); } public Call(Object o, java.lang.reflect.Method m, ArgumentList args) { useDirectMethod = true; signature = args.getTypes(); method = m; object = o; } private void debug(String in) { JSONAPI.dbug(in); } public int getNumberOfExpectedArgs() { return expectedArgs; } public Object call(Object[] params) throws Exception { if (useDirectMethod) { return method.invoke(object, params); } int size = stack.size(); ArrayList<Object> oParams = new ArrayList<Object>(Arrays.asList(params)); oParams.ensureCapacity(oParams.size() + defaults.size()); for (Integer i : defaults.keySet()) { oParams.add(i, defaults.get(i)); } if (JSONAPI.shouldDebug) { debug("oParams:" + oParams.toString()); debug("Stack:" + stack); } Object lastResult = null; for (int i = 0; i < size; i++) { Object v = stack.get(i); if (JSONAPI.shouldDebug) { debug("v:" + v.getClass().getCanonicalName()); } if (v instanceof Server || v instanceof APIWrapperMethods || (i == 0 && v instanceof Plugin)) { lastResult = v; } else if (v instanceof SubField) { SubField obj = (SubField) v; if (JSONAPI.shouldDebug) { debug("Requesting field: " + obj.getName()); } if (obj.getName().equals("length") && lastResult.getClass().isArray()) { lastResult = Array.getLength(lastResult); } else { java.lang.reflect.Field field; try { field = lastResult.getClass().getField(obj.getName()); } catch (NoSuchFieldException e) { field = lastResult.getClass().getDeclaredField(obj.getName()); } catch (NoClassDefFoundError e) { lastResult = null; return lastResult; } field.setAccessible(true); lastResult = field.get(lastResult); } } else if (v instanceof SubCall) { SubCall obj = (SubCall) v; if (JSONAPI.shouldDebug) { debug("Calling method: '" + obj.getName() + "' with signature: '" + obj.requiresArgs() + "' '" + Arrays.asList(sigForIndices(obj.requiresArgs())) + "'."); debug("Last result:" + (lastResult == null ? null : lastResult.toString())); debug("Invoking method: '" + obj.getName() + "' with args: '" + Arrays.asList(indicies(oParams, obj.requiresArgs())) + "'."); } Object[] args = indicies(oParams, obj.requiresArgs()); Class<?>[] sig = sigForIndices(obj.requiresArgs()); for (int x = 0; x < args.length; x++) { Object val = args[x]; if ((val.getClass().equals(Long.class) || val.getClass().equals(Double.class) || val.getClass().equals(String.class) || val.getClass().equals(long.class) || val.getClass().equals(double.class)) && (sig[x].equals(Integer.class) || sig[x].equals(int.class))) { args[x] = Integer.valueOf(val.toString()); val = args[x]; } else if ((val.getClass().equals(Integer.class) || val.getClass().equals(Long.class) || val.getClass().equals(String.class) || val.getClass().equals(long.class) || val.getClass().equals(int.class)) && (sig[x].equals(Double.class) || sig[x].equals(double.class))) { args[x] = Double.valueOf(val.toString()); val = args[x]; } else if ((val.getClass().equals(Integer.class) || val.getClass().equals(Double.class) || val.getClass().equals(String.class) || val.getClass().equals(int.class) || val.getClass().equals(double.class)) && (sig[x].equals(Long.class) || sig[x].equals(long.class))) { args[x] = Long.valueOf(val.toString()); val = args[x]; } else if ((val.getClass().equals(Integer.class) || val.getClass().equals(Double.class) || val.getClass().equals(String.class) || val.getClass().equals(int.class) || val.getClass().equals(double.class)) && (sig[x].equals(Boolean.class) || sig[x].equals(boolean.class))) { if(val instanceof String) { args[x] = false; if(((String) val).toLowerCase().equals("true")) { args[x] = true; } } else { args[x] = (Integer)val != 0; } val = args[x]; } else if (val instanceof List) { sig[x] = List.class; } if (JSONAPI.shouldDebug) { debug("Arg " + x + ": '" + val + "', type: " + val.getClass().getName()); debug("Sig type: " + sig[x].getName()); } } if (flags.contains("NO_EXCEPTIONS") || flags.contains("FALSE_ON_EXCEPTION")) { try { java.lang.reflect.Method thisMethod; try { thisMethod = lastResult.getClass().getMethod(obj.getName(), sig); } catch (NoSuchMethodException e) { thisMethod = lastResult.getClass().getDeclaredMethod(obj.getName(), sig); } thisMethod.setAccessible(true); lastResult = thisMethod.invoke(lastResult, args); } catch (Exception e) { if (flags.contains("FALSE_ON_EXCEPTION")) { return false; } return null; } } else { java.lang.reflect.Method thisMethod = null; try { thisMethod = lastResult.getClass().getMethod(obj.getName(), sig); } catch (NoSuchMethodException e) { thisMethod = lastResult.getClass().getDeclaredMethod(obj.getName(), sig); } catch (NullPointerException e) { throw new APIException("this returned null: " + stack.get(i - 1)); } thisMethod.setAccessible(true); lastResult = thisMethod.invoke(lastResult, args); } if (JSONAPI.shouldDebug) { debug("New value:" + lastResult); } } } return lastResult; } public Object[] indicies(ArrayList<Object> o, ArrayList<Integer> i) { if (i == null) { return new Object[] {}; } Object[] ret = new Object[i.size()]; for (int y = 0; y < ret.length; y++) { ret[y] = o.get(i.get(y)); } return ret; } public Class<?>[] sigForIndices(ArrayList<Integer> i) { if (i == null) { return new Class<?>[] {}; } Class<?>[] ret = new Class<?>[i.size()]; for (int y = 0; y < ret.length; y++) { ret[y] = signature[i.get(y)]; } return ret; } public void parseString(String input) { String[] parts = input.split("\\.(?=[a-zA-Z])"); for (int i = 0; i < parts.length; i++) { String v = parts[i]; // if(i == 0) { if (v.equals("Server")) { stack.add(Server); } else if (v.equals("this")) { stack.add(APIWrapperMethods.getInstance()); } else if (v.equals("Plugins")) { // handles // Plugins.PLUGINNAME.pluginMethod(0,1,2) String v2 = parts[i + 1]; stack.add(Server.getPluginManager().getPlugin(v2)); i++; continue; } // } else { // no args if (v.endsWith("()")) { stack.add(new SubCall(v.substring(0, v.length() - 2), new ArrayList<Integer>())); } // field else if (!v.endsWith(")")) { stack.add(new SubField(v)); } // args else { // find the position of the args int startPos = v.lastIndexOf("("); // take the stuff in the ('s and )'s String[] argParts = v.substring(startPos + 1, v.length() - 1).split(","); ArrayList<Integer> argPos = new ArrayList<Integer>(); // put all string 0, 1, 2, 3 etc into a int[] int multiplier = 0; for (int x = 0; x < argParts.length; x++) { if (argParts[x].trim().startsWith("\"") && argParts[x].trim().endsWith("\"")) { defaults.put(x, argParts[x].trim().substring(1, argParts[x].trim().length() - 1)); ArrayList<Class<?>> cc = new ArrayList<Class<?>>(Arrays.asList(signature)); cc.add(x, String.class); signature = cc.toArray(signature); if (argPos.size() > 0) { argPos.add(argPos.get(argPos.size() - 1) + 1); } else { argPos.add(0); } multiplier++; } else { try { argPos.add(Integer.parseInt(argParts[x].trim()) + (multiplier)); } catch (NumberFormatException e) { e.printStackTrace(); } } } expectedArgs = argPos.size() - multiplier; // add this "method" onto the stack stack.add(new SubCall(v.substring(0, startPos), argPos)); } } } } static class SubField { private String name; public SubField(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "." + name; } } static class SubCall { private String name; private ArrayList<Integer> argPos; public SubCall(String name, ArrayList<Integer> argPos) { this.name = name; this.argPos = argPos; } public ArrayList<Integer> requiresArgs() { return (argPos.size() > 0 ? argPos : null); } public String getName() { return name; } @Override public String toString() { StringBuilder argList = new StringBuilder(); for (Integer i : argPos) { argList.append(i).append(", "); } return "." + name + "(" + argList.toString() + ")"; } } }