001: /*
002: * FindBugs - Find bugs in Java programs
003: * Copyright (C) 2003-2005, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs;
021:
022: import java.io.IOException;
023:
024: import org.apache.bcel.Constants;
025: import org.apache.bcel.classfile.Field;
026: import org.apache.bcel.generic.ConstantPoolGen;
027: import org.apache.bcel.generic.FieldInstruction;
028: import org.apache.bcel.generic.GETFIELD;
029: import org.apache.bcel.generic.GETSTATIC;
030: import org.apache.bcel.generic.Instruction;
031: import org.apache.bcel.generic.PUTFIELD;
032: import org.apache.bcel.generic.PUTSTATIC;
033:
034: import edu.umd.cs.findbugs.ba.AnalysisContext;
035: import edu.umd.cs.findbugs.ba.SignatureConverter;
036: import edu.umd.cs.findbugs.ba.SourceInfoMap;
037: import edu.umd.cs.findbugs.ba.XField;
038: import edu.umd.cs.findbugs.classfile.FieldDescriptor;
039: import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
040: import edu.umd.cs.findbugs.visitclass.DismantleBytecode;
041: import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
042: import edu.umd.cs.findbugs.xml.XMLAttributeList;
043: import edu.umd.cs.findbugs.xml.XMLOutput;
044:
045: /**
046: * A BugAnnotation specifying a particular field in particular class.
047: *
048: * @author David Hovemeyer
049: * @see BugAnnotation
050: */
051: public class FieldAnnotation extends PackageMemberAnnotation {
052: private static final long serialVersionUID = 1L;
053:
054: private static final String DEFAULT_ROLE = "FIELD_DEFAULT";
055:
056: private String fieldName;
057: private String fieldSig;
058: private boolean isStatic;
059:
060: /**
061: * Constructor.
062: *
063: * @param className the name of the class containing the field
064: * @param fieldName the name of the field
065: * @param fieldSig the type signature of the field
066: */
067: public FieldAnnotation(@DottedClassName
068: String className, String fieldName, String fieldSig,
069: boolean isStatic) {
070: super (className, DEFAULT_ROLE);
071: if (fieldSig.indexOf(".") >= 0) {
072: assert false : "signatures should not be dotted: "
073: + fieldSig;
074: fieldSig = fieldSig.replace('.', '/');
075: }
076: this .fieldName = fieldName;
077: this .fieldSig = fieldSig;
078: this .isStatic = isStatic;
079: }
080:
081: /**
082: * Constructor.
083: *
084: * @param className the name of the class containing the field
085: * @param fieldName the name of the field
086: * @param fieldSig the type signature of the field
087: * @param accessFlags accessFlags for the field
088: */
089: public FieldAnnotation(@DottedClassName
090: String className, String fieldName, String fieldSig, int accessFlags) {
091: this (className, fieldName, fieldSig,
092: (accessFlags & Constants.ACC_STATIC) != 0);
093: }
094:
095: /**
096: * Factory method. Class name, field name, and field signatures are taken from
097: * the given visitor, which is visiting the field.
098: *
099: * @param visitor the visitor which is visiting the field
100: * @return the FieldAnnotation object
101: */
102: public static FieldAnnotation fromVisitedField(
103: PreorderVisitor visitor) {
104: return new FieldAnnotation(visitor.getDottedClassName(),
105: visitor.getFieldName(), visitor.getFieldSig(), visitor
106: .getFieldIsStatic());
107: }
108:
109: /**
110: * Factory method. Class name, field name, and field signatures are taken from
111: * the given visitor, which is visiting a reference to the field
112: * (i.e., a getfield or getstatic instruction).
113: *
114: * @param visitor the visitor which is visiting the field reference
115: * @return the FieldAnnotation object
116: */
117: public static FieldAnnotation fromReferencedField(
118: DismantleBytecode visitor) {
119: String className = visitor.getDottedClassConstantOperand();
120: return new FieldAnnotation(className, visitor
121: .getNameConstantOperand(), visitor
122: .getSigConstantOperand(), visitor.getRefFieldIsStatic());
123: }
124:
125: /**
126: * Factory method. Construct from class name and BCEL Field object.
127: *
128: * @param className the name of the class which defines the field
129: * @param field the BCEL Field object
130: * @return the FieldAnnotation
131: */
132: public static FieldAnnotation fromBCELField(String className,
133: Field field) {
134: return new FieldAnnotation(className, field.getName(), field
135: .getSignature(), field.isStatic());
136: }
137:
138: /**
139: * Factory method. Construct from a FieldDescriptor.
140: *
141: * @param fieldDescriptor the FieldDescriptor
142: * @return the FieldAnnotation
143: */
144: public static FieldAnnotation fromFieldDescriptor(
145: FieldDescriptor fieldDescriptor) {
146: return new FieldAnnotation(fieldDescriptor.getClassDescriptor()
147: .getDottedClassName(), fieldDescriptor.getName(),
148: fieldDescriptor.getSignature(), fieldDescriptor
149: .isStatic());
150: }
151:
152: public static FieldAnnotation fromXField(XField fieldDescriptor) {
153: return new FieldAnnotation(fieldDescriptor.getClassName(),
154: fieldDescriptor.getName(), fieldDescriptor
155: .getSignature(), fieldDescriptor.isStatic());
156: }
157:
158: /**
159: * Get the field name.
160: */
161: public String getFieldName() {
162: return fieldName;
163: }
164:
165: /**
166: * Get the type signature of the field.
167: */
168: public String getFieldSignature() {
169: return fieldSig;
170: }
171:
172: /**
173: * Return whether or not the field is static.
174: */
175: public boolean isStatic() {
176: return isStatic;
177: }
178:
179: /**
180: * Is the given instruction a read of a field?
181: *
182: * @param ins the Instruction to check
183: * @param cpg ConstantPoolGen of the method containing the instruction
184: * @return the Field if the instruction is a read of a field, null otherwise
185: */
186: public static FieldAnnotation isRead(Instruction ins,
187: ConstantPoolGen cpg) {
188: if (ins instanceof GETFIELD || ins instanceof GETSTATIC) {
189: FieldInstruction fins = (FieldInstruction) ins;
190: String className = fins.getClassName(cpg);
191: return new FieldAnnotation(className, fins.getName(cpg),
192: fins.getSignature(cpg), fins instanceof GETSTATIC);
193: } else
194: return null;
195: }
196:
197: /**
198: * Is the instruction a write of a field?
199: *
200: * @param ins the Instruction to check
201: * @param cpg ConstantPoolGen of the method containing the instruction
202: * @return the Field if instruction is a write of a field, null otherwise
203: */
204: public static FieldAnnotation isWrite(Instruction ins,
205: ConstantPoolGen cpg) {
206: if (ins instanceof PUTFIELD || ins instanceof PUTSTATIC) {
207: FieldInstruction fins = (FieldInstruction) ins;
208: String className = fins.getClassName(cpg);
209: return new FieldAnnotation(className, fins.getName(cpg),
210: fins.getSignature(cpg), fins instanceof PUTSTATIC);
211: } else
212: return null;
213: }
214:
215: public void accept(BugAnnotationVisitor visitor) {
216: visitor.visitFieldAnnotation(this );
217: }
218:
219: @Override
220: protected String formatPackageMember(String key,
221: ClassAnnotation primaryClass) {
222: if (key.equals("") || key.equals("hash"))
223: return className + "." + fieldName;
224: else if (key.equals("givenClass")) {
225: if (className.equals(primaryClass.getClassName()))
226: return getNameInClass(primaryClass);
227: else
228: return shorten(primaryClass.getPackageName(), className)
229: + "." + getNameInClass(primaryClass);
230: } else if (key.equals("name"))
231: return fieldName;
232: else if (key.equals("fullField")) {
233: SignatureConverter converter = new SignatureConverter(
234: fieldSig);
235: StringBuffer result = new StringBuffer();
236: if (isStatic)
237: result.append("static ");
238: result.append(converter.parseNext());
239: result.append(' ');
240: result.append(className);
241: result.append('.');
242: result.append(fieldName);
243: return result.toString();
244: } else
245: throw new IllegalArgumentException("unknown key " + key);
246: }
247:
248: /**
249: * @param primaryClass
250: * @return
251: */
252: private String getNameInClass(ClassAnnotation primaryClass) {
253: if (primaryClass == null)
254: return className + "." + fieldName;
255: String givenPackageName = primaryClass.getPackageName();
256: String this PackageName = this .getPackageName();
257: if (this PackageName.equals(givenPackageName))
258: if (this PackageName.length() == 0)
259: return fieldName;
260: else
261: return className
262: .substring(this PackageName.length() + 1)
263: + "." + fieldName;
264: return className + "." + fieldName;
265: }
266:
267: @Override
268: public int hashCode() {
269: return className.hashCode() + fieldName.hashCode()
270: + fieldSig.hashCode();
271: }
272:
273: @Override
274: public boolean equals(Object o) {
275: if (!(o instanceof FieldAnnotation))
276: return false;
277: FieldAnnotation other = (FieldAnnotation) o;
278: return className.equals(other.className)
279: && fieldName.equals(other.fieldName)
280: && fieldSig.equals(other.fieldSig)
281: && isStatic == other.isStatic;
282: }
283:
284: public int compareTo(BugAnnotation o) {
285: if (!(o instanceof FieldAnnotation)) // BugAnnotations must be Comparable with any type of BugAnnotation
286: return this .getClass().getName().compareTo(
287: o.getClass().getName());
288: FieldAnnotation other = (FieldAnnotation) o;
289: int cmp;
290: cmp = className.compareTo(other.className);
291: if (cmp != 0)
292: return cmp;
293: cmp = fieldName.compareTo(other.fieldName);
294: if (cmp != 0)
295: return cmp;
296: return fieldSig.compareTo(other.fieldSig);
297: }
298:
299: /* (non-Javadoc)
300: * @see edu.umd.cs.findbugs.PackageMemberAnnotation#getSourceLines()
301: */
302: @Override
303: public SourceLineAnnotation getSourceLines() {
304: if (sourceLines == null) {
305: // Create source line annotation for field on demand
306: AnalysisContext currentAnalysisContext = AnalysisContext
307: .currentAnalysisContext();
308: if (currentAnalysisContext == null)
309: sourceLines = new SourceLineAnnotation(className,
310: sourceFileName, -1, -1, -1, -1);
311: else {
312: SourceInfoMap.SourceLineRange fieldLine = currentAnalysisContext
313: .getSourceInfoMap().getFieldLine(className,
314: fieldName);
315: if (fieldLine == null)
316: sourceLines = new SourceLineAnnotation(className,
317: sourceFileName, -1, -1, -1, -1);
318: else
319: sourceLines = new SourceLineAnnotation(className,
320: sourceFileName, fieldLine.getStart(),
321: fieldLine.getEnd(), -1, -1);
322: }
323: }
324: return sourceLines;
325: }
326:
327: /* ----------------------------------------------------------------------
328: * XML Conversion support
329: * ---------------------------------------------------------------------- */
330:
331: private static final String ELEMENT_NAME = "Field";
332:
333: public void writeXML(XMLOutput xmlOutput) throws IOException {
334: writeXML(xmlOutput, false);
335: }
336:
337: public void writeXML(XMLOutput xmlOutput, boolean addMessages)
338: throws IOException {
339: XMLAttributeList attributeList = new XMLAttributeList()
340: .addAttribute("classname", getClassName())
341: .addAttribute("name", getFieldName()).addAttribute(
342: "signature", getFieldSignature()).addAttribute(
343: "isStatic", String.valueOf(isStatic()));
344:
345: String role = getDescription();
346: if (!role.equals(DEFAULT_ROLE))
347: attributeList.addAttribute("role", role);
348:
349: xmlOutput.openTag(ELEMENT_NAME, attributeList);
350: getSourceLines().writeXML(xmlOutput, addMessages);
351: if (addMessages) {
352: xmlOutput.openTag(BugAnnotation.MESSAGE_TAG);
353: xmlOutput.writeText(this .toString());
354: xmlOutput.closeTag(BugAnnotation.MESSAGE_TAG);
355: }
356: xmlOutput.closeTag(ELEMENT_NAME);
357: }
358: }
359:
360: // vim:ts=4
|