Update of /cvsroot/fb-contrib/fb-contrib/src/com/mebigfatguy/fbcontrib/detect
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8800/src/com/mebigfatguy/fbcontrib/detect
Added Files:
ClassEnvy.java
Log Message:
Initial Checkin: Class Envy Detector
--- NEW FILE: ClassEnvy.java ---
package com.mebigfatguy.fbcontrib.detect;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.Type;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.StatelessDetector;
import edu.umd.cs.findbugs.ba.ClassContext;
public class ClassEnvy extends BytecodeScanningDetector implements StatelessDetector
{
private static final String ENVY_PERCENT_PROPERTY = "fb-contrib.ce.percentt";
private BugReporter bugReporter;
private OpcodeStack stack;
private String packageName;
private String clsName;
private Map<String, Integer> clsMethodCount;
private int thisClsMethodCount;
private double envyPercent = 0.80;
public ClassEnvy(BugReporter bugReporter) {
this.bugReporter = bugReporter;
stack = new OpcodeStack();
String percent = System.getProperty(ENVY_PERCENT_PROPERTY);
if (percent != null) {
try {
envyPercent = Double.valueOf(percent);
} catch (NumberFormatException nfe) {
//Stick with original
}
}
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void visitClassContext(ClassContext classContext) {
JavaClass cls = classContext.getJavaClass();
packageName = cls.getPackageName();
clsName = cls.getClassName();
super.visitClassContext(classContext);
}
public void visitCode(Code obj) {
stack.resetForMethodEntry(this);
clsMethodCount = new HashMap<String, Integer>();
thisClsMethodCount = 0;
super.visitCode(obj);
String bestEnvy = null;
double bestPercent = -1.0;
Iterator<Map.Entry<String,Integer>> it = clsMethodCount.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Integer> entry = it.next();
Integer mc = entry.getValue();
if (mc.intValue() < 3)
continue;
double percent = ((double)mc.intValue()) / ((double)(mc.intValue() + thisClsMethodCount));
if (percent > bestPercent) {
bestPercent = percent;
bestEnvy = entry.getKey();
}
}
if (bestPercent > envyPercent) {
bugReporter.reportBug( new BugInstance( this, "CE_CLASS_ENVY", NORMAL_PRIORITY)
.addClass(this)
.addMethod(this)
.addSourceLine(this, 0)
.addString(bestEnvy));
}
}
public void sawOpcode(int seen) {
try {
if ((seen == INVOKEVIRTUAL)
|| (seen == INVOKEINTERFACE)
|| (seen == INVOKESTATIC)) {
String calledClass = getClassConstantOperand().replace('/', '.');
if (calledClass.startsWith("java.")
|| calledClass.startsWith("javax."))
return;
if (seen == INVOKEINTERFACE) {
int parmCount = Type.getArgumentTypes(this.getMethodSig()).length;
try {
if (stack.getStackDepth() > parmCount) {
OpcodeStack.Item itm = stack.getStackItem(parmCount);
JavaClass jcls = itm.getJavaClass();
if (jcls != null)
calledClass = itm.getJavaClass().getClassName();
}
} catch (ClassNotFoundException cnfe) {
bugReporter.reportMissingClass(cnfe);
}
}
String calledPackage = getPackageName(calledClass);
if (calledClass.equals(clsName))
thisClsMethodCount++;
else {
if (similarPackages(calledPackage, packageName, 2)) {
Integer cnt = clsMethodCount.get(calledClass);
if (cnt == null) {
cnt = new Integer(1);
clsMethodCount.put(calledClass, cnt);
} else {
clsMethodCount.put(calledClass, new Integer(cnt.intValue() + 1));
}
}
}
}
} finally {
stack.sawOpcode(this, seen);
}
}
private String getPackageName(String className) {
int dotPos = className.lastIndexOf(".");
if (dotPos < 0)
return "";
return className.substring(0, dotPos);
}
private boolean similarPackages(String packName1, String packName2, int depth) {
if (depth == 0)
return true;
int dot1 = packName1.indexOf(".");
int dot2 = packName2.indexOf(".");
if (dot1 < 0) {
if (dot2 < 0)
return true;
return false;
} else if (dot2 < 0)
return false;
String s1 = packName1.substring(0, dot1);
String s2 = packName2.substring(0, dot2);
if (!s1.equals(s2))
return false;
return similarPackages(packName1.substring(dot1+1), packName2.substring(dot2+1), depth-1);
}
}
|