package com.ikokoon.serenity.instrumentation;
import java.io.ByteArrayOutputStream;
import org.apache.log4j.Logger;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import com.ikokoon.serenity.instrumentation.dependency.DependencyAnnotationAdapter;
import com.ikokoon.serenity.instrumentation.dependency.DependencySignatureAdapter;
import com.ikokoon.toolkit.ObjectFactory;
/**
* This class instantiates visitors for classes, methods, field, signature and annotations.
*
* @author Michael Couck
* @since 09.12.09
* @version 01.00
*/
public class VisitorFactory {
private static Logger LOGGER = Logger.getLogger(VisitorFactory.class);
/**
* Instantiates a chain of class visitors. Each visitor can modify or add code to the class as it is parsed and the writer will output the new
* class byte code.
*
* @param classAdapterClasses
* the class visitor classes
* @param className
* the name of the class to be visited
* @param classBytes
* the byte array of the byte code
* @param source
* the output stream of the source code for the class
* @return the class visitor/writer
*/
public static ClassVisitor getClassVisitor(Class<ClassVisitor>[] classAdapterClasses, String className, byte[] classBytes,
ByteArrayOutputStream source) {
ClassReader reader = new ClassReader(classBytes);
// 'true' for ASM 2.2, 'ClassWriter.COMPUTE_MAXS' for ASM 3++
ClassWriter writer = new ClassWriter(reader, true);
ClassVisitor visitor = writer;
for (Class<ClassVisitor> klass : classAdapterClasses) {
Object[] parameters = new Object[] { visitor, className, classBytes, source };
visitor = ObjectFactory.getObject(klass, parameters);
LOGGER.debug("Adding class visitor : " + visitor);
}
// 'false' for ASM 2.2, '0' for ASM 3++
reader.accept(visitor, false);
return writer;
}
/**
* This method constructs a method visitor chain based on the class of the method adapter passed as a parameter.
*
* @param visitor
* the parent method visitor
* @param klass
* the class of the method visitor to initialise
* @param className
* the name of the class that the method visitor will visit
* @param methodName
* the name of the method
* @param methodDescription
* the description or signature of the method in byte code format
* @return the method visitor
*/
public static MethodVisitor getMethodVisitor(MethodVisitor visitor, Class<?> klass, int access, String className, String methodName,
String methodDescription) {
Object[] parameters = new Object[] { visitor, access, className, methodName, methodDescription };
MethodVisitor adapter = (MethodVisitor) ObjectFactory.getObject(klass, parameters);
return adapter;
}
/**
* This method constructs a field visitor chain that will visit the byte code for a field in a class.
*
* @param visitor
* the parent field visitor
* @param klass
* the field visitor class type
* @param className
* the name of the class the field is in
* @param description
* the description of the field in byte code format
* @param signature
* the signature of the field in byte code
* @return the field visitor
*/
public static FieldVisitor getFieldVisitor(FieldVisitor visitor, Class<?> klass, String className, String description, String signature) {
Object[] parameters = new Object[] { visitor, className, description, signature };
FieldVisitor adapter = (FieldVisitor) ObjectFactory.getObject(klass, parameters);
return adapter;
}
/**
* This method constructs a signature visitor that will visit a signature for something, could be an annotation or a method.
*
* @param className
* the name of the class the signature is in
* @param signature
* the signature in byte code to visit
* @return the signature to be visited
*/
public static SignatureVisitor getSignatureVisitor(String className, String signature) {
SignatureReader reader = new SignatureReader(signature);
SignatureVisitor adapter = new DependencySignatureAdapter(className);
reader.accept(adapter);
return adapter;
}
/**
* This method constructs an annotation visitor to visit annotations.
*
* @param visitor
* the parent annotation visitor
* @param className
* the name of the class with the annotation
* @param description
* the description or signature of the annotation
* @return the annotation visitor
*/
public static AnnotationVisitor getAnnotationVisitor(AnnotationVisitor visitor, String className, String description) {
AnnotationVisitor adapter = new DependencyAnnotationAdapter(visitor, className, description);
return adapter;
}
}