package core.framework.impl.code;
import core.framework.api.util.Exceptions;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author neo
*/
public class DynamicInstanceBuilder<T> {
private static final AtomicInteger INDEX = new AtomicInteger();
private final Logger logger = LoggerFactory.getLogger(DynamicInstanceBuilder.class);
private final CtClass classBuilder;
private final ClassPool classPool;
private Class<?>[] constructorParamClasses;
public DynamicInstanceBuilder(Class<?> interfaceClass, String className) {
if (!interfaceClass.isInterface())
throw Exceptions.error("interface class must be interface, interfaceClass={}", interfaceClass);
classPool = ClassPool.getDefault();
classBuilder = classPool.makeClass(className + "$" + (INDEX.getAndIncrement()));
try {
classBuilder.addInterface(classPool.get(interfaceClass.getCanonicalName()));
CtConstructor constructor = new CtConstructor(null, classBuilder);
constructor.setBody(";");
classBuilder.addConstructor(constructor);
} catch (NotFoundException | CannotCompileException e) {
throw new CodeCompileException(e);
}
}
public void constructor(Class<?>[] constructorParamClasses, String body) {
if (this.constructorParamClasses != null)
throw new Error("dynamic class must have no more than one custom constructor");
try {
this.constructorParamClasses = constructorParamClasses;
CtClass[] params = new CtClass[constructorParamClasses.length];
for (int i = 0; i < constructorParamClasses.length; i++) {
Class<?> paramClass = constructorParamClasses[i];
params[i] = classPool.getCtClass(paramClass.getCanonicalName());
}
CtConstructor constructor = new CtConstructor(params, classBuilder);
constructor.setBody(body);
classBuilder.addConstructor(constructor);
} catch (CannotCompileException | NotFoundException e) {
throw new CodeCompileException(e);
}
}
public void addMethod(String method) {
try {
classBuilder.addMethod(CtMethod.make(method, classBuilder));
} catch (CannotCompileException e) {
logger.error("method failed to compile:\n{}", method);
throw new CodeCompileException(e);
}
}
public void addField(String field) {
try {
classBuilder.addField(CtField.make(field, classBuilder));
} catch (CannotCompileException e) {
logger.error("field failed to compile:\n{}", field);
throw new CodeCompileException(e);
}
}
public T build(Object... constructorParams) {
try {
@SuppressWarnings("unchecked")
Class<T> targetClass = classBuilder.toClass();
classBuilder.detach();
return targetClass.getDeclaredConstructor(constructorParamClasses).newInstance(constructorParams);
} catch (CannotCompileException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new CodeCompileException(e);
}
}
}