KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > SourceInfoMap


1 /*
2  * FindBugs - Find Bugs in Java programs
3  * Copyright (C) 2006, David Hovemeyer <daveho@users.sourceforge.net>
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.ba;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.InputStreamReader JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.NoSuchElementException JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30 import java.util.TreeMap JavaDoc;
31 import java.util.regex.Pattern JavaDoc;
32
33 import edu.umd.cs.findbugs.SystemProperties;
34 import edu.umd.cs.findbugs.annotations.CheckForNull;
35 import edu.umd.cs.findbugs.annotations.NonNull;
36
37 /**
38  * Global information about the source code for an application.
39  * Currently, this object contains a map of source line information
40  * for fields and classes (items we don't get line number information
41  * for directly in classfiles), and also source line information
42  * for methods that don't appear directly in classfiles,
43  * such as abstract and native methods.
44  *
45  * @author David Hovemeyer
46  */

47 public class SourceInfoMap {
48     static class FieldDescriptor implements Comparable JavaDoc<FieldDescriptor> {
49         String JavaDoc className;
50         String JavaDoc fieldName;
51         
52         public FieldDescriptor(String JavaDoc className, String JavaDoc fieldName) {
53             this.className = className;
54             this.fieldName = fieldName;
55         }
56         
57         public String JavaDoc toString() {
58             return className +"." + fieldName;
59         }
60         /* (non-Javadoc)
61          * @see java.lang.Comparable#compareTo(T)
62          */

63         public int compareTo(FieldDescriptor o) {
64             int cmp = className.compareTo(o.className);
65             if (cmp != 0)
66                 return cmp;
67             return fieldName.compareTo(o.fieldName);
68         }
69         
70         /* (non-Javadoc)
71          * @see java.lang.Object#hashCode()
72          */

73         @Override JavaDoc
74         public int hashCode() {
75             return 1277 * className.hashCode() +fieldName.hashCode();
76         }
77         
78         /* (non-Javadoc)
79          * @see java.lang.Object#equals(java.lang.Object)
80          */

81         @Override JavaDoc
82         public boolean equals(Object JavaDoc obj) {
83             if (obj == null || obj.getClass() != this.getClass())
84                 return false;
85             FieldDescriptor other = (FieldDescriptor) obj;
86             return className.equals(other.className) && fieldName.equals(other.fieldName);
87         }
88     }
89     
90     static class MethodDescriptor implements Comparable JavaDoc<MethodDescriptor> {
91         private String JavaDoc className;
92         private String JavaDoc methodName;
93         private String JavaDoc methodSignature;
94         
95         public MethodDescriptor(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSignature) {
96             this.className = className;
97             this.methodName = methodName;
98             this.methodSignature = methodSignature;
99         }
100         public String JavaDoc toString() {
101             return className +"." + methodName;
102         }
103         /* (non-Javadoc)
104          * @see java.lang.Comparable#compareTo(T)
105          */

106         public int compareTo(MethodDescriptor o) {
107             int cmp;
108             if ((cmp = className.compareTo(o.className)) != 0)
109                 return cmp;
110             if ((cmp = methodName.compareTo(o.methodName)) != 0)
111                 return cmp;
112             return methodSignature.compareTo(o.methodSignature);
113         }
114         
115         /* (non-Javadoc)
116          * @see java.lang.Object#hashCode()
117          */

118         @Override JavaDoc
119         public int hashCode() {
120             return 1277 * className.hashCode()
121                 + 37 * methodName.hashCode()
122                 + methodSignature.hashCode();
123         }
124         
125         /* (non-Javadoc)
126          * @see java.lang.Object#equals(java.lang.Object)
127          */

128         @Override JavaDoc
129         public boolean equals(Object JavaDoc obj) {
130             if (obj == null || obj.getClass() != this.getClass())
131                 return false;
132             MethodDescriptor other = (MethodDescriptor) obj;
133             return className.equals(other.className)
134                 && methodName.equals(other.methodName)
135                 && methodSignature.equals(other.methodSignature);
136         }
137     }
138     
139     /**
140      * A range of source lines.
141      */

142     public static class SourceLineRange {
143         private final Integer JavaDoc start, end;
144
145         /**
146          * Constructor for a single line.
147          */

148         public SourceLineRange(@NonNull Integer JavaDoc line) {
149             this.start = this.end = line;
150         }
151         
152         /**
153          * Constructor for a range of lines.
154          *
155          * @param start start line in range
156          * @param end end line in range
157          */

158         public SourceLineRange(@NonNull Integer JavaDoc start, @NonNull Integer JavaDoc end) {
159             this.start = start;
160             this.end = end;
161         }
162         
163         /**
164          * @return Returns the start.
165          */

166         public @NonNull Integer JavaDoc getStart() {
167             return start;
168         }
169         
170         /**
171          * @return Returns the end.
172          */

173         public @NonNull Integer JavaDoc getEnd() {
174             return end;
175         }
176         
177         /* (non-Javadoc)
178          * @see java.lang.Object#toString()
179          */

180         @Override JavaDoc
181         public String JavaDoc toString() {
182             return start + (start.equals(end) ? "" : "-" + end);
183         }
184     }
185     
186     private static final boolean DEBUG = SystemProperties.getBoolean("sourceinfo.debug");
187     
188     private Map JavaDoc<FieldDescriptor, SourceLineRange> fieldLineMap;
189     private Map JavaDoc<MethodDescriptor, SourceLineRange> methodLineMap;
190     private Map JavaDoc<String JavaDoc, SourceLineRange> classLineMap;
191     
192     /**
193      * Constructor.
194      * Creates an empty object.
195      */

196     public SourceInfoMap() {
197         this.fieldLineMap = new HashMap JavaDoc<FieldDescriptor, SourceLineRange>();
198         this.methodLineMap = new HashMap JavaDoc<MethodDescriptor, SourceLineRange>();
199         this.classLineMap = new HashMap JavaDoc<String JavaDoc, SourceLineRange>();
200     }
201     
202     /**
203      * Add a line number entry for a field.
204      *
205      * @param className name of class containing the field
206      * @param fieldName name of field
207      * @param range the line number(s) of the field
208      */

209     public void addFieldLine(String JavaDoc className, String JavaDoc fieldName, SourceLineRange range) {
210         fieldLineMap.put(new FieldDescriptor(className, fieldName), range);
211     }
212     
213     /**
214      * Add a line number entry for a method.
215      *
216      * @param className name of class containing the method
217      * @param methodName name of method
218      * @param methodSignature signature of method
219      * @param range the line number of the method
220      */

221     public void addMethodLine(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSignature,
222             SourceLineRange range) {
223         methodLineMap.put(new MethodDescriptor(className, methodName, methodSignature), range);
224     }
225     
226     /**
227      * Add line number entry for a class.
228      *
229      * @param className name of class
230      * @param range the line numbers of the class
231      */

232     public void addClassLine(String JavaDoc className, SourceLineRange range) {
233         classLineMap.put(className, range);
234     }
235     
236     /**
237      * Look up the line number range for a field.
238      *
239      * @param className name of class containing the field
240      * @param fieldName name of field
241      * @return the line number range, or null if no line number is known for the field
242      */

243     public @CheckForNull SourceLineRange getFieldLine(String JavaDoc className, String JavaDoc fieldName) {
244         return fieldLineMap.get(new FieldDescriptor(className, fieldName));
245     }
246     
247     /**
248      * Look up the line number range for a method.
249      *
250      * @param className name of class containing the method
251      * @param methodName name of method
252      * @param methodSignature signature of method
253      * @return the line number range, or null if no line number is known for the method
254      */

255     public @CheckForNull SourceLineRange getMethodLine(String JavaDoc className, String JavaDoc methodName, String JavaDoc methodSignature) {
256         return methodLineMap.get(new MethodDescriptor(className, methodName, methodSignature));
257     }
258     
259     /**
260      * Look up the line number range for a class.
261      *
262      * @param className name of the class
263      * @return the line number range, or null if no line number is known for the class
264      */

265     public @CheckForNull SourceLineRange getClassLine(String JavaDoc className) {
266         return classLineMap.get(className);
267     }
268     
269     private static final Pattern JavaDoc DIGITS = Pattern.compile("^[0-9]+$");
270     
271     /**
272      * Read source info from given InputStream.
273      * The stream is guaranteed to be closed.
274      *
275      * @param inputStream the InputStream
276      * @throws IOException if an I/O error occurs, or if the format is invalid
277      */

278     public void read(InputStream JavaDoc inputStream) throws IOException JavaDoc {
279         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(inputStream));
280         
281         int lineNumber = 0;
282         try {
283             String JavaDoc line;
284             int lparen;
285             String JavaDoc version;
286             
287             while ((line = reader.readLine()) != null) {
288                 ++lineNumber;
289                 
290                 if (lineNumber == 1) {
291                     if (DEBUG) System.out.println("First line: " + line);
292                     // Try to parse the version number string from the first line.
293
// null means that the line does not appear to be a version number.
294
version = parseVersionNumber(line);
295                     if (version != null) {
296                         // Check to see if version is supported.
297
// Only 1.0 supported for now.
298
if (!version.equals("1.0"))
299                             throw new IOException JavaDoc("Unsupported sourceInfo version " + version);
300                         
301                         // Version looks good. Skip to next line of file.
302
continue;
303                     }
304                 }
305                 
306                 StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(line, ",");
307                 
308                 String JavaDoc className = tokenizer.nextToken();
309                 String JavaDoc next = tokenizer.nextToken();
310                 if (DIGITS.matcher(next).matches()) {
311                     // Line number for class
312
SourceLineRange range = createRange(next, tokenizer.nextToken());
313                     classLineMap.put(className, range);
314                     if (DEBUG) System.out.println("class:" + className + "," + range);
315                 } else if ((lparen = next.indexOf('(')) >= 0) {
316                     // Line number for method
317
String JavaDoc methodName = next.substring(0, lparen);
318                     String JavaDoc methodSignature = next.substring(lparen);
319                     
320                     if (methodName.equals("init^"))
321                         methodName = "<init>";
322                     else if (methodName.equals("clinit^"))
323                         methodName = "<clinit>";
324                     
325                     SourceLineRange range = createRange(tokenizer.nextToken(), tokenizer.nextToken());
326                     methodLineMap.put(new MethodDescriptor(className, methodName, methodSignature), range);
327                     if (DEBUG) System.out.println("method:" + methodName+methodSignature + "," + range);
328                 } else {
329                     // Line number for field
330
String JavaDoc fieldName = next;
331                     SourceLineRange range = createRange(tokenizer.nextToken(), tokenizer.nextToken());
332                     fieldLineMap.put(
333                             new FieldDescriptor(className, fieldName),
334                             range);
335                     if (DEBUG) System.out.println("field:" + className + "," +
336                             fieldName + "," + range);
337                 }
338                 
339                 // Note: we could complain if there are more tokens,
340
// but instead we'll just ignore them.
341
}
342         } catch (NoSuchElementException JavaDoc e) {
343             IOException JavaDoc ioe =
344                 new IOException JavaDoc("Invalid syntax in source info file at line " + lineNumber);
345             ioe.initCause(e);
346             throw ioe;
347         } finally {
348             try {
349                 reader.close();
350             } catch (IOException JavaDoc e) {
351                 // ignore
352
}
353         }
354     }
355
356     /**
357      * Parse the sourceInfo version string.
358      *
359      * @param line the first line of the sourceInfo file
360      * @return the version number constant, or null if
361      * the line does not appear to be a version string
362      */

363     private static String JavaDoc parseVersionNumber(String JavaDoc line) {
364         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(line, " \t");
365
366         if (!expect(tokenizer, "sourceInfo")
367                 || !expect(tokenizer, "version")
368                 || !tokenizer.hasMoreTokens())
369             return null;
370         
371         return tokenizer.nextToken();
372     }
373
374     /**
375      * Expect a particular token string to be returned by the given
376      * StringTokenizer.
377      *
378      * @param tokenizer the StringTokenizer
379      * @param token the expectedToken
380      * @return true if the expected token was returned, false if not
381      */

382     private static boolean expect(StringTokenizer JavaDoc tokenizer, String JavaDoc token) {
383         if (!tokenizer.hasMoreTokens())
384             return false;
385         String JavaDoc s = tokenizer.nextToken();
386         if (DEBUG) System.out.println("token="+s);
387         return s.equals(token);
388     }
389
390     private static SourceLineRange createRange(String JavaDoc start, String JavaDoc end) {
391         return new SourceLineRange(Integer.valueOf(start), Integer.valueOf(end));
392     }
393 }
394
Popular Tags