ProjectAres/Util/core/src/main/java/tc/oc/evil/JavassistDecoratorGenerator...

88 lines
4.1 KiB
Java

package tc.oc.evil;
import java.util.Set;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import tc.oc.javassist.Javassists;
/**
* This never worked properly, and was abandoned in favor of {@link LibCGDecoratorGenerator}
*/
public class JavassistDecoratorGenerator implements DecoratorGenerator {
private final CtMethod delegateMethod;
private final Set<CtMethod> forbiddenMethods;
public JavassistDecoratorGenerator() {
try {
this.delegateMethod = Javassists.getClass(Decorator.class).getDeclaredMethod("delegate", null);
final CtClass object = Javassists.getClass(Object.class);
this.forbiddenMethods = ImmutableSet.of(
object.getDeclaredMethod("finalize", null),
object.getDeclaredMethod("clone", null)
);
} catch(NotFoundException e) {
throw Throwables.propagate(e);
}
}
@Override
public <T, D extends Decorator<T>> Meta<T, D> implement(Class<T> type, Class<D> decorator) {
try {
// If the decorator is not abstract, there will be no delegating methods to generate,
// so we don't need a subclass at all.
if(!Modifier.isAbstract(decorator.getModifiers())) {
return new Meta<>(type, decorator, decorator);
}
final CtClass originalClass = Javassists.getClass(type);
final ClassPool decoratorPool = Javassists.getPool(decorator.getClassLoader());
final CtClass decoratorClass = decoratorPool.get(decorator.getName());
final CtClass implClass = decoratorPool.makeClass(decorator.getName() + "$Impl", decoratorClass);
Javassists.setAccessModifiers(implClass, decoratorClass.getModifiers());
for(CtMethod decoratorMethod : decoratorClass.getMethods()) {
if(Modifier.isStatic(decoratorMethod.getModifiers())) continue;
if(forbiddenMethods.contains(decoratorMethod)) continue;
final CtMethod originalMethod = Javassists.tryMethod(originalClass, decoratorMethod);
final boolean decoratorAbstract = Javassists.isAbstract(decoratorMethod);
if(decoratorMethod.equals(delegateMethod)) {
// If we find the delegate() method, just check that it's callable
// CtMethod.equals only compares the signature, so this will work
if(decoratorAbstract) {
throw new IllegalStateException("Decorator class must implement method " + delegateMethod.getLongName());
}
} else if(originalMethod != null &&
!Javassists.isFinal(decoratorMethod) &&
(decoratorAbstract || originalMethod.getDeclaringClass().equals(decoratorMethod.getDeclaringClass()))) {
// If the method was found in the original class, and the decorator
// does not override it, delegate to the original.
Javassists.delegateMethod(implClass, decoratorMethod, delegateMethod);
} else if(originalMethod == null && decoratorAbstract) {
// If the method is not in the original class at all, and the
// decorator doesn't implement it, then we can't implement the class
throw new IllegalStateException("No implementation for method " + decoratorMethod.getLongName() +
" found in either the decorator or the decorated class");
}
}
// Inherit all accessible constructors, including their annotations
Javassists.inheritAllConstructors(implClass);
return new Meta<>(type, decorator, implClass.toClass());
} catch(NotFoundException | CannotCompileException e) {
throw Throwables.propagate(e);
}
}
}