KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > MethodAnnotation


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-2005, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs;
21
22 import java.io.IOException JavaDoc;
23
24 import org.apache.bcel.Constants;
25 import org.apache.bcel.classfile.JavaClass;
26
27 import edu.umd.cs.findbugs.ba.AnalysisContext;
28 import edu.umd.cs.findbugs.ba.Hierarchy;
29 import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
30 import edu.umd.cs.findbugs.ba.SignatureConverter;
31 import edu.umd.cs.findbugs.ba.SourceInfoMap;
32 import edu.umd.cs.findbugs.ba.XFactory;
33 import edu.umd.cs.findbugs.ba.XMethod;
34 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
35 import edu.umd.cs.findbugs.util.ClassName;
36 import edu.umd.cs.findbugs.visitclass.DismantleBytecode;
37 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
38 import edu.umd.cs.findbugs.xml.XMLAttributeList;
39 import edu.umd.cs.findbugs.xml.XMLOutput;
40
41 /**
42  * A BugAnnotation specifying a particular method in a particular class.
43  * A MethodAnnotation may (optionally) have a SourceLineAnnotation directly
44  * embedded inside it to indicate the range of source lines where the
45  * method is defined.
46  *
47  * @author David Hovemeyer
48  * @see BugAnnotation
49  */

50 public class MethodAnnotation extends PackageMemberAnnotation {
51     private static final long serialVersionUID = 1L;
52
53     private static final boolean UGLY_METHODS = SystemProperties.getBoolean("ma.ugly");
54
55     private static final String JavaDoc DEFAULT_ROLE = "METHOD_DEFAULT";
56
57     private String JavaDoc methodName;
58     private String JavaDoc methodSig;
59     private String JavaDoc fullMethod;
60     private boolean isStatic;
61     
62
63     /**
64      * Constructor.
65      *
66      * @param className the name of the class containing the method
67      * @param methodName the name of the method
68      * @param methodSig the Java type signature of the method
69      * @param isStatic true if the method is static, false if not
70      */

71     public MethodAnnotation(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSig, boolean isStatic) {
72         super(className, DEFAULT_ROLE);
73         this.methodName = methodName;
74         if (methodSig.indexOf(".") >= 0) {
75             assert false : "signatures should not be dotted: " + methodSig;
76             methodSig = methodSig.replace('.','/');
77         }
78         this.methodSig = methodSig;
79         this.isStatic = isStatic;
80         fullMethod = null;
81         sourceLines = null;
82     }
83
84     /**
85      * Factory method to create a MethodAnnotation from the method the
86      * given visitor is currently visiting.
87      *
88      * @param visitor the BetterVisitor currently visiting the method
89      */

90     public static MethodAnnotation fromVisitedMethod(PreorderVisitor visitor) {
91         String JavaDoc className = visitor.getDottedClassName();
92         MethodAnnotation result = new MethodAnnotation(
93                 className,
94                 visitor.getMethodName(),
95                 visitor.getMethodSig(),
96                 visitor.getMethod().isStatic());
97
98         // Try to find the source lines for the method
99
SourceLineAnnotation srcLines = SourceLineAnnotation.fromVisitedMethod(visitor);
100         result.setSourceLines(srcLines);
101
102         return result;
103     }
104
105     /**
106      * Factory method to create a MethodAnnotation from a method
107      * called by the instruction the given visitor is currently visiting.
108      *
109      * @param visitor the visitor
110      * @return the MethodAnnotation representing the called method
111      */

112     public static MethodAnnotation fromCalledMethod(DismantleBytecode visitor) {
113         String JavaDoc className = visitor.getDottedClassConstantOperand();
114         String JavaDoc methodName = visitor.getNameConstantOperand();
115         String JavaDoc methodSig = visitor.getSigConstantOperand();
116         
117         return fromCalledMethod(className, methodName, methodSig,
118                 visitor.getOpcode() == Constants.INVOKESTATIC);
119     }
120
121     /**
122      * Factory method to create the MethodAnnotation from
123      * the classname, method name, signature, etc.
124      * The method tries to look up source line information for
125      * the method.
126      *
127      * @param className name of the class containing the method
128      * @param methodName name of the method
129      * @param methodSig signature of the method
130      * @param isStatic true if the method is static, false otherwise
131      * @return the MethodAnnotation
132      */

133     public static MethodAnnotation fromForeignMethod(
134             String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSig, boolean isStatic) {
135         
136         // FIXME: would be nice to do this without using BCEL
137

138         className = ClassName.toDottedClassName(className);
139         
140         // Create MethodAnnotation.
141
// It won't have source lines yet.
142
MethodAnnotation methodAnnotation =
143             new MethodAnnotation(className, methodName, methodSig, isStatic);
144         
145         // Try to find source lines by looking up the exact class and method.
146
SourceLineAnnotation sourceLines = null;
147         try {
148             JavaClass targetClass = AnalysisContext.currentAnalysisContext()
149                 .lookupClass(className);
150             JavaClassAndMethod targetMethod = Hierarchy.findMethod(targetClass, methodName, methodSig);
151             if (targetMethod != null) {
152                 sourceLines = SourceLineAnnotation.forEntireMethod(
153                         targetMethod.getJavaClass(), targetMethod.getMethod());
154             }
155         } catch (ClassNotFoundException JavaDoc e) {
156             // Can't find the class
157
}
158         
159         // Try consulting the SourceInfoMap
160
if (sourceLines == null) {
161             SourceInfoMap.SourceLineRange range = AnalysisContext.currentAnalysisContext()
162                 .getSourceInfoMap()
163                 .getMethodLine(className, methodName, methodSig);
164             if (range != null) {
165                 sourceLines = new SourceLineAnnotation(
166                         className,
167                         AnalysisContext.currentAnalysisContext().lookupSourceFile(className),
168                         range.getStart(),
169                         range.getEnd(),
170                         -1,
171                         -1);
172             }
173         }
174         
175         // If we couldn't find the source lines,
176
// create an unknown source line annotation referencing
177
// the class and source file.
178
if (sourceLines == null) {
179             sourceLines = SourceLineAnnotation.createUnknown(className);
180         }
181         
182         methodAnnotation.setSourceLines(sourceLines);
183         
184         return methodAnnotation;
185     }
186     
187     /**
188      * Create a MethodAnnotation from a method that is not
189      * directly accessible. We will use the repository to
190      * try to find its class in order to populate the information
191      * as fully as possible.
192      *
193      * @param className class containing called method
194      * @param methodName name of called method
195      * @param methodSig signature of called method
196      * @param isStatic true if called method is static
197      * @return the MethodAnnotation for the called method
198      */

199     public static MethodAnnotation fromCalledMethod(
200             String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSig, boolean isStatic) {
201         
202         MethodAnnotation methodAnnotation =
203             fromForeignMethod(className, methodName, methodSig, isStatic);
204         methodAnnotation.setDescription("METHOD_CALLED");
205         return methodAnnotation;
206         
207     }
208     
209     /**
210      * Create a MethodAnnotation from an XMethod.
211      *
212      * @param xmethod the XMethod
213      * @return the MethodAnnotation
214      */

215     public static MethodAnnotation fromXMethod(XMethod xmethod) {
216         return fromForeignMethod(
217                 xmethod.getClassName(),
218                 xmethod.getName(),
219                 xmethod.getSignature(),
220                 xmethod.isStatic());
221     }
222
223     /**
224      * Create a MethodAnnotation from a MethodDescriptor.
225      *
226      * @param methodDescriptor the MethodDescriptor
227      * @return the MethodAnnotation
228      */

229     public static BugAnnotation fromMethodDescriptor(MethodDescriptor methodDescriptor) {
230         return fromForeignMethod(
231                 methodDescriptor.getClassName(),
232                 methodDescriptor.getName(),
233                 methodDescriptor.getSignature(),
234                 methodDescriptor.isStatic());
235     }
236
237     /**
238      * Get the method name.
239      */

240     public String JavaDoc getMethodName() {
241         return methodName;
242     }
243
244     public String JavaDoc getJavaSourceMethodName() {
245         if (methodName.equals("<clinit>")) return "<static initializer>";
246         if (methodName.equals("<init>")) {
247             String JavaDoc result = getClassName();
248             int pos = Math.max(result.lastIndexOf('$'),result.lastIndexOf('.'));
249             return className.substring(pos+1);
250         }
251         return methodName;
252     }
253     /**
254      * Get the method type signature.
255      */

256     public String JavaDoc getMethodSignature() {
257         return methodSig;
258     }
259     
260     /**
261      * Return whether or not the method is static.
262      *
263      * @return true if the method is static, false otherwise
264      */

265     public boolean isStatic() {
266         return isStatic;
267     }
268     
269     /**
270      * Convert to an XMethod.
271      *
272      * @return an XMethod specifying the same method as this MethodAnnotation
273      */

274     public XMethod toXMethod() {
275         return XFactory.createXMethod(className, methodName, methodSig, isStatic);
276     }
277
278     
279     public void accept(BugAnnotationVisitor visitor) {
280         visitor.visitMethodAnnotation(this);
281     }
282
283     @Override JavaDoc
284     protected String JavaDoc formatPackageMember(String JavaDoc key, ClassAnnotation primaryClass) {
285         if (key.equals(""))
286             return UGLY_METHODS ? getUglyMethod() : getFullMethod(primaryClass);
287         else if (key.equals("givenClass")) return getNameInClass(primaryClass);
288         else if (key.equals("shortMethod") )
289             return className + "." + methodName + "()";
290         else if (key.equals("hash")){
291                 String JavaDoc tmp= getNameInClass(true, primaryClass, true);
292
293                 return className + "." + tmp;
294         }
295         else if (key.equals("returnType")) {
296             int i = methodSig.indexOf(')');
297             String JavaDoc returnType = methodSig.substring(i+1);
298             String JavaDoc pkgName = primaryClass == null ? "" : primaryClass.getPackageName();
299             SignatureConverter converter = new SignatureConverter(returnType);
300             return shorten(pkgName, converter.parseNext());
301         } else
302             throw new IllegalArgumentException JavaDoc("unknown key " + key);
303     }
304
305     String JavaDoc nameInClass = null;
306     /**
307      * Get the "full" method name.
308      * This is a format which looks sort of like a method signature
309      * that would appear in Java source code.
310      * @param primaryClass TODO
311      */

312     public String JavaDoc getNameInClass(ClassAnnotation primaryClass) {
313         if (nameInClass == null) {
314             nameInClass = getNameInClass(true, primaryClass, false);
315         }
316         return nameInClass;
317     }
318
319     /**
320      * Get the "full" method name.
321      * This is a format which looks sort of like a method signature
322      * that would appear in Java source code.
323      *
324      * note: If shortenPackeges==true, this will return the same
325      * value as getNameInClass(), except that method caches the
326      * result and this one does not. Calling this one may be slow.
327      *
328      * @param shortenPackages whether to shorten package names
329      * if they are in java or in the same package as this method.
330      * @param primaryClass TODO
331      * @param useJVMMethodName TODO
332      */

333     public String JavaDoc getNameInClass(boolean shortenPackages, ClassAnnotation primaryClass, boolean useJVMMethodName) {
334         if (primaryClass == null) shortenPackages = false;
335         // Convert to "nice" representation
336
StringBuffer JavaDoc result = new StringBuffer JavaDoc();
337         if (useJVMMethodName)
338             result.append(getMethodName());
339         else result.append(getJavaSourceMethodName());
340         result.append('(');
341
342         // append args
343
SignatureConverter converter = new SignatureConverter(methodSig);
344         
345         if (converter.getFirst() != '(')
346             throw new IllegalStateException JavaDoc("bad method signature " + methodSig);
347         converter.skip();
348
349         String JavaDoc pkgName = null;
350         if (shortenPackages)
351             pkgName = primaryClass.getPackageName();
352         boolean needsComma = false;
353         while (converter.getFirst() != ')') {
354             if (needsComma)
355                 result.append(',');
356             if (shortenPackages)
357                 result.append(shorten(pkgName, converter.parseNext()));
358             else
359                 result.append(converter.parseNext());
360             needsComma = true;
361         }
362         converter.skip();
363
364         result.append(')');
365         return result.toString();
366     }
367
368     
369     /**
370      * Get the "full" method name.
371      * This is a format which looks sort of like a method signature
372      * that would appear in Java source code.
373      * @param primaryClass TODO
374      */

375     public String JavaDoc getFullMethod(ClassAnnotation primaryClass) {
376         if (fullMethod == null) {
377             fullMethod = className + "." + getNameInClass(primaryClass);
378         }
379
380         return fullMethod;
381     }
382
383     private String JavaDoc getUglyMethod() {
384         return className + "." + methodName + " : " + methodSig.replace('/', '.');
385     }
386
387     @Override JavaDoc
388     public int hashCode() {
389         return className.hashCode() + methodName.hashCode() + methodSig.hashCode();
390     }
391
392     @Override JavaDoc
393     public boolean equals(Object JavaDoc o) {
394         if (!(o instanceof MethodAnnotation))
395             return false;
396         MethodAnnotation other = (MethodAnnotation) o;
397         return className.equals(other.className)
398                 && methodName.equals(other.methodName)
399                 && methodSig.equals(other.methodSig);
400     }
401
402     public int compareTo(BugAnnotation o) {
403         if (!(o instanceof MethodAnnotation)) // BugAnnotations must be Comparable with any type of BugAnnotation
404
return this.getClass().getName().compareTo(o.getClass().getName());
405         MethodAnnotation other = (MethodAnnotation) o;
406         int cmp;
407         cmp = className.compareTo(other.className);
408         if (cmp != 0)
409             return cmp;
410         cmp = methodName.compareTo(other.methodName);
411         if (cmp != 0)
412             return cmp;
413         return methodSig.compareTo(other.methodSig);
414     }
415
416     /* ----------------------------------------------------------------------
417      * XML Conversion support
418      * ---------------------------------------------------------------------- */

419
420     private static final String JavaDoc ELEMENT_NAME = "Method";
421
422     public void writeXML(XMLOutput xmlOutput) throws IOException JavaDoc {
423     }
424
425     public void writeXML(XMLOutput xmlOutput, boolean addMessages) throws IOException JavaDoc {
426         XMLAttributeList attributeList = new XMLAttributeList()
427             .addAttribute("classname", getClassName())
428             .addAttribute("name", getMethodName())
429             .addAttribute("signature", getMethodSignature())
430             .addAttribute("isStatic", String.valueOf(isStatic()));
431         
432         String JavaDoc role = getDescription();
433         if (!role.equals(DEFAULT_ROLE))
434             attributeList.addAttribute("role", role);
435         
436         if (sourceLines == null && !addMessages) {
437             xmlOutput.openCloseTag(ELEMENT_NAME, attributeList);
438         } else {
439             xmlOutput.openTag(ELEMENT_NAME, attributeList);
440             if (sourceLines != null) {
441                 sourceLines.writeXML(xmlOutput);
442             }
443             if (addMessages) {
444                 xmlOutput.openTag(MESSAGE_TAG);
445                 xmlOutput.writeText(this.toString());
446                 xmlOutput.closeTag(MESSAGE_TAG);
447             }
448             xmlOutput.closeTag(ELEMENT_NAME);
449         }
450     }
451 }
452
453 // vim:ts=4
454
Popular Tags