/** * Copyright (c) 2006 - 2009 Smaxe Ltd (www.smaxe.com). * All rights reserved. */ package com.smaxe.uv.invoker.support; import com.smaxe.uv.invoker.IMethodInvoker; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; /** * <code>MethodInvoker</code> - very simple, general and, of course, * inefficient {@link IMethodInvoker} implementation. * * @author Andrei Sochirca */ public final class MethodInvoker extends Object implements IMethodInvoker { // fields private final Executor executor; private final Map<Class, Class> primitives; private final String method; /** * Constructor. */ public MethodInvoker() { this(null /*executor*/, ".client()"); } /** * Constructor. * * @param method */ public MethodInvoker(final String method) { this(null /*executor*/, method); } /** * Constructor. * * @param executor executor to use for method invocation * @param method class method */ public MethodInvoker(final Executor executor, final String method) { this.method = method; this.executor = executor; this.primitives = createPrimitives(); } public void invoke(final Object o, final String method, final ICallback callback, final Object... args) { final String methodToInvoke = method.indexOf('|') < 0 ? method : method.replace('|', '_'); final Runnable task = new Runnable() { @SuppressWarnings("unchecked") public void run() { try { final List<Method> methods = findMethods(o.getClass().getMethods(), methodToInvoke); switch (methods.size()) { case 0: { final String clazz = o.getClass().getSimpleName(); throw new NoSuchMethodException("Method '" + method + "' is not found in the " + clazz + MethodInvoker.this.method + " class. " + "Please define method '" + methodToInvoke + "' in the " + clazz + MethodInvoker.this.method + " class that accepts parameters " + Arrays.toString(args)); } case 1: { final Method method = methods.get(0); final Class[] parameterTypes = method.getParameterTypes(); Object result = null; if (parameterTypes.length == 1 && (parameterTypes[0] == Object[].class)) { result = method.invoke(o, new Object[] {args}); } else { result = method.invoke(o, args); } if (callback != null) callback.onResult(result); } break; default: { Method method = null; for (Method m : methods) { final Class[] parameterTypes = m.getParameterTypes(); if (parameterTypes.length != args.length) continue; boolean success = true; for (int i = 0; i < args.length; i++) { final Object value = args[i]; if (value == null) continue; final Class parameterType = getParameterClass(parameterTypes[i]); if (!parameterType.isAssignableFrom(value.getClass())) { success = false; break; } } if (success) { method = m; break; } } if (method == null) throw new NoSuchMethodException("Method '" + methodToInvoke + "' has different signature in the " + o.getClass()); final Object result = method.invoke(o, args); if (callback != null) callback.onResult(result); } } } catch (Exception e) { if (callback != null) callback.onException(e); } } }; if (executor == null) { task.run(); } else { executor.execute(task); } } // inner use methods /** * Returns parameter class. * * @param clazz * @return parameter class */ private Class getParameterClass(final Class clazz) { final Class c = primitives.get(clazz); return c == null ? clazz : c; } /** * Creates and returns primitive -> class mapping. * * @return primitive -> class mapping */ private Map<Class, Class> createPrimitives() { Map<Class, Class> map = new HashMap<Class, Class>(32); map.put(Boolean.TYPE, Boolean.class); map.put(Character.TYPE, Number.class); map.put(Byte.TYPE, Number.class); map.put(Short.TYPE, Number.class); map.put(Integer.TYPE, Number.class); map.put(Long.TYPE, Number.class); map.put(Float.TYPE, Number.class); map.put(Double.TYPE, Number.class); return map; } /** * Finds methods defined by <code>name</code>. * * @param methods * @param name * @return methods */ private static List<Method> findMethods(final Method[] methods, final String name) { List<Method> methodsList = new ArrayList<Method>(methods.length); for (Method method : methods) { if (name.equals(method.getName())) methodsList.add(method); } return methodsList; } }