package org.emdev.ui.actions; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.emdev.common.log.LogContext; import org.emdev.common.log.LogManager; import org.emdev.utils.LengthUtils; import org.emdev.utils.collections.SparseArrayEx; public class ActionControllerMethod { private static final LogContext LCTX = LogManager.root().lctx("Actions"); private static HashMap<Class<?>, SparseArrayEx<Method>> s_methods = new HashMap<Class<?>, SparseArrayEx<Method>>(); private final IActionController<?> m_controller; private final ActionEx m_action; private Object m_target; private Method m_method; private Throwable m_errorInfo; /** * Constructor * * @param controller * action controller * @param actionId * action id */ ActionControllerMethod(final IActionController<?> controller, final ActionEx action) { m_controller = controller; m_action = action; } /** * Invokes controller method for the given controller and action * * @param action * action * @return execution result * @throws Throwable * thrown by reflection API or executed method */ public Object invoke(final ActionEx action) throws Throwable { final Method m = getMethod(); if (m != null) { return m.invoke(m_target, action); } else { throw m_errorInfo; } } /** * @return <code>true</code> if method is exist */ public boolean isValid() { return null != getMethod(); } /** * Returns reflection error info. * * @return {@link Throwable} */ public Throwable getErrorInfo() { getMethod(); return m_errorInfo; } /** * @return {@link Method} */ Method getMethod() { if (m_method == null && m_errorInfo == null) { List<String> classes = new ArrayList<String>(); for (IActionController<?> c = m_controller; m_method == null && c != null; c = c.getParent()) { classes.add(c.getManagedComponent().getClass().getSimpleName()); classes.add(c.getClass().getSimpleName()); m_method = getMethod(c.getManagedComponent(), m_action.id); m_target = m_method != null ? c.getManagedComponent() : null; if (m_method == null) { m_method = getMethod(c, m_action.id); m_target = m_method != null ? c : null; } } if (m_method == null) { String text = "No appropriate method found for action " + m_action.name + " in the following classes: " + classes; m_errorInfo = new NoSuchMethodException(text); } else { LCTX.d("Action method found for " + m_action.name + ": " + m_method); } } return m_method; } /** * Gets the method. * * @param target * a possible action target * @param actionId * the action id * * @return the method */ private static synchronized Method getMethod(final Object target, final int actionId) { Class<? extends Object> clazz = target.getClass(); SparseArrayEx<Method> methods = s_methods.get(clazz); if (methods == null) { methods = getActionMethods(clazz); s_methods.put(clazz, methods); } return methods.get(actionId); } /** * Gets the method. * * @param clazz * an action target class * * @return the map of action methods method */ private static SparseArrayEx<Method> getActionMethods(final Class<?> clazz) { final SparseArrayEx<Method> result = new SparseArrayEx<Method>(); getActionMethodsFromMethodAnnotations(clazz, result); return result; } private static void getActionMethodsFromMethodAnnotations(final Class<?> clazz, final SparseArrayEx<Method> result) { final Method[] methods = clazz.getMethods(); for (final Method method : methods) { final int modifiers = method.getModifiers(); if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { final Class<?>[] args = method.getParameterTypes(); if (LengthUtils.length(args) == 1 && ActionEx.class.equals(args[0])) { if (method.isAnnotationPresent(ActionMethod.class)) { final ActionMethod annotation = method.getAnnotation(ActionMethod.class); if (annotation != null) { for (int id : annotation.ids()) { result.put(id, method); } } } } } } } @Override public String toString() { Method m = getMethod(); return (m != null ? "" + m : "no method: " + m_errorInfo.getMessage()); } }