[Fb-contrib-commit] fb-contrib/src/com/mebigfatguy/fbcontrib/detect ClassEnvy.java,NONE,1.1
Brought to you by:
dbrosius
From: Dave B. <dbr...@us...> - 2005-09-17 04:42:43
|
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); } } |