package org.nutz.lang; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import static java.lang.String.*; /** * 函数调用方式 * * @author zozoh(zozohtnt@gmail.com) * @author wendal(wendal1985@gmail.com) */ public class Invoking { /*------------------------------------------------------------------------*/ private static abstract class Invoker { protected Method method; public Invoker(Method method) { this.method = method; } abstract Object invoke(Object obj) throws Exception; } /*------------------------------------------------------------------------*/ private static class DefaultInvoker extends Invoker { private Object[] args; public DefaultInvoker(Method method, Object[] args) { super(method); this.args = args; } @Override Object invoke(Object obj) throws Exception { return method.invoke(obj, args); } } /*------------------------------------------------------------------------*/ // zozoh: 这个实现有问题,需要检查一下 private static class DynamicArgsInvoker extends Invoker { private Object args; public DynamicArgsInvoker(Method method, Object args) { super(method); this.args = args; } @Override Object invoke(Object obj) throws Exception { return method.invoke(obj, args); } } /*------------------------------------------------------------------------*/ private static class NullArgInvoker extends Invoker { public NullArgInvoker(Method method) { super(method); } @Override Object invoke(Object obj) throws Exception { return method.invoke(obj); } } /*------------------------------------------------------------------------*/ public Invoking(Class<?> type, String methodName, Object... args) { try { // get directoy if (null == args || args.length == 0) { invoker = new NullArgInvoker(type.getMethod(methodName)); } else { // get all same name methods Method[] all = type.getMethods(); List<Method> candidates = new ArrayList<Method>(all.length); for (Method m : all) if (m.getName().equals(methodName)) { // int mod = // m.getParameterTypes().length - // args.length; // if (mod == 0 || mod == 1) candidates.add(m); } // get argTypes Class<?>[] argTypes = Mirror.evalToTypes(args); Object dynaArg = Mirror.evalArgToRealArray(args); // check the candidate methods can be // match or not for (Iterator<Method> it = candidates.iterator(); it.hasNext();) { Method m = it.next(); Class<?>[] pts = m.getParameterTypes(); MatchType mr = Mirror.matchParamTypes(pts, argTypes); if (MatchType.YES == mr) { invoker = new DefaultInvoker(m, args); break; } else if (MatchType.LACK == mr) { invoker = new DefaultInvoker(m, Lang.arrayLast( args, Mirror.blankArrayArg(pts))); break; } else if (null != dynaArg && pts.length == 1) { if (pts[0] == dynaArg.getClass()) { invoker = new DynamicArgsInvoker(m, Lang.array2array(args, pts[0].getComponentType())); break; } if (pts[0].isArray()) { if (Mirror.me(pts[0].getComponentType()).getWrapper() .equals(Mirror.me(dynaArg.getClass().getComponentType()).getWrapper())){ invoker = new DynamicArgsInvoker(m, Lang.array2array(args, pts[0].getComponentType())); break; } } } } // if fail to match, try to cast args // to same length param method // ro to last param is "T...", length+1 // method if (null == invoker) try { for (Iterator<Method> it = candidates.iterator(); it.hasNext();) { Method m = it.next(); Class<?>[] pts = m.getParameterTypes(); if (pts.length == args.length) { invoker = new DefaultInvoker(m, Lang.array2ObjectArray(args, pts)); } else if (pts.length == args.length + 1 && pts[args.length].isArray()) { invoker = new DefaultInvoker(m, Lang.array2ObjectArray(args, pts)); } } } catch (Exception e) {} // to same length + last is dynamic // argument method } } catch (NoSuchMethodException e) { throw Lang.wrapThrow(e); } if (null == invoker) throw new InvokingException("Don't know how to invoke [%s].%s() by args:\n %s", type.getName(), methodName, Lang.concat('\n', args)); msg = format( "Fail to invoke [%s].%s() by args:\n %s", type.getName(), methodName, Lang.concat('\n', args)) + "\nFor the reason: %s"; } private String msg; private Invoker invoker; public Object invoke(Object obj) { try { return invoker.invoke(obj); } catch (Throwable e) { throw new InvokingException(msg, Lang.unwrapThrow(e)); } } }