package com.spun.util; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Arrays; import com.spun.util.filters.Filter; import com.spun.util.filters.FilterUtils; public class MethodExecutionPath implements Serializable { public static final Object NULL_ENCOUNTERED_ON_PATH = new Object(); private Class<? extends Object> classType, returnType; private String methodNames[]; private Parameters[] parameters; private Method methods[]; /***********************************************************************/ public MethodExecutionPath(Class<? extends Object> clazz, String methodName) { this(clazz, new String[]{methodName}, null); } /***********************************************************************/ public MethodExecutionPath(Class<? extends Object> clazz, String... methodNames) { this(clazz, methodNames, null); } /***********************************************************************/ public MethodExecutionPath(Class<? extends Object> clazz, String methodName, Parameters parameters) { this(clazz, new String[]{methodName}, new Parameters[]{parameters}); } /***********************************************************************/ public MethodExecutionPath(Class<? extends Object> clazz, String[] methodNames, Parameters[] parameters) { this.classType = clazz; this.methodNames = methodNames; this.parameters = parameters; this.methods = getRecursiveMethods(clazz, methodNames, parameters); this.returnType = (this.methods != null) ? methods[methods.length - 1].getReturnType() : null; } /************************************************************************/ public static MethodExecutionPath method(Class<? extends Object> class1, String method, Object... paramaters) { return new MethodExecutionPath(class1, new String[]{method}, new Parameters[]{new Parameters(paramaters)}); } public static Method[] getRecursiveMethods(Class clazz, String[] methodNames, Parameters[] parameters) { if (clazz == null) { return null; } Method methods[] = new Method[methodNames.length]; String currentMethodName = null; Parameters parameter = null; try { for (int i = 0; i < methods.length; i++) { currentMethodName = methodNames[i]; parameter = Parameters.getParametersFor(parameters, i); methods[i] = parameter.getBestFitMethod(clazz, currentMethodName); clazz = methods[i].getReturnType(); } return methods; } catch (Exception e) { throw new Error("Unable to get method for " + clazz.getName() + "." + currentMethodName + "(" + parameter + ")", e); } } /***********************************************************************/ public Object extractValue(Object object) { if (object == null) { return NULL_ENCOUNTERED_ON_PATH; } Method[] methods = this.methods == null ? getRecursiveMethods(object.getClass(), this.methodNames, this.parameters) : this.methods; for (int i = 0; i < methods.length; i++) { if (object == null) { return NULL_ENCOUNTERED_ON_PATH; } object = extractValue(object, methods[i], Parameters.getParametersFor(parameters, i).values); } return object; } /************************************************************************/ private static Object extractValue(Object object, Method method, Object values[]) { try { return method.invoke(object, values); } catch (Exception e) { throw new Error("Problems extracting values from " + object.getClass().getName() + "." + method.getName(), e); } } /************************************************************************/ public Class<? extends Object> getClassType() { return classType; } /***********************************************************************/ public Class getReturnType() { return returnType; } /***********************************************************************/ /***********************************************************************/ public static class Parameters { public static final Parameters EMPTY = new Parameters(null, null); public Class[] definitions; public Object[] values; /***********************************************************************/ public Parameters(Object... values) { if (!ArrayUtils.isEmpty(values)) { this.values = values; this.definitions = (Class[]) ObjectUtils.extractArray(values, "getClass"); } } /***********************************************************************/ public Parameters(Class[] definitions, Object[] values) { this.definitions = definitions; this.values = values; } /***********************************************************************/ public Method getBestFitMethod(Class clazz, String currentMethodName) throws NoSuchMethodException { return getBestFitMethod(clazz, currentMethodName, definitions); } /***********************************************************************/ public static Method getBestFitMethod(Class clazz, String currentMethodName, Class[] definitions) throws NoSuchMethodException { try { return clazz.getMethod(currentMethodName, definitions); } catch (NoSuchMethodException e) { Method[] methods = (Method[]) FilterUtils.retainExtracted(clazz.getMethods(), new MethodParameterFilter(currentMethodName, definitions)).toArray(new Method[0]); if (methods.length == 0) { throw e; } if (methods.length == 1) { return methods[0]; } else { throw new Error("Don't know how to handle multiple available methods yet."); } } } /***********************************************************************/ public static Parameters getParametersFor(Parameters[] parameters, int i) { if (parameters == null || (i >= parameters.length) || parameters[i] == null) { return EMPTY; } return parameters[i]; } /***********************************************************************/ public String toString() { return definitions == null ? "" : Arrays.asList(definitions).toString(); } } public static class MethodParameterFilter implements Filter { private String methodName; private Class[] classParameters; /***********************************************************************/ public MethodParameterFilter(String methodName, Class[] classParameters) { super(); this.methodName = methodName; this.classParameters = classParameters; } /***********************************************************************/ public boolean isExtracted(Object object) throws IllegalArgumentException { ObjectUtils.assertInstance(Method.class, object); Method m = (Method) object; if (m.getName().equals(methodName) && m.getParameterTypes().length == classParameters.length) { Class<?>[] params = m.getParameterTypes(); for (int i = 0; i < params.length; i++) { if (!ObjectUtils.isThisInstanceOfThat(classParameters[i], params[i])) { return false; } } return true; } else { return false; } } } /***********************************************************************/ /***********************************************************************/ }