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;
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 final String fieldName;
private final String fieldSig;
private String fieldSourceSig;
private final boolean isStatic;
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;
}
public FieldAnnotation(@DottedClassName String className, String fieldName, String fieldSig, int accessFlags) {
this(className, fieldName, fieldSig, (accessFlags & Constants.ACC_STATIC) != 0);
}
public static FieldAnnotation fromVisitedField(PreorderVisitor visitor) {
return new FieldAnnotation(visitor.getDottedClassName(), visitor.getFieldName(), visitor.getFieldSig(),
visitor.getFieldIsStatic());
}
public static FieldAnnotation fromReferencedField(DismantleBytecode visitor) {
String className = visitor.getDottedClassConstantOperand();
return new FieldAnnotation(className, visitor.getNameConstantOperand(), visitor.getSigConstantOperand(),
visitor.getRefFieldIsStatic());
}
public static FieldAnnotation fromBCELField(@DottedClassName String className, Field field) {
return new FieldAnnotation(className, field.getName(), field.getSignature(), field.isStatic());
}
public static FieldAnnotation fromBCELField(JavaClass jClass, Field field) {
return new FieldAnnotation(jClass.getClassName(), field.getName(), field.getSignature(), field.isStatic());
}
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);
}
public String getFieldName() {
return fieldName;
}
public String getFieldSignature() {
return fieldSig;
}
public boolean isStatic() {
return isStatic;
}
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;
}
}
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;
}
}
@Override
public void accept(BugAnnotationVisitor visitor) {
visitor.visitFieldAnnotation(this);
}
@Override
protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
if ("".equals(key) || "hash".equals(key)) {
return className + "." + fieldName;
} else if ("givenClass".equals(key)) {
String primaryClassName = primaryClass.getClassName();
if (className.equals(primaryClassName)) {
return getNameInClass(primaryClass);
} else {
return shorten(primaryClass.getPackageName(), className) + "." + fieldName;
}
} else if ("name".equals(key)) {
return fieldName;
} else if ("fullField".equals(key)) {
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);
}
}
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;
}
@Override
public int compareTo(BugAnnotation o) {
if (!(o instanceof FieldAnnotation)) {
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);
}
@Override
public SourceLineAnnotation getSourceLines() {
if (sourceLines == null) {
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;
}
private static final String ELEMENT_NAME = "Field";
@Override
public void writeXML(XMLOutput xmlOutput) throws IOException {
writeXML(xmlOutput, false, false);
}
@Override
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 (!DEFAULT_ROLE.equals(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);
}
}