MethodAnnotation.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
edu.umd.cs.findbugs |
![]() |
![]() |
FindBugs |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2005, University of Maryland
*
* 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 edu.umd.cs.findbugs;
import java.io.IOException;
import org.apache.bcel.Constants;
import edu.umd.cs.findbugs.ba.SignatureConverter;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.util.ClassName;
import edu.umd.cs.findbugs.visitclass.DismantleBytecode;
import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
import edu.umd.cs.findbugs.xml.XMLAttributeList;
import edu.umd.cs.findbugs.xml.XMLOutput;
/**
* A BugAnnotation specifying a particular method in a particular class.
* A MethodAnnotation may (optionally) have a SourceLineAnnotation directly
* embedded inside it to indicate the range of source lines where the
* method is defined.
*
* @author David Hovemeyer
* @see BugAnnotation
*/
public class MethodAnnotation extends PackageMemberAnnotation {
private static final long serialVersionUID = 1L;
private static final boolean UGLY_METHODS = SystemProperties.getBoolean("ma.ugly");
private static final String DEFAULT_ROLE = "METHOD_DEFAULT";
private String methodName;
private String methodSig;
private String fullMethod;
private boolean isStatic;
public static final String METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL = "METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL";
public static final String METHOD_DANGEROUS_TARGET = "METHOD_DANGEROUS_TARGET";
public static final String METHOD_SAFE_TARGET = "METHOD_SAFE_TARGET";
/**
* Constructor.
*
* @param className the name of the class containing the method
* @param methodName the name of the method
* @param methodSig the Java type signature of the method
* @param isStatic true if the method is static, false if not
*/
public MethodAnnotation(String className, String methodName, String methodSig, boolean isStatic) {
super(className, DEFAULT_ROLE);
this.methodName = methodName;
if (methodSig.indexOf(".") >= 0) {
assert false : "signatures should not be dotted: " + methodSig;
methodSig = methodSig.replace('.','/');
}
this.methodSig = methodSig;
this.isStatic = isStatic;
fullMethod = null;
sourceLines = null;
}
/**
* Factory method to create a MethodAnnotation from the method the
* given visitor is currently visiting.
*
* @param visitor the BetterVisitor currently visiting the method
*/
public static MethodAnnotation fromVisitedMethod(PreorderVisitor visitor) {
String className = visitor.getDottedClassName();
MethodAnnotation result = new MethodAnnotation(
className,
visitor.getMethodName(),
visitor.getMethodSig(),
visitor.getMethod().isStatic());
// Try to find the source lines for the method
SourceLineAnnotation srcLines = SourceLineAnnotation.fromVisitedMethod(visitor);
result.setSourceLines(srcLines);
return result;
}
/**
* Factory method to create a MethodAnnotation from a method
* called by the instruction the given visitor is currently visiting.
*
* @param visitor the visitor
* @return the MethodAnnotation representing the called method
*/
public static MethodAnnotation fromCalledMethod(DismantleBytecode visitor) {
String className = visitor.getDottedClassConstantOperand();
String methodName = visitor.getNameConstantOperand();
String methodSig = visitor.getSigConstantOperand();
return fromCalledMethod(className, methodName, methodSig,
visitor.getOpcode() == Constants.INVOKESTATIC);
}
/**
* Factory method to create the MethodAnnotation from
* the classname, method name, signature, etc.
* The method tries to look up source line information for
* the method.
*
* @param className name of the class containing the method
* @param methodName name of the method
* @param methodSig signature of the method
* @param accessFlags the access flags of the method
* @return the MethodAnnotation
*/
public static MethodAnnotation fromForeignMethod(
String className, String methodName, String methodSig, int accessFlags) {
// FIXME: would be nice to do this without using BCEL
className = ClassName.toDottedClassName(className);
// Create MethodAnnotation.
// It won't have source lines yet.
MethodAnnotation methodAnnotation =
new MethodAnnotation(className, methodName, methodSig, (accessFlags & Constants.ACC_STATIC) != 0);
SourceLineAnnotation sourceLines = SourceLineAnnotation.getSourceAnnotationForMethod(
className, methodName, methodSig);
methodAnnotation.setSourceLines(sourceLines);
return methodAnnotation;
}
/**
* Factory method to create the MethodAnnotation from
* the classname, method name, signature, etc.
* The method tries to look up source line information for
* the method.
*
* @param className name of the class containing the method
* @param methodName name of the method
* @param methodSig signature of the method
* @param isStatic true if the method is static, false otherwise
* @return the MethodAnnotation
*/
public static MethodAnnotation fromForeignMethod(
String className, String methodName, String methodSig, boolean isStatic) {
// FIXME: would be nice to do this without using BCEL
className = ClassName.toDottedClassName(className);
// Create MethodAnnotation.
// It won't have source lines yet.
MethodAnnotation methodAnnotation =
new MethodAnnotation(className, methodName, methodSig, isStatic);
SourceLineAnnotation sourceLines = SourceLineAnnotation.getSourceAnnotationForMethod(
className, methodName, methodSig);
methodAnnotation.setSourceLines(sourceLines);
return methodAnnotation;
}
/**
* Create a MethodAnnotation from a method that is not
* directly accessible. We will use the repository to
* try to find its class in order to populate the information
* as fully as possible.
*
* @param className class containing called method
* @param methodName name of called method
* @param methodSig signature of called method
* @param isStatic true if called method is static
* @return the MethodAnnotation for the called method
*/
public static MethodAnnotation fromCalledMethod(
String className, String methodName, String methodSig, boolean isStatic) {
MethodAnnotation methodAnnotation =
fromForeignMethod(className, methodName, methodSig, isStatic);
methodAnnotation.setDescription("METHOD_CALLED");
return methodAnnotation;
}
/**
* Create a MethodAnnotation from an XMethod.
*
* @param xmethod the XMethod
* @return the MethodAnnotation
*/
public static MethodAnnotation fromXMethod(XMethod xmethod) {
return fromForeignMethod(
xmethod.getClassName(),
xmethod.getName(),
xmethod.getSignature(),
xmethod.isStatic());
}
/**
* Create a MethodAnnotation from a MethodDescriptor.
*
* @param methodDescriptor the MethodDescriptor
* @return the MethodAnnotation
*/
public static BugAnnotation fromMethodDescriptor(MethodDescriptor methodDescriptor) {
return fromForeignMethod(
methodDescriptor.getSlashedClassName(),
methodDescriptor.getName(),
methodDescriptor.getSignature(),
methodDescriptor.isStatic());
}
/**
* Get the method name.
*/
public String getMethodName() {
return methodName;
}
public String getJavaSourceMethodName() {
if (methodName.equals("<clinit>")) return "<static initializer>";
if (methodName.equals("<init>")) {
String result = getClassName();
int pos = Math.max(result.lastIndexOf('$'),result.lastIndexOf('.'));
return className.substring(pos+1);
}
return methodName;
}
/**
* Get the method type signature.
*/
public String getMethodSignature() {
return methodSig;
}
/**
* Return whether or not the method is static.
*
* @return true if the method is static, false otherwise
*/
public boolean isStatic() {
return isStatic;
}
/**
* Convert to an XMethod.
*
* @return an XMethod specifying the same method as this MethodAnnotation
*/
public XMethod toXMethod() {
return XFactory.createXMethod(className, methodName, methodSig, isStatic);
}
public void accept(BugAnnotationVisitor visitor) {
visitor.visitMethodAnnotation(this);
}
@Override
protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
if (key.equals(""))
return UGLY_METHODS ? getUglyMethod() : getFullMethod(primaryClass);
else if (key.equals("givenClass")) {
if (methodName.equals("<init>")) {
return "new " + shorten(primaryClass.getPackageName(), className) + getSignatureInClass(primaryClass);
}
if (className.equals(primaryClass.getClassName())) return getNameInClass(primaryClass);
else return shorten(primaryClass.getPackageName(), className) + "." + getNameInClass(primaryClass);
}
else if (key.equals("nameAndSignature")) {
return getNameInClass(primaryClass);
}
else if (key.equals("shortMethod") )
return className + "." + methodName + "(...)";
else if (key.equals("hash")){
String tmp= getNameInClass(false, true, true);
return className + "." + tmp;
}
else if (key.equals("returnType")) {
int i = methodSig.indexOf(')');
String returnType = methodSig.substring(i+1);
String pkgName = primaryClass == null ? "" : primaryClass.getPackageName();
SignatureConverter converter = new SignatureConverter(returnType);
return shorten(pkgName, converter.parseNext());
} else
throw new IllegalArgumentException("unknown key " + key);
}
/**
* Get the "full" method name.
* This is a format which looks sort of like a method signature
* that would appear in Java source code.
* @param primaryClass TODO
*/
public String getNameInClass(ClassAnnotation primaryClass) {
return getNameInClass(true, false, false, false);
}
public String getSignatureInClass(ClassAnnotation primaryClass) {
return getNameInClass(true, false, false, true);
}
public String getNameInClass(boolean shortenPackages, boolean useJVMMethodName, boolean hash) {
return getNameInClass(shortenPackages, useJVMMethodName, hash, false);
}
/**
* Get the "full" method name.
* This is a format which looks sort of like a method signature
* that would appear in Java source code.
*
* note: If shortenPackeges==true, this will return the same
* value as getNameInClass(), except that method caches the
* result and this one does not. Calling this one may be slow.
*
* @param shortenPackages whether to shorten package names
* if they are in java or in the same package as this method.
* @param useJVMMethodName TODO
* @param hash TODO
*/
public String getNameInClass(boolean shortenPackages, boolean useJVMMethodName, boolean hash, boolean omitMethodName) {
// Convert to "nice" representation
StringBuilder result = new StringBuilder();
if (!omitMethodName) {
if (useJVMMethodName)
result.append(getMethodName());
else result.append(getJavaSourceMethodName());
}
result.append('(');
// append args
SignatureConverter converter = new SignatureConverter(methodSig);
if (converter.getFirst() != '(')
throw new IllegalStateException("bad method signature " + methodSig);
converter.skip();
boolean needsComma = false;
while (converter.getFirst() != ')') {
if (needsComma)
if (hash) result.append(",");
else result.append(", ");
if (shortenPackages)
result.append(removePackageName(converter.parseNext()));
else
result.append(converter.parseNext());
needsComma = true;
}
converter.skip();
result.append(')');
return result.toString();
}
/**
* Get the "full" method name.
* This is a format which looks sort of like a method signature
* that would appear in Java source code.
* @param primaryClass TODO
*/
public String getFullMethod(ClassAnnotation primaryClass) {
if (fullMethod == null) {
if (methodName.equals("<init>"))
fullMethod = "new " + stripJavaLang(className) + getSignatureInClass(primaryClass);
else fullMethod = stripJavaLang(className) + "." + getNameInClass(primaryClass);
}
return fullMethod;
}
public String stripJavaLang(@DottedClassName String className) {
if (className.startsWith("java.lang.")) return className.substring(10);
return className;
}
private String getUglyMethod() {
return className + "." + methodName + " : " + methodSig.replace('/', '.');
}
@Override
public int hashCode() {
return className.hashCode() + methodName.hashCode() + methodSig.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof MethodAnnotation))
return false;
MethodAnnotation other = (MethodAnnotation) o;
return className.equals(other.className)
&& methodName.equals(other.methodName)
&& methodSig.equals(other.methodSig);
}
public int compareTo(BugAnnotation o) {
if (!(o instanceof MethodAnnotation)) // BugAnnotations must be Comparable with any type of BugAnnotation
return this.getClass().getName().compareTo(o.getClass().getName());
MethodAnnotation other = (MethodAnnotation) o;
int cmp;
cmp = className.compareTo(other.className);
if (cmp != 0)
return cmp;
cmp = methodName.compareTo(other.methodName);
if (cmp != 0)
return cmp;
return methodSig.compareTo(other.methodSig);
}
/* ----------------------------------------------------------------------
* XML Conversion support
* ---------------------------------------------------------------------- */
private static final String ELEMENT_NAME = "Method";
public void writeXML(XMLOutput xmlOutput) throws IOException {
}
public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
XMLAttributeList attributeList = new XMLAttributeList()
.addAttribute("classname", getClassName())
.addAttribute("name", getMethodName())
.addAttribute("signature", getMethodSignature())
.addAttribute("isStatic", String.valueOf(isStatic()));
if (isPrimary) attributeList.addAttribute("primary", "true");
String role = getDescription();
if (!role.equals(DEFAULT_ROLE))
attributeList.addAttribute("role", role);
if (sourceLines == null && !addMessages) {
xmlOutput.openCloseTag(ELEMENT_NAME, attributeList);
} else {
xmlOutput.openTag(ELEMENT_NAME, attributeList);
if (sourceLines != null) {
sourceLines.writeXML(xmlOutput);
}
if (addMessages) {
xmlOutput.openTag(MESSAGE_TAG);
xmlOutput.writeText(this.toString());
xmlOutput.closeTag(MESSAGE_TAG);
}
xmlOutput.closeTag(ELEMENT_NAME);
}
}
@Override
public boolean isSignificant() {
String role = getDescription();
if (METHOD_DANGEROUS_TARGET.equals(role)
|| METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL.equals(role)
|| METHOD_SAFE_TARGET.equals(role)) return false;
return true;
}
}
// vim:ts=4
The table below shows all metrics for MethodAnnotation.java.



