package fitnesse.slim.fixtureInteraction;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CachedInteraction extends DefaultInteraction {
private static final Constructor<?> noConstructor = NotExisting.class.getConstructors()[0];
private static final Method noMethod = NotExisting.class.getDeclaredMethods()[0];
private final Map<String, Constructor<?>> constructorsByClassAndArgs = new HashMap<>();
private final Map<String, Class<?>> classCache = new HashMap<>();
private final Map<MethodKey, Method> methodsByNameAndArgs = new HashMap<>();
@Override
protected Constructor<?> getConstructor(Class<?> clazz, Object[] args) {
String key = getConstructorKey(clazz, args);
Constructor<?> cached = constructorsByClassAndArgs.get(key);
if (cached == noConstructor) return null;
if (cached != null) return cached;
Constructor<?> constructor = handleConstructorCacheMiss(clazz, args);
if (constructor == null) {
constructorsByClassAndArgs.put(key, noConstructor);
} else {
constructorsByClassAndArgs.put(key, constructor);
}
return constructor;
}
protected String getConstructorKey(Class<?> clazz, Object[] args) {
return clazz.getName()+ "_" + args.length;
}
@Override
protected Class<?> getClass(String className) {
Class<?> cached = classCache.get(className);
if (cached == NotExisting.class) return null;
if (cached != null) return cached;
Class<?> k = handleClassCacheMiss(className);
if (k == null) {
classCache.put(className, NotExisting.class);
} else {
classCache.put(className, k);
}
return k;
}
@Override
protected Method findMatchingMethod(String methodName, Object instance, Object... args) {
MethodKey key = new MethodKey(instance.getClass(), methodName, args.length);
Method cached = methodsByNameAndArgs.get(key);
if (cached == noMethod) return null;
if (cached != null) return cached;
Method method = handleMethodCacheMiss(methodName, instance, args);
if (method == null) {
methodsByNameAndArgs.put(key, noMethod);
} else {
methodsByNameAndArgs.put(key, method);
}
return method;
}
protected Constructor<?> handleConstructorCacheMiss(Class<?> clazz, Object[] args) {
return super.getConstructor(clazz, args);
}
protected Class<?> handleClassCacheMiss(String className) {
return super.getClass(className);
}
protected Method handleMethodCacheMiss(String methodName, Object instance, Object[] args) {
return super.findMatchingMethod(methodName, instance, args);
}
private static final class MethodKey {
private final String k;
private final String method;
private final int nArgs;
public MethodKey(Class<?> k, String method, int nArgs) {
this.k = k.getName();
this.method = method;
this.nArgs = nArgs;
}
@Override
public int hashCode() {
int result = k.hashCode();
result = 31 * result + method.hashCode();
result = 31 * result + nArgs;
return result;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
MethodKey methodKey = (MethodKey) o;
if (nArgs != methodKey.nArgs) return false;
if (!k.equals(methodKey.k)) return false;
return method.equals(methodKey.method);
}
}
private static final class NotExisting {
public NotExisting() {
}
public void doIt() {
}
}
}