/*
* 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 org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.PUTFIELD;
import org.apache.bcel.generic.PUTSTATIC;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.SignatureConverter;
import edu.umd.cs.findbugs.ba.SourceInfoMap;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.FieldDescriptor;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
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 field in particular class.
*
* @author David Hovemeyer
* @see BugAnnotation
*/
public class FieldAnnotation extends PackageMemberAnnotation {
private static final long serialVersionUID = 1L;
public static final String DEFAULT_ROLE = "FIELD_DEFAULT";
public static final String DID_YOU_MEAN_ROLE = "FIELD_DID_YOU_MEAN";
public static final String VALUE_OF_ROLE = "FIELD_VALUE_OF";
public static final String LOADED_FROM_ROLE = VALUE_OF_ROLE;
public static final String STORED_ROLE = "FIELD_STORED";
public static final String INVOKED_ON_ROLE = "FIELD_INVOKED_ON";
public static final String ARGUMENT_ROLE = "FIELD_ARGUMENT";
private String fieldName;
private String fieldSig;
private String fieldSourceSig;
private boolean isStatic;
/**
* Constructor.
*
* @param className
* the name of the class containing the field
* @param fieldName
* the name of the field
* @param fieldSig
* the type signature of the field
*/
public FieldAnnotation(@DottedClassName String className, String fieldName, String fieldSig, boolean isStatic) {
super(className, DEFAULT_ROLE);
if (fieldSig.indexOf(".") >= 0) {
assert false : "signatures should not be dotted: " + fieldSig;
fieldSig = fieldSig.replace('.', '/');
}
this.fieldName = fieldName;
this.fieldSig = fieldSig;
this.isStatic = isStatic;
}
public FieldAnnotation(@DottedClassName String className, String fieldName, String fieldSig, String fieldSourceSig,
boolean isStatic) {
this(className, fieldName, fieldSig, isStatic);
this.fieldSourceSig = fieldSourceSig;
}
/**
* Constructor.
*
* @param className
* the name of the class containing the field
* @param fieldName
* the name of the field
* @param fieldSig
* the type signature of the field
* @param accessFlags
* accessFlags for the field
*/
public FieldAnnotation(@DottedClassName String className, String fieldName, String fieldSig, int accessFlags) {
this(className, fieldName, fieldSig, (accessFlags & Constants.ACC_STATIC) != 0);
}
/**
* Factory method. Class name, field name, and field signatures are taken
* from the given visitor, which is visiting the field.
*
* @param visitor
* the visitor which is visiting the field
* @return the FieldAnnotation object
*/
public static FieldAnnotation fromVisitedField(PreorderVisitor visitor) {
return new FieldAnnotation(visitor.getDottedClassName(), visitor.getFieldName(), visitor.getFieldSig(),
visitor.getFieldIsStatic());
}
/**
* Factory method. Class name, field name, and field signatures are taken
* from the given visitor, which is visiting a reference to the field (i.e.,
* a getfield or getstatic instruction).
*
* @param visitor
* the visitor which is visiting the field reference
* @return the FieldAnnotation object
*/
public static FieldAnnotation fromReferencedField(DismantleBytecode visitor) {
String className = visitor.getDottedClassConstantOperand();
return new FieldAnnotation(className, visitor.getNameConstantOperand(), visitor.getSigConstantOperand(),
visitor.getRefFieldIsStatic());
}
/**
* Factory method. Construct from class name and BCEL Field object.
*
* @param className
* the name of the class which defines the field
* @param field
* the BCEL Field object
* @return the FieldAnnotation
*/
public static FieldAnnotation fromBCELField(@DottedClassName String className, Field field) {
return new FieldAnnotation(className, field.getName(), field.getSignature(), field.isStatic());
}
/**
* Factory method. Construct from class name and BCEL Field object.
*
* @param jClass
* the class which defines the field
* @param field
* the BCEL Field object
* @return the FieldAnnotation
*/
public static FieldAnnotation fromBCELField(JavaClass jClass, Field field) {
return new FieldAnnotation(jClass.getClassName(), field.getName(), field.getSignature(), field.isStatic());
}
/**
* Factory method. Construct from a FieldDescriptor.
*
* @param fieldDescriptor
* the FieldDescriptor
* @return the FieldAnnotation
*/
public static FieldAnnotation fromFieldDescriptor(FieldDescriptor fieldDescriptor) {
return new FieldAnnotation(fieldDescriptor.getClassDescriptor().getDottedClassName(), fieldDescriptor.getName(),
fieldDescriptor.getSignature(), fieldDescriptor.isStatic());
}
public static FieldAnnotation fromXField(XField fieldDescriptor) {
return new FieldAnnotation(fieldDescriptor.getClassName(), fieldDescriptor.getName(), fieldDescriptor.getSignature(),
fieldDescriptor.getSourceSignature(), fieldDescriptor.isStatic());
}
public XField toXField() {
return XFactory.createXField(className, fieldName, fieldSig, isStatic);
}
public FieldDescriptor toFieldDescriptor() {
return DescriptorFactory.instance().getFieldDescriptor(this);
}
/**
* Get the field name.
*/
public String getFieldName() {
return fieldName;
}
/**
* Get the type signature of the field.
*/
public String getFieldSignature() {
return fieldSig;
}
/**
* Return whether or not the field is static.
*/
public boolean isStatic() {
return isStatic;
}
/**
* Is the given instruction a read of a field?
*
* @param ins
* the Instruction to check
* @param cpg
* ConstantPoolGen of the method containing the instruction
* @return the Field if the instruction is a read of a field, null otherwise
*/
public static FieldAnnotation isRead(Instruction ins, ConstantPoolGen cpg) {
if (ins instanceof GETFIELD || ins instanceof GETSTATIC) {
FieldInstruction fins = (FieldInstruction) ins;
String className = fins.getClassName(cpg);
return new FieldAnnotation(className, fins.getName(cpg), fins.getSignature(cpg), fins instanceof GETSTATIC);
} else
return null;
}
/**
* Is the instruction a write of a field?
*
* @param ins
* the Instruction to check
* @param cpg
* ConstantPoolGen of the method containing the instruction
* @return the Field if instruction is a write of a field, null otherwise
*/
public static FieldAnnotation isWrite(Instruction ins, ConstantPoolGen cpg) {
if (ins instanceof PUTFIELD || ins instanceof PUTSTATIC) {
FieldInstruction fins = (FieldInstruction) ins;
String className = fins.getClassName(cpg);
return new FieldAnnotation(className, fins.getName(cpg), fins.getSignature(cpg), fins instanceof PUTSTATIC);
} else
return null;
}
public void accept(BugAnnotationVisitor visitor) {
visitor.visitFieldAnnotation(this);
}
@Override
protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
if (key.equals("") || key.equals("hash"))
return className + "." + fieldName;
else if (key.equals("givenClass")) {
String primaryClassName = primaryClass.getClassName();
if (className.equals(primaryClassName))
return getNameInClass(primaryClass);
else
return shorten(primaryClass.getPackageName(), className) + "." + fieldName;
} else if (key.equals("name"))
return fieldName;
else if (key.equals("fullField")) {
SignatureConverter converter = new SignatureConverter(fieldSig);
StringBuilder result = new StringBuilder();
if (isStatic)
result.append("static ");
result.append(converter.parseNext());
result.append(' ');
result.append(className);
result.append('.');
result.append(fieldName);
return result.toString();
} else
throw new IllegalArgumentException("unknown key " + key);
}
/**
* @param primaryClass
* @return
*/
private String getNameInClass(ClassAnnotation primaryClass) {
if (primaryClass == null)
return className + "." + fieldName;
String givenPackageName = primaryClass.getPackageName();
String thisPackageName = this.getPackageName();
if (thisPackageName.equals(givenPackageName))
if (thisPackageName.length() == 0)
return fieldName;
else
return className.substring(thisPackageName.length() + 1) + "." + fieldName;
return className + "." + fieldName;
}
@Override
public int hashCode() {
return className.hashCode() + fieldName.hashCode() + fieldSig.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FieldAnnotation))
return false;
FieldAnnotation other = (FieldAnnotation) o;
return className.equals(other.className) && fieldName.equals(other.fieldName) && fieldSig.equals(other.fieldSig)
&& isStatic == other.isStatic;
}
public int compareTo(BugAnnotation o) {
if (!(o instanceof FieldAnnotation)) // BugAnnotations must be
// Comparable with any type of
// BugAnnotation
return this.getClass().getName().compareTo(o.getClass().getName());
FieldAnnotation other = (FieldAnnotation) o;
int cmp;
cmp = className.compareTo(other.className);
if (cmp != 0)
return cmp;
cmp = fieldName.compareTo(other.fieldName);
if (cmp != 0)
return cmp;
return fieldSig.compareTo(other.fieldSig);
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.PackageMemberAnnotation#getSourceLines()
*/
@Override
public SourceLineAnnotation getSourceLines() {
if (sourceLines == null) {
// Create source line annotation for field on demand
AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
if (currentAnalysisContext == null)
sourceLines = new SourceLineAnnotation(className, sourceFileName, -1, -1, -1, -1);
else {
SourceInfoMap.SourceLineRange fieldLine = currentAnalysisContext.getSourceInfoMap().getFieldLine(className,
fieldName);
if (fieldLine == null)
sourceLines = new SourceLineAnnotation(className, sourceFileName, -1, -1, -1, -1);
else
sourceLines = new SourceLineAnnotation(className, sourceFileName, fieldLine.getStart(), fieldLine.getEnd(),
-1, -1);
}
}
return sourceLines;
}
/*
* ----------------------------------------------------------------------
* XML Conversion support
* ----------------------------------------------------------------------
*/
private static final String ELEMENT_NAME = "Field";
public void writeXML(XMLOutput xmlOutput) throws IOException {
writeXML(xmlOutput, false, false);
}
public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
XMLAttributeList attributeList = new XMLAttributeList().addAttribute("classname", getClassName())
.addAttribute("name", getFieldName()).addAttribute("signature", getFieldSignature());
if (fieldSourceSig != null)
attributeList.addAttribute("sourceSignature", fieldSourceSig);
attributeList.addAttribute("isStatic", String.valueOf(isStatic()));
if (isPrimary)
attributeList.addAttribute("primary", "true");
String role = getDescription();
if (!role.equals(DEFAULT_ROLE))
attributeList.addAttribute("role", role);
xmlOutput.openTag(ELEMENT_NAME, attributeList);
getSourceLines().writeXML(xmlOutput, addMessages, false);
if (addMessages) {
xmlOutput.openTag(BugAnnotation.MESSAGE_TAG);
xmlOutput.writeText(this.toString());
xmlOutput.closeTag(BugAnnotation.MESSAGE_TAG);
}
xmlOutput.closeTag(ELEMENT_NAME);
}
}
// vim:ts=4