blob: f9d426adc0431d6210b7a8c23cc1771c913c8bb6 [file] [log] [blame]
package net.sf.cglib.proxy;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ConcurrentWeakValueHashMap;
import net.sf.cglib.core.CodeGenerationException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
/**
* @author peter
*/
public class AdvancedProxy {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.impl.AdvancedProxy");
public static Method FINALIZE_METHOD;
public static Method EQUALS_METHOD;
public static Method HASHCODE_METHOD;
public static Method TOSTRING_METHOD;
static {
try {
FINALIZE_METHOD = Object.class.getDeclaredMethod("finalize");
EQUALS_METHOD = Object.class.getDeclaredMethod("equals", Object.class);
HASHCODE_METHOD = Object.class.getDeclaredMethod("hashCode");
TOSTRING_METHOD = Object.class.getDeclaredMethod("toString");
}
catch (NoSuchMethodException e) {
LOG.error(e);
}
}
private static final Map<ProxyDescription, Factory> ourFactories = new ConcurrentWeakValueHashMap<ProxyDescription, Factory>();
private static final CallbackFilter NO_OBJECT_METHODS_FILTER = new CallbackFilter() {
public int accept(Method method) {
if (AdvancedProxy.FINALIZE_METHOD.equals(method)) {
return 1;
}
if ((method.getModifiers() & Modifier.ABSTRACT) != 0) {
return 0;
}
return 1;
}
};
private static final CallbackFilter WITH_OBJECT_METHODS_FILTER = new CallbackFilter() {
public int accept(Method method) {
if (AdvancedProxy.FINALIZE_METHOD.equals(method)) {
return 1;
}
if (HASHCODE_METHOD.equals(method) || TOSTRING_METHOD.equals(method) || EQUALS_METHOD.equals(method)) {
return 0;
}
if ((method.getModifiers() & Modifier.ABSTRACT) != 0) {
return 0;
}
return 1;
}
};
public static InvocationHandler getInvocationHandler(Object proxy) {
return (InvocationHandler)((Factory) proxy).getCallback(0);
}
public static <T> T createProxy(final InvocationHandler handler, final Class<T> superClass, final Class... otherInterfaces) {
return createProxy(superClass, otherInterfaces, handler, ArrayUtil.EMPTY_OBJECT_ARRAY);
}
public static <T> T createProxy(final Class<T> superClass, final Class... otherInterfaces) {
return createProxy(superClass, otherInterfaces, new InvocationHandler() {
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
throw new AbstractMethodError(method.toString());
}
}, false, ArrayUtil.EMPTY_OBJECT_ARRAY);
}
public static <T> T createProxy(final Class<T> superClass,
final Class[] interfaces,
final InvocationHandler handler, final Object... constructorArgs) {
return createProxy(superClass, interfaces, handler, true, constructorArgs);
}
public static <T> T createProxy(final Class<T> superClass,
final Class[] interfaces,
final InvocationHandler handler,
final boolean interceptObjectMethods, final Object... constructorArgs) {
try {
final Callback[] callbacks = new Callback[]{handler, NoOp.INSTANCE};
final ProxyDescription key = new ProxyDescription(superClass, interfaces);
Factory factory = ourFactories.get(key);
if (factory != null) {
return (T)factory.newInstance(getConstructorParameterTypes(factory.getClass(), constructorArgs), constructorArgs, callbacks);
}
AdvancedEnhancer e = new AdvancedEnhancer();
e.setInterfaces(interfaces);
e.setCallbacks(callbacks);
e.setCallbackFilter(interceptObjectMethods ? WITH_OBJECT_METHODS_FILTER : NO_OBJECT_METHODS_FILTER);
if (superClass != null) {
e.setSuperclass(superClass);
factory = (Factory)e.create(getConstructorParameterTypes(superClass, constructorArgs), constructorArgs);
}
else {
assert constructorArgs.length == 0;
factory = (Factory)e.create();
}
ourFactories.put(key, factory);
return (T)factory;
}
catch (CodeGenerationException e) {
final Throwable throwable = e.getCause();
if (throwable instanceof InvocationTargetException) {
final InvocationTargetException targetException = (InvocationTargetException)throwable;
final Throwable cause = targetException.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
}
if (cause instanceof Error) {
throw (Error)cause;
}
}
if (throwable instanceof RuntimeException) {
throw (RuntimeException)throwable;
}
throw e;
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Class[] getConstructorParameterTypes(final Class aClass, final Object... constructorArgs) {
if (constructorArgs.length == 0) return ArrayUtil.EMPTY_CLASS_ARRAY;
loop: for (final Constructor constructor : aClass.getDeclaredConstructors()) {
final Class[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == constructorArgs.length) {
for (int i = 0; i < parameterTypes.length; i++) {
Class parameterType = parameterTypes[i];
final Object constructorArg = constructorArgs[i];
if (!parameterType.isInstance(constructorArg) && constructorArg != null) {
continue loop;
}
}
return constructor.getParameterTypes();
}
}
throw new AssertionError("Cannot find constructor for arguments: " + Arrays.asList(constructorArgs));
}
private static class ProxyDescription {
private final Class mySuperClass;
private final Class[] myInterfaces;
public ProxyDescription(final Class superClass, final Class[] interfaces) {
mySuperClass = superClass;
myInterfaces = interfaces;
}
public String toString() {
return mySuperClass + " " + (myInterfaces != null ? Arrays.asList(myInterfaces) : "");
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ProxyDescription that = (ProxyDescription)o;
if (!Arrays.equals(myInterfaces, that.myInterfaces)) return false;
if (mySuperClass != null ? !mySuperClass.equals(that.mySuperClass) : that.mySuperClass != null) return false;
return true;
}
public int hashCode() {
int result;
result = (mySuperClass != null ? mySuperClass.hashCode() : 0);
return result;
}
}
}