1 19 20 package edu.umd.cs.findbugs.ba; 21 22 import java.io.BufferedReader ; 23 import java.io.IOException ; 24 import java.io.InputStream ; 25 import java.io.InputStreamReader ; 26 import java.util.HashMap ; 27 import java.util.Map ; 28 import java.util.NoSuchElementException ; 29 import java.util.StringTokenizer ; 30 import java.util.TreeMap ; 31 import java.util.regex.Pattern ; 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 47 public class SourceInfoMap { 48 static class FieldDescriptor implements Comparable <FieldDescriptor> { 49 String className; 50 String fieldName; 51 52 public FieldDescriptor(String className, String fieldName) { 53 this.className = className; 54 this.fieldName = fieldName; 55 } 56 57 public String toString() { 58 return className +"." + fieldName; 59 } 60 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 73 @Override 74 public int hashCode() { 75 return 1277 * className.hashCode() +fieldName.hashCode(); 76 } 77 78 81 @Override 82 public boolean equals(Object 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 <MethodDescriptor> { 91 private String className; 92 private String methodName; 93 private String methodSignature; 94 95 public MethodDescriptor(String className, String methodName, String methodSignature) { 96 this.className = className; 97 this.methodName = methodName; 98 this.methodSignature = methodSignature; 99 } 100 public String toString() { 101 return className +"." + methodName; 102 } 103 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 118 @Override 119 public int hashCode() { 120 return 1277 * className.hashCode() 121 + 37 * methodName.hashCode() 122 + methodSignature.hashCode(); 123 } 124 125 128 @Override 129 public boolean equals(Object 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 142 public static class SourceLineRange { 143 private final Integer start, end; 144 145 148 public SourceLineRange(@NonNull Integer line) { 149 this.start = this.end = line; 150 } 151 152 158 public SourceLineRange(@NonNull Integer start, @NonNull Integer end) { 159 this.start = start; 160 this.end = end; 161 } 162 163 166 public @NonNull Integer getStart() { 167 return start; 168 } 169 170 173 public @NonNull Integer getEnd() { 174 return end; 175 } 176 177 180 @Override 181 public String 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 <FieldDescriptor, SourceLineRange> fieldLineMap; 189 private Map <MethodDescriptor, SourceLineRange> methodLineMap; 190 private Map <String , SourceLineRange> classLineMap; 191 192 196 public SourceInfoMap() { 197 this.fieldLineMap = new HashMap <FieldDescriptor, SourceLineRange>(); 198 this.methodLineMap = new HashMap <MethodDescriptor, SourceLineRange>(); 199 this.classLineMap = new HashMap <String , SourceLineRange>(); 200 } 201 202 209 public void addFieldLine(String className, String fieldName, SourceLineRange range) { 210 fieldLineMap.put(new FieldDescriptor(className, fieldName), range); 211 } 212 213 221 public void addMethodLine(String className, String methodName, String methodSignature, 222 SourceLineRange range) { 223 methodLineMap.put(new MethodDescriptor(className, methodName, methodSignature), range); 224 } 225 226 232 public void addClassLine(String className, SourceLineRange range) { 233 classLineMap.put(className, range); 234 } 235 236 243 public @CheckForNull SourceLineRange getFieldLine(String className, String fieldName) { 244 return fieldLineMap.get(new FieldDescriptor(className, fieldName)); 245 } 246 247 255 public @CheckForNull SourceLineRange getMethodLine(String className, String methodName, String methodSignature) { 256 return methodLineMap.get(new MethodDescriptor(className, methodName, methodSignature)); 257 } 258 259 265 public @CheckForNull SourceLineRange getClassLine(String className) { 266 return classLineMap.get(className); 267 } 268 269 private static final Pattern DIGITS = Pattern.compile("^[0-9]+$"); 270 271 278 public void read(InputStream inputStream) throws IOException { 279 BufferedReader reader = new BufferedReader (new InputStreamReader (inputStream)); 280 281 int lineNumber = 0; 282 try { 283 String line; 284 int lparen; 285 String 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 version = parseVersionNumber(line); 295 if (version != null) { 296 if (!version.equals("1.0")) 299 throw new IOException ("Unsupported sourceInfo version " + version); 300 301 continue; 303 } 304 } 305 306 StringTokenizer tokenizer = new StringTokenizer (line, ","); 307 308 String className = tokenizer.nextToken(); 309 String next = tokenizer.nextToken(); 310 if (DIGITS.matcher(next).matches()) { 311 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 String methodName = next.substring(0, lparen); 318 String 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 String 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 } 342 } catch (NoSuchElementException e) { 343 IOException ioe = 344 new IOException ("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 e) { 351 } 353 } 354 } 355 356 363 private static String parseVersionNumber(String line) { 364 StringTokenizer tokenizer = new StringTokenizer (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 382 private static boolean expect(StringTokenizer tokenizer, String token) { 383 if (!tokenizer.hasMoreTokens()) 384 return false; 385 String s = tokenizer.nextToken(); 386 if (DEBUG) System.out.println("token="+s); 387 return s.equals(token); 388 } 389 390 private static SourceLineRange createRange(String start, String end) { 391 return new SourceLineRange(Integer.valueOf(start), Integer.valueOf(end)); 392 } 393 } 394 | Popular Tags |