package org.gap.jseed;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.Modifier;
import javassist.NotFoundException;
/**
* This object is responsible for delegating the code injection based on
* class {@code Annotation}s, method {@code Annotation}s or field {@code Annotation}s.
*
* @author gpelcha
* @see ClassInjector
* @see MethodInjector
* @see FieldInjector
*/
class Injector implements Configuration{
private static ClassPool POOL;
private SubClassFactory factory;
private ClassInjector classInjector;
private MethodInjector methodInjector;
private FieldInjector fieldInjector;
static {
try {
POOL = ClassPool.getDefault();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
public Injector() {
factory = new SubClassFactory(POOL);
classInjector = new ClassInjector();
methodInjector = new MethodInjector();
fieldInjector = new FieldInjector();
}
/**
* Injects the abstraction with an implementation that can be instantiated.
* @param <T>
* @param type
* @return
* @throws InjectCodeException unable to inject code.
*/
public <T> Class<? extends T> inject(Class<T> type){
return inject(null, type);
}
/**
*
* @param <T>
* @param annotation specifies the class annotation to be used.
* @param type
* @return
*/
@SuppressWarnings("unchecked")
public <T> Class<? extends T> inject(Class<? extends Annotation> annotation, Class<T> type) {
try {
Class<? extends T> result = null;
CtClass abstraction = null;
try {
abstraction = POOL.get(type.getName());
}
catch (NotFoundException e) {
POOL.insertClassPath(new ClassClassPath(type));
abstraction = POOL.get(type.getName());
}
CtClass implementation = factory.createSubClass(type.getName(), abstraction);
methodInjector.injectBehavior(type, abstraction, implementation);
if (annotation == null) {
classInjector.injectBehavior(type, abstraction, implementation);
} else {
classInjector.injectBehavior(annotation, type, abstraction, implementation);
}
fieldInjector.injectBehavior(type, abstraction, implementation);
implementation.setModifiers(Modifier.PUBLIC);
result = (Class<? extends T>)implementation.toClass();
implementation.prune();
return result;
} catch (Exception e) {
throw new InjectCodeException(e);
}
}
@Override
public void registerClass(Class<? extends Annotation> annotation, Class<? extends InvocationHandler> handler) {
classInjector.add(annotation, handler);
}
public void registerClass(Class<? extends Annotation> annotation, Class<? extends InvocationHandler> handler, Validator validator) {
classInjector.add(annotation, handler);
classInjector.validateWith(annotation, validator);
}
public void registerMethod(Class<? extends Annotation> annotation, Class<? extends InvocationHandler> handler) {
methodInjector.add(annotation, handler);
}
public void registerField(Class<? extends Annotation> annotation, Class<? extends FieldHandler> handler) {
fieldInjector.add(annotation, handler);
}
}