package net.sourceforge.javautil.interceptor.type.cjcw;
import java.lang.reflect.Method;
import java.util.List;
import net.sourceforge.javautil.bytecode.BytecodeCompiler;
import net.sourceforge.javautil.bytecode.api.BytecodeContext;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.MethodDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess.Scope;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeBlock;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeConstructor;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeConstructorBase;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeContextMethod;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeMethodConcrete;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.interceptor.IInterceptedInstanceWrapper;
import net.sourceforge.javautil.interceptor.IInterceptorManager;
import net.sourceforge.javautil.interceptor.IInterceptorManipulator;
import net.sourceforge.javautil.interceptor.type.InterceptorProxyTypeInterceptor;
/**
* A proxy type that wraps an instance of it's super class overriding public methods and then proxying to an underlying instance.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class InterceptorProxyTypeCJCW extends InterceptorProxyTypeInterceptor {
public static final String TARGET_FIELD_NAME = "target";
public InterceptorProxyTypeCJCW(BytecodeCompiler compiler, List<IInterceptorManipulator> abilities, Class type, String name) {
super(compiler, abilities, type, name);
this.addInstanceField(TARGET_FIELD_NAME, IInterceptedInstanceWrapper.class, Scope.Private, true);
}
@Override public void addProxyConstructors() {
InterceptorProxyConstructor apc = new InterceptorProxyConstructor(abilities);
for (IBytecodeConstructor constructor : getSuperType().getDeclaredConstructors()) {
if (constructor.getAccess().getScope() == Scope.Public) {
TypeDescriptor[] parameters = constructor.getDescriptor().getParameters();
parameters = CollectionUtil.insert(parameters, 0, TypeDescriptor.getFor(IInterceptorManager.class), TypeDescriptor.getFor(IInterceptedInstanceWrapper.class));
BytecodeConstructorBase pc = this.addConstructor(Scope.Public, constructor.isVarArgs(), parameters);
pc.copyAnnotations(constructor);
pc.setMethodBody(apc);
}
}
}
@Override public void addProxyMethods() {
final InterceptorProxyMethod apm = new InterceptorProxyMethod();
BytecodeBlock apmb = new BytecodeBlock() {
@Override protected void writeInstructions(BytecodeContextMethod context) {
apm.write(context);
}
};
IBytecodeResolvable st = getSuperType();
while (st != null && !"java.lang.Object".equals( st.getType().getClassName() )) {
for (IBytecodeMethod method : st.getDeclaredMethods()) {
if (method.getAccess().isStatic() || method.getAccess().getScope() != Scope.Public) continue;
BytecodeMethodConcrete bmc = this.addMethod(method.getName(), Scope.Public, false, true, method.getDescriptor());
bmc.copyAnnotations(method);
bmc.setMethodBody(apmb);
}
st = st.getSuperType();
}
try {
Method toString = type.getMethod("toString");
if (toString.getDeclaringClass() == Object.class) {
this.addMethod("toString", Scope.Public, false, true, new MethodDescriptor(toString))
.setMethodBody(new BytecodeBlock() {
@Override protected void writeInstructions(BytecodeContextMethod context) {
context.returnValue(context.createInvocation(
context.getThisField(TARGET_FIELD_NAME).createInvocation(context, "get", context.resolve("this")), "toString")
);
}
});
}
} catch (NoSuchMethodException e) {}
}
}