package com.ikokoon.serenity.instrumentation.dependency;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import com.ikokoon.serenity.Collector;
import com.ikokoon.serenity.instrumentation.VisitorFactory;
import com.ikokoon.toolkit.Toolkit;
/**
* This is the entry point for parsing the byte code and collecting the dependency metrics for the class. This class also collects the Java source for
* the class if it is available.
*
* Dependency metrics consist of the following:<br>
*
* 1) Afferent - the number of packages that rely on this package, i.e. how many times it is referenced by other packages, the number of packages that
* this class affects<br>
* 2) Efferent - the number of packages this package relies on, i.e. the opposite of afferent, the number of classes that this class is effected by<br>
* 3) Abstractness - the ratio of abstract to implementations in a package<br>
* 4) Entropy - package A relies on package B. Then Package C is introduced and relies on A and B increasing the entropy<br>
* 5) Stability - Ce / (Ca + Ce), efferent coupling divided by the afferent coupling plus the efferent coupling<br>
* 6) Distance from main - find the stability distance of the package from the main which is (X=0,Y=1) to (X=1,Y=0) <br>
*
* @author Michael Couck
* @since 18.07.09
* @version 01.00
*/
public class DependencyClassAdapter extends ClassAdapter implements Opcodes {
/** The logger for the class. */
private Logger logger = Logger.getLogger(DependencyClassAdapter.class);
/** The name of the class to collect dependency metrics on. */
private String className;
/** The source for the class if available. */
private ByteArrayOutputStream source;
/**
* Constructor initialises a {@link DependencyClassAdapter} and takes the parent visitor and the name of the class that will be analysed for
* dependency.
*
* @param classVisitor
* the parent visitor for the class
* @param className
* the name of the class to be analysed
* @param source
* the Java source
*/
public DependencyClassAdapter(ClassVisitor classVisitor, String className, ByteArrayOutputStream source) {
super(classVisitor);
this.className = Toolkit.slashToDot(className);
this.source = source;
logger.debug("Class name : " + className + ", source : " + source);
}
/**
* {@inheritDoc}
*/
public void visit(int version, int access, String className, String signature, String superName, String[] interfaces) {
if (logger.isDebugEnabled()) {
logger.debug("visit : " + version + ", " + access + ", " + className + ", " + signature + ", " + superName);
if (interfaces != null) {
logger.debug(Arrays.asList(interfaces).toString());
}
}
String[] normedInterfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
String normedInterface = Toolkit.slashToDot(interfaces[i]);
normedInterfaces[i] = normedInterface;
}
Collector.collectEfferentAndAfferent(Toolkit.slashToDot(className), Toolkit.slashToDot(superName));
Collector.collectEfferentAndAfferent(Toolkit.slashToDot(className), normedInterfaces);
Collector.collectAccess(Toolkit.slashToDot(className), access);
super.visit(version, access, className, signature, superName, interfaces);
}
/**
* {@inheritDoc}
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (logger.isDebugEnabled()) {
logger.debug("visitAnnotation : " + desc + ", " + visible);
}
AnnotationVisitor visitor = super.visitAnnotation(desc, visible);
AnnotationVisitor adapter = VisitorFactory.getAnnotationVisitor(visitor, className, desc);
return adapter;
}
/**
* {@inheritDoc}
*/
public void visitAttribute(Attribute attr) {
if (logger.isDebugEnabled()) {
logger.debug("visitAttribute : " + attr);
}
// Attributes are code added that is not standard, and we are not really interested in it are we?
super.visitAttribute(attr);
}
/**
* {@inheritDoc}
*/
public FieldVisitor visitField(int access, String fieldName, String desc, String signature, Object value) {
if (logger.isDebugEnabled()) {
logger.debug("visitField : " + access + ", " + fieldName + ", " + desc + ", " + signature + ", " + value);
}
FieldVisitor visitor = super.visitField(access, fieldName, desc, signature, value);
FieldVisitor adapter = VisitorFactory.getFieldVisitor(visitor, DependencyFieldAdapter.class, className, desc, signature);
return adapter;
}
/**
* {@inheritDoc}
*/
public void visitInnerClass(String innerName, String outerName, String innerSimpleName, int access) {
if (logger.isDebugEnabled()) {
logger.info("visitInnerClass : inner name : " + innerName + ", outer name : " + outerName + ", inner simple name : " + innerSimpleName);
}
if (outerName != null) {
Collector.collectInnerClass(Toolkit.slashToDot(innerName), Toolkit.slashToDot(outerName));
}
if (innerName != null && !innerName.trim().equals("")) {
Collector.collectAccess(Toolkit.slashToDot(innerName), access);
}
super.visitInnerClass(innerName, outerName, innerSimpleName, access);
}
/**
* {@inheritDoc}
*/
public void visitOuterClass(String outerName, String outerMethodName, String outerMethodDescription) {
if (logger.isDebugEnabled()) {
logger.info("visitOuterClass : class name : " + className + ", owner : " + outerName + ", method name : " + outerMethodName
+ ", description : " + outerMethodDescription);
}
Collector.collectOuterClass(className, Toolkit.slashToDot(outerName), outerMethodName, outerMethodDescription);
super.visitOuterClass(outerName, outerMethodName, outerMethodDescription);
}
/**
* {@inheritDoc}
*/
public MethodVisitor visitMethod(int access, String methodName, String methodDescription, String signature, String[] exceptions) {
if (logger.isDebugEnabled()) {
logger.debug("visitMethod : " + access + ", " + methodName + ", " + methodDescription + ", " + signature);
if (exceptions != null) {
logger.debug(Arrays.asList(exceptions).toString());
}
}
MethodVisitor visitor = super.visitMethod(access, methodName, methodDescription, signature, exceptions);
if (exceptions != null) {
for (String exception : exceptions) {
Collector.collectEfferentAndAfferent(className, Toolkit.slashToDot(exception));
}
}
Collector.collectAccess(className, methodName, methodDescription, access);
MethodVisitor adapter = VisitorFactory.getMethodVisitor(visitor, DependencyMethodAdapter.class, access, className, methodName,
methodDescription);
return adapter;
}
/**
* {@inheritDoc}
*/
public void visitSource(String source, String debug) {
if (logger.isDebugEnabled()) {
logger.debug("visitSource : " + source + ", " + debug);
}
if (this.source != null && this.source.size() > 0) {
Collector.collectSource(className, this.source.toString());
}
super.visitSource(source, debug);
}
/**
* {@inheritDoc}
*/
public void visitEnd() {
logger.debug("visitEnd : ");
super.visitEnd();
}
}