package com.josketres.builderator;
import org.objectweb.asm.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
class ConstructorAnalyser {
public ConstructorSignature getSignature(final Class<?> targetClass) throws IOException {
ClassLoader classLoader = targetClass.getClassLoader();
Type type = Type.getType(targetClass);
String url = type.getInternalName() + ".class";
InputStream classFileInputStream = classLoader.getResourceAsStream(url);
if (classFileInputStream == null) {
throw new IllegalArgumentException("The constructor's class loader cannot " +
"find the bytecode that defined the constructor's class (URL: " + url + ")");
}
final ConstructorSignature ctrSignature = new ConstructorSignature();
try {
ClassReader classReader = new ClassReader(classFileInputStream);
classReader.accept(new ConstructorVisitorDelegate(new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
if (!"this".equals(name)) {
Constructor firstConstructor = targetClass.getDeclaredConstructors()[0];
Class<?> argumentType = extractArgumentType(firstConstructor, desc);
ctrSignature.addArgument(desc, name, signature, argumentType);
}
}
private Class<?> extractArgumentType(final Constructor<?> constructor, String desc) {
Class<?> argumentType = null;
for (Class<?> type : constructor.getParameterTypes()) {
String typeDesc = Type.getDescriptor(type);
if (typeDesc.equals(desc)) {
argumentType = type;
}
}
return argumentType;
}
}), 0);
} finally {
classFileInputStream.close();
}
return ctrSignature;
}
private static class ConstructorVisitorDelegate extends ClassVisitor {
private final MethodVisitor constructorMethodVisitor;
public ConstructorVisitorDelegate(MethodVisitor constructorMethodVisitor) {
super(Opcodes.ASM4);
this.constructorMethodVisitor = constructorMethodVisitor;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if ("<init>".equals(name)) {
return constructorMethodVisitor;
} else {
return null;
}
}
}
}