package jeql.engine.function; import java.lang.reflect.*; import java.util.List; import jeql.api.error.ExecutionException; import jeql.api.error.JeqlException; import jeql.engine.EngineContext; import jeql.engine.FunctionRegistry; import jeql.engine.Scope; import jeql.syntax.ParseTreeNode; import jeql.util.ClassUtil; import jeql.util.ExceptionUtil; import jeql.util.TypeUtil; public class MethodFunctionInvoker { private Method method; private boolean isContextSupplied; private int argOffset = 0; public MethodFunctionInvoker(Method method) { this.method = method; isContextSupplied = isContextSupplied(method); if (isContextSupplied) argOffset = 1; } public Class getType(Scope scope) { Class rtnType = method.getReturnType(); return ClassUtil.objectClass(rtnType); } /** * Gets the type of a parameterized List * @param scope * @return */ public Class getParameterizedListType(Scope scope) { Type returnType = method.getGenericReturnType(); ParameterizedType type = (ParameterizedType) returnType; Type[] typeArguments = type.getActualTypeArguments(); Class typeArgClass = (Class) typeArguments[0]; // System.out.println("typeArgClass = " + typeArgClass); return typeArgClass; /* * Class rtnType = method.getReturnType(); TypeVariable[] typeParam = * rtnType.getTypeParameters(); Class listType = typeParam[0].getClass(); * return ClassUtil.getObjectClass(listType); */ } public int getUserArgNum() { int methodArgNum = method.getParameterTypes().length; if (isContextSupplied) { return methodArgNum - 1; } return methodArgNum; } /** * Tests whether the current {@link EngineContext} must be passed to this * function. * * @return true if the current context should be supplied to this function */ private static boolean isContextSupplied(Method method) { Class[] paramTypes = method.getParameterTypes(); if (paramTypes.length <= 0) return false; if (paramTypes[0] == EngineContext.class) return true; return false; } public int getArgOffset() { return argOffset; } /** * Creates an argument array of the required size for this function. * * @return */ public Object[] createArgArray() { int count = getUserArgNum(); if (isContextSupplied) { count++; } Object[] argVal = new Object[count]; return argVal; } public Object eval(Scope scope, List args) { return eval(scope, null, args); } public Object eval(Scope scope, Object target, List args) { Object[] argVal = createArgArray(); evalArgs(scope, args, argVal, getArgOffset()); Object result = invoke(scope, target, argVal); return result; } private Object[] evalArgs(Scope scope, List args, Object[] argVal, int offset) { for (int i = 0; i < args.size(); i++) { argVal[i + offset] = ((ParseTreeNode) args.get(i)).eval(scope); } return argVal; } private Object invoke(Scope scope, Object target, Object[] args) { if (isContextSupplied) { args[0] = scope.getContext(); } return invoke(method, target, args); } /** * Invokes a static method, and maps any exceptions to JEQL standard exception * classes. * * @param method * @param arg0 * @param args * @return * @throws ExecutionException * */ public static Object invoke(Method method, Object[] args) { return invoke(method, null, args); } /** * Invokes a method on a target object, and maps any exceptions to JEQL * standard exception classes. * * @param method * @param arg0 * @param args * @return * @throws ExecutionException * */ public static Object invoke(Method method, Object target, Object[] args) { Object result = null; try { result = method.invoke(target, args); } catch (InvocationTargetException ex) { Throwable t = ex.getCause(); if (t instanceof JeqlException) throw (JeqlException) t; throwExecutionException(method, args, t); } catch (Exception ex) { // System.out.println(ex.getMessage()); throwExecutionException(method, args, ex); } return result; } private static void throwExecutionException(Method method, Object[] args, Throwable t) { throw new ExecutionException(functionSig(method, args) + " : " + ExceptionUtil.getMessage(t)); } private static String functionSig(Method method, Object[] args) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.length; i++) { if (i > 0) sb.append(","); sb.append(TypeUtil.toCodeStringLimited(args[i])); } return FunctionRegistry.functionName(method.getDeclaringClass().getName(), method.getName()) + "(" + sb.toString() + ")"; } private static String invocationErrMsg(InvocationTargetException ex) { Throwable targetEx = ex.getTargetException(); String msg = ClassUtil.classname(targetEx.getClass()) + ": " + targetEx.getMessage(); return msg; } }