package fitnesse.slim.fixtureInteraction;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import fitnesse.slim.ConverterSupport;
import fitnesse.slim.MethodExecutionResult;
import fitnesse.slim.SlimError;
import fitnesse.slim.SlimServer;
public class DefaultInteraction implements FixtureInteraction {
private static final Method AROUND_METHOD;
static {
try {
AROUND_METHOD = InteractionAwareFixture.class.getMethod("aroundSlimInvoke", FixtureInteraction.class, Method.class, Object[].class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public Object createInstance(List<String> paths, String className, Object[] args)
throws IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class<?> k = searchPathsForClass(paths, className);
Constructor<?> constructor = getConstructor(k, args);
if (constructor == null) {
throw new SlimError(String.format("message:<<%s %s>>",
SlimServer.NO_CONSTRUCTOR, className));
}
return newInstance(args, constructor);
}
private Object newInstance(Object[] args, Constructor<?> constructor)
throws IllegalAccessException, InstantiationException,
InvocationTargetException {
Object[] initargs = ConverterSupport.convertArgs(args,
constructor.getParameterTypes());
return newInstance(constructor, initargs);
}
protected Class<?> searchPathsForClass(List<String> paths, String className) {
Class<?> k = getClass(className);
if (k != null) {
return k;
}
for (String path : paths) {
k = getClass(path + "." + className);
if (k != null) {
return k;
}
}
throw new SlimError(String.format("message:<<%s %s>>", SlimServer.NO_CLASS, className));
}
protected Class<?> getClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
return null;
}
}
protected Constructor<?> getConstructor(Class<?> clazz,
Object[] args) {
for (Constructor<?> constructor : clazz.getConstructors()) {
Class<?>[] arguments = constructor.getParameterTypes();
if (arguments.length == args.length) {
return constructor;
}
}
return null;
}
protected Object newInstance(Constructor<?> constructor, Object... initargs) throws InvocationTargetException, InstantiationException, IllegalAccessException {
return constructor.newInstance(initargs);
}
@Override
public MethodExecutionResult findAndInvoke(String methodName, Object instance, Object... args) throws Throwable {
Method method = findMatchingMethod(methodName, instance, args);
if (method != null) {
return this.invokeMethod(instance, method, args);
}
return MethodExecutionResult.noMethod(methodName, instance.getClass(), args.length);
}
protected Method findMatchingMethod(String methodName, Object instance, Object... args) {
Class<?> k = instance.getClass();
Method[] methods = k.getMethods();
int nArgs = args.length;
for (Method method : methods) {
boolean hasMatchingName = method.getName().equals(methodName);
boolean hasMatchingArguments = method.getParameterTypes().length == nArgs;
if (hasMatchingName && hasMatchingArguments) {
return method;
}
}
return null;
}
protected MethodExecutionResult invokeMethod(Object instance, Method method, Object[] args) throws Throwable {
Object[] convertedArgs = convertArgs(method, args);
Object retval = callMethod(instance, method, convertedArgs);
Class<?> retType = method.getReturnType();
return new MethodExecutionResult(retval, retType);
}
protected Object[] convertArgs(Method method, Object[] args) {
Type[] argumentParameterTypes = method.getGenericParameterTypes();
return ConverterSupport.convertArgs(args, argumentParameterTypes);
}
protected Object callMethod(Object instance, Method method, Object[] convertedArgs) throws Throwable {
try {
Object result;
if (instance instanceof InteractionAwareFixture) {
// invoke via interaction, so it can also do its thing on the aroundMethod invocation
Object[] args = { this, method, convertedArgs };
result = methodInvoke(AROUND_METHOD, instance, args);
} else {
result = methodInvoke(method, instance, convertedArgs);
}
return result;
} catch (InvocationTargetException e) {
if (e.getCause() != null) {
throw e.getCause();
} else {
throw e.getTargetException();
}
}
}
@Override
public Object methodInvoke(Method method, Object instance, Object... convertedArgs) throws Throwable {
try {
return method.invoke(instance, convertedArgs);
} catch (InvocationTargetException e) {
if (e.getCause() != null) {
throw e.getCause();
} else {
throw e.getTargetException();
}
} catch (IllegalArgumentException e) {
throw new RuntimeException("Bad call of: " + method.getDeclaringClass().getName() + "." + method.getName()
+ ". On instance of: " + instance.getClass().getName(), e);
}
}
}