Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * fb-contrib - Auxiliary detectors for Java programs
   * Copyright (C) 2005-2013 Dave Brosius
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 package com.mebigfatguy.fbcontrib.detect;
 
 import java.util.Map;
 import java.util.Set;
 
 import  org.apache.bcel.Repository;
 import  org.apache.bcel.classfile.Code;
 import  org.apache.bcel.classfile.JavaClass;
 import  org.apache.bcel.classfile.LineNumberTable;
 import  org.apache.bcel.classfile.Method;
 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.ba.ClassContext;

finds methods that excessively use methods from another class. This probably means these methods should be defined in that other class.
 
 public class ClassEnvy extends BytecodeScanningDetector
 {
 	private static final String ENVY_PERCENT_PROPERTY = "fb-contrib.ce.percent";
 	private static final Set<StringignorableInterfaces = new HashSet<String>();
 	static {
 		.add("java.io.Serializable");
 		.add("java.lang.Cloneable");
 		.add("java.lang.Comparable");
 	}
 
 	private final BugReporter bugReporter;
 	private OpcodeStack stack;
 	private String packageName;
 	private String clsName;
 	private int thisClsAccessCount;
 	private String methodName;
 	private boolean methodIsStatic;
 	private double envyPercent = 0.90;
 	private int envyMin = 5;

constructs a CE detector given the reporter to report bugs on

Parameters:
bugReporter the sync of bug reports
 
 	public ClassEnvy(final BugReporter bugReporter) {
 		this. = bugReporter;
 		String percent = System.getProperty();
 		if (percent != null) {
 			try {
 				 = Double.parseDouble(percent);
 			} catch (NumberFormatException nfe) {
 				//Stick with original
 			}
 		}
 		Integer min = Integer.getInteger("ENVY_MIN_PROPERTY");
 		if (min != null) {
 			 = min.intValue();
 		}
 	}

overrides the visitor to collect package and class names

Parameters:
classContext the context object that holds the JavaClass being parsed
 
 	public void visitClassContext(final ClassContext classContext) {
 		try {
 			JavaClass cls = classContext.getJavaClass();
 			 = cls.getPackageName();
 			 = cls.getClassName();
 			 = new OpcodeStack();
			super.visitClassContext(classContext);
finally {
			 = null;
		}
	}

overrides the visitor to check whether the method is static

Parameters:
obj the method currently being parsed
	public void visitMethod(final Method obj) {
		 = obj.getName();
		 = (obj.getAccessFlags() & ACC_STATIC) != 0;
	}

overrides the visitor to look for the method that uses another class the most, and if it exceeds the threshold reports it

Parameters:
obj the code that is currently being parsed
	@SuppressWarnings("unchecked")
	public void visitCode(final Code obj) {
		.resetForMethodEntry(this);
		if ("<clinit>".equals()) {
			return;
		}
		super.visitCode(obj);
		if (.size() > 0) {
			Arrays.sort(enviesnew Comparator<Map.Entry<StringSet<Integer>>>()
					{
				public int compare(final Map.Entry<StringSet<Integer>> entry1final Map.Entry<StringSet<Integer>> entry2) {
					return entry2.getValue().size() - entry1.getValue().size();
				}
					});
			Map.Entry<StringSet<Integer>> bestEnvyEntry = envies[0];
			int bestEnvyCount = bestEnvyEntry.getValue().size();
			if (bestEnvyCount < ) {
				return;
			}
			double bestPercent = ((double)bestEnvyCount) / ((double) (bestEnvyCount + ));
			if (bestPercent > ) {
	            String bestEnvy = bestEnvyEntry.getKey();
				if (implementsCommonInterface(bestEnvy)) {
					return;
				}
				.reportBug( new BugInstance( this"CE_CLASS_ENVY", NORMAL_PRIORITY)
				.addClass(this)
				.addMethod(this)
				.addSourceLineRange(this, 0, obj.getCode().length-1)
				.addString(bestEnvy));
			}
		}
	}

overrides the visitor to look for method calls, and populate a class access count map based on the owning class of methods called.

Parameters:
seen the opcode currently being parsed
	public void sawOpcode(final int seen) {
		try {
			.mergeJumps(this);
			if ((seen == INVOKEVIRTUAL)
					||  (seen == INVOKEINTERFACE)
					||  (seen == INVOKESTATIC)
					||  (seen == INVOKESPECIAL)) {
				String calledClass = getClassConstantOperand().replace('/''.');
				if (seen == INVOKEINTERFACE) {
					int parmCount = Type.getArgumentTypes(this.getSigConstantOperand()).length;
					if (!countClassAccess(parmCount)) {
						countClassAccess(calledClass);
					}
else {
					countClassAccess(calledClass);
				}
else if (seen == PUTFIELD) {
else if (seen == GETFIELD) {
else if ((seen == ALOAD_0) && (!)) {
			}
finally {
			.sawOpcode(thisseen);
		}
	}

return whether or not a class implements a common or marker interface

Parameters:
name the class name to check
Returns:
if this class implements a common or marker interface
	private boolean implementsCommonInterface(String name) {
		try {
			JavaClass cls = Repository.lookupClass(name);
			JavaClass[] infs = cls.getAllInterfaces();
			for (JavaClass inf : infs) {
				String infName = inf.getClassName();
				if (.contains(infName)) {
					continue;
				}
				if (infName.startsWith("java.")) {
					return true;
				}
			}
			return false;
catch (ClassNotFoundException cnfe) {
			.reportMissingClass(cnfe);
			return true;
		}
	}

increment the count of class access of the class on the stack

Parameters:
classAtStackIndex the position on the stack of the class in question
Returns:
true if the class is counted
	private boolean countClassAccess(final int classAtStackIndex) {
		String calledClass;
		try {
			if (.getStackDepth() > classAtStackIndex) {
				OpcodeStack.Item itm = .getStackItem(classAtStackIndex);
				JavaClass cls = itm.getJavaClass();
				if (cls != null) {
					calledClass = cls.getClassName();
					countClassAccess(calledClass);
					return true;
				}
			}
catch (ClassNotFoundException cfne) {
			.reportMissingClass(cfne);
		}
		return false;
	}

increment the count of class access of the specified class if it is in a similar package to the caller, and is not general purpose

Parameters:
calledClass the class to check
	private void countClassAccess(final String calledClass) {
		if (calledClass.equals()) {
else {
			String calledPackage = SignatureUtils.getPackageName(calledClass);
			if (SignatureUtils.similarPackages(calledPackage, 2) && !generalPurpose(calledClass)) {
				Set<IntegerlineNumbers = .get(calledClass);
				if (lineNumbers == null) {
					lineNumbers = new HashSet<Integer>();
					addLineNumber(lineNumbers);
					.put(calledClasslineNumbers);
else {
					addLineNumber(lineNumbers);
				}
			}
		}
	}

add the current line number to a set of line numbers

Parameters:
lineNumbers the current set of line numbers
	private void addLineNumber(Set<IntegerlineNumbers) {
		LineNumberTable lnt = getCode().getLineNumberTable();
		if (lnt == null) {
			lineNumbers.add(Integer.valueOf(-lineNumbers.size()));
else {
			int line = lnt.getSourceLine(getPC());
			if (line < 0) {
				lineNumbers.add(Integer.valueOf(lineNumbers.size()));
else {
				lineNumbers.add(Integer.valueOf(line));
			}
		}
	}

checks to see if the specified class is a built in class, or implements a simple interface

Parameters:
className the class in question
Returns:
whether or not the class is general purpose
	private boolean generalPurpose(final String className) {
		if (className.startsWith("java.") || className.startsWith("javax.")) {
			return true;
		}
		try {
			JavaClass cls = Repository.lookupClass(className);
			JavaClass[] infs = cls.getAllInterfaces();
			for (JavaClass inf : infs) {
				String infName = inf.getClassName();
				if ("java.io.Serializable".equals(infName)
						||  "java.lang.Cloneable".equals(infName)
						||  "java.lang.Comparable".equals(infName)
						||  "java.lang.Runnable".equals(infName)) {
					continue;
				}
				if (infName.startsWith("java.lang.")
						||  infName.startsWith("javax.lang.")) {
					return true;
				}
			}
			JavaClass[] sups = cls.getSuperClasses();
			for (JavaClass sup : sups) {
				String supName = sup.getClassName();
				if ("java.lang.Object".equals(supName)
						||  "java.lang.Exception".equals(supName)
						||  "java.lang.RuntimeException".equals(supName)
						||  "java.lang.Throwable".equals(supName)) {
					continue;
				}
				if (supName.startsWith("java.lang.")
						||  supName.startsWith("javax.lang.")) {
					return true;
				}
			}
catch (ClassNotFoundException cfne) {
			.reportMissingClass(cfne);
			return true;
		}
		return false;
	}
New to GrepCode? Check out our FAQ X