package org.gap.jseed;
import static org.gap.jseed.JavaWriter.braces;
import static org.gap.jseed.JavaWriter.call;
import static org.gap.jseed.JavaWriter.createParameters;
import static org.gap.jseed.JavaWriter.getInvokingMethodCode;
import static org.gap.jseed.JavaWriter.getParameterTypes;
import static org.gap.jseed.JavaWriter.line;
import static org.gap.jseed.JavaWriter.returnCall;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import org.gap.jseed.annotation.Advice;
import org.gap.jseed.annotation.BeanUtil;
public class MethodInjector extends AbstractInjector {
private Map<Class<? extends Annotation>, Class<? extends InvocationHandler>> annotations;
public MethodInjector() {
annotations = new HashMap<Class<? extends Annotation>, Class<? extends InvocationHandler>>();
}
public void injectBehavior(Class<?> theInterface, CtClass abstraction, CtClass implementation) throws ClassNotFoundException, CannotCompileException, NotFoundException {
for (CtMethod eachMethod : abstraction.getDeclaredMethods()) {
for (Object eachAnnot : eachMethod.getAnnotations()) {
for (Class<? extends Annotation> methodAnnotion : annotations.keySet()) {
if (methodAnnotion.isInstance(eachAnnot)) {
Class<? extends InvocationHandler> handler = annotations.get(methodAnnotion);
String propertyName = BeanUtil.getPropertyName(eachMethod.getName() + "_" + handler.getSimpleName());
injectField(propertyName, implementation, handler);
Advice advice = methodAnnotion.getAnnotation(Advice.class);
switch (advice.value()) {
case METHOD:
addMethodBody(implementation, eachMethod, propertyName);
break;
case BEFORE:
CtMethod method = addCallSuper(implementation, eachMethod);
addBeforeBody(implementation, method, propertyName);
try {
implementation.addMethod(method);
} catch (Exception e) {
// ignore
}
}
}
}
}
}
}
private CtMethod addCallSuper(CtClass implementation, CtMethod eachMethod) throws CannotCompileException, NotFoundException {
CtMethod newMethod = null;
try {
newMethod = implementation.getMethod(eachMethod.getName(), null);
} catch (Exception e) {
String body = null;
newMethod = new CtMethod(eachMethod, implementation, null);
if (eachMethod.getMethodInfo().getCodeAttribute() == null) {
body = ";";
} else {
body = line("super." + eachMethod.getName() + "($1)");
}
newMethod.setBody(body);
}
return newMethod;
}
private void addBeforeBody(CtClass implementation, CtMethod newMethod, String propertyName) throws NotFoundException, CannotCompileException {
String body = braces(getInvokingMethodCode(newMethod, getParameterTypes(newMethod.getParameterTypes())) + line(returnCall(newMethod, callInvocationHandler(propertyName, newMethod))));
newMethod.insertBefore(body);
}
private void addMethodBody(CtClass implementation, CtMethod eachMethod, String propertyName) throws CannotCompileException, NotFoundException {
CtMethod newMethod = new CtMethod(eachMethod, implementation, null);
newMethod.setBody(braces(getInvokingMethodCode(eachMethod, getParameterTypes(newMethod.getParameterTypes())) + line(returnCall(eachMethod, callInvocationHandler(propertyName, newMethod)))));
implementation.addMethod(newMethod);
}
private String callInvocationHandler(String propertyName, CtMethod newMethod) throws NotFoundException {
return call("invoke", propertyName, "this", "method", createParameters(newMethod.getParameterTypes().length));
}
public void add(Class<? extends Annotation> annotation, Class<? extends InvocationHandler> handler) {
annotations.put(annotation, handler);
}
}