-
Notifications
You must be signed in to change notification settings - Fork 2
/
Delegator.java
97 lines (83 loc) · 3.95 KB
/
Delegator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.timgroup.karg.reflection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.google.common.base.Preconditions;
public class Delegator<T, I> {
private final Method targetMethod;
private final Class<? super I> delegateClass;
public static final class MethodNameBinder {
private final String methodName;
private MethodNameBinder(String methodName) {
this.methodName = methodName;
}
public <T> Delegator.TargetClassBinder<T> of(final Class<? super T> targetClass) {
return new Delegator.TargetClassBinder<T>() {
@Override public <I> Delegator<T, I> to(Class<? super I> delegateClass) {
Method targetMethod = getTargetMethod(methodName, targetClass, delegateClass);
return new Delegator<T, I>(targetMethod, delegateClass);
}
};
}
public <T> ProxyBinder<T> ofInstance(final T targetInstance) {
return new ProxyBinder<T>() {
@SuppressWarnings("unchecked")
@Override public <I> I to(Class<? super I> delegateClass) {
Class<T> targetClass = (Class<T>) targetInstance.getClass();
return ofMethod(methodName).of(targetClass).<I>to(delegateClass).delegateTo(targetInstance);
}
};
}
}
public static interface TargetClassBinder<T> {
<I> Delegator<T, I> to(Class<? super I> delegateClass);
}
public static interface ProxyBinder<T> {
<I> I to(Class<? super I> delegateClass);
}
public static Delegator.MethodNameBinder ofMethod(final String methodName) {
return new MethodNameBinder(methodName);
}
private static Method getTargetMethod(String methodName, Class<?> targetClass, Class<?> delegateClass) {
try {
Preconditions.checkArgument(delegateClass.isInterface(), "Delegate class must be an interface");
Preconditions.checkArgument(delegateClass.getDeclaredMethods().length == 1,
"Delegate interface must have only one argument");
Method delegateMethod = delegateClass.getDeclaredMethods()[0];
Method method = targetClass.getMethod(methodName, delegateMethod.getParameterTypes());
Preconditions.checkArgument(delegateMethod.getReturnType().isAssignableFrom(method.getReturnType()));
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public Delegator(Method targetMethod, Class<? super I> delegateClass) {
this.targetMethod = targetMethod;
this.delegateClass = delegateClass;
}
@SuppressWarnings("unchecked")
I delegateTo(T instance) {
return (I) Proxy.newProxyInstance(instance.getClass().getClassLoader(),
new Class<?>[] { delegateClass },
invocationHandlerFor(instance));
}
private InvocationHandler invocationHandlerFor(final T instance) {
return new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
return targetMethod.invoke(instance, args);
} catch (InvocationTargetException e) {
Class<? extends Throwable> targetExceptionClass = e.getTargetException().getClass();
for(Class<?> exceptionType : method.getExceptionTypes()) {
if (exceptionType.isAssignableFrom(targetExceptionClass)) {
throw e.getTargetException();
}
}
throw e;
}
}
};
}
}