1 19 20 package edu.umd.cs.findbugs; 21 22 import java.io.IOException ; 23 24 import org.apache.bcel.classfile.Code; 25 import org.apache.bcel.classfile.JavaClass; 26 import org.apache.bcel.classfile.LineNumber; 27 import org.apache.bcel.classfile.LineNumberTable; 28 import org.apache.bcel.classfile.Method; 29 import org.apache.bcel.generic.InstructionHandle; 30 import org.apache.bcel.generic.MethodGen; 31 32 import edu.umd.cs.findbugs.annotations.NonNull; 33 import edu.umd.cs.findbugs.ba.AnalysisContext; 34 import edu.umd.cs.findbugs.ba.ClassContext; 35 import edu.umd.cs.findbugs.ba.Hierarchy; 36 import edu.umd.cs.findbugs.ba.JavaClassAndMethod; 37 import edu.umd.cs.findbugs.ba.SourceInfoMap; 38 import edu.umd.cs.findbugs.ba.XMethod; 39 import edu.umd.cs.findbugs.visitclass.PreorderVisitor; 40 import edu.umd.cs.findbugs.xml.XMLAttributeList; 41 import edu.umd.cs.findbugs.xml.XMLOutput; 42 43 50 public class SourceLineAnnotation implements BugAnnotation { 51 private static final long serialVersionUID = 1L; 52 53 private static final String DEFAULT_ROLE = "SOURCE_LINE_DEFAULT"; 54 private static final String DEFAULT_ROLE_UNKNOWN_LINE = "SOURCE_LINE_DEFAULT_UNKNOWN_LINE"; 55 56 60 public static final String UNKNOWN_SOURCE_FILE = "<Unknown>"; 61 62 private String description; 63 final private String className; 64 private String sourceFile; 65 final private int startLine; 66 final private int endLine; 67 final private int startBytecode; 68 final private int endBytecode; 69 private boolean synthetic; 70 71 81 public SourceLineAnnotation(@NonNull String className, @NonNull String sourceFile, int startLine, int endLine, 82 int startBytecode, int endBytecode) { 83 if (className == null) throw new IllegalArgumentException ("class name is null"); 84 if (sourceFile == null) throw new IllegalArgumentException ("source file is null"); 85 this.description = DEFAULT_ROLE; 86 this.className = className; 87 this.sourceFile = sourceFile; 88 this.startLine = startLine; 89 this.endLine = endLine; 90 this.startBytecode = startBytecode; 91 this.endBytecode = endBytecode; 92 } 93 94 @Override 96 public Object clone() { 97 try { 98 return super.clone(); 99 } catch (CloneNotSupportedException e) { 100 throw new AssertionError (e); 101 } 102 } 103 104 111 public static SourceLineAnnotation createUnknown(String className, String sourceFile) { 112 return createUnknown(className, sourceFile, -1, -1); 113 } 114 115 123 public static SourceLineAnnotation createUnknown(String className) { 124 return createUnknown( 125 className, 126 AnalysisContext.currentAnalysisContext().lookupSourceFile(className), 127 -1, 128 -1); 129 } 130 131 140 public static SourceLineAnnotation createUnknown(String className, String sourceFile, int startBytecode, int endBytecode) { 141 SourceLineAnnotation result = new SourceLineAnnotation(className, sourceFile, -1, -1, startBytecode, endBytecode); 142 return result; 144 } 145 146 153 public static SourceLineAnnotation fromVisitedMethod(PreorderVisitor visitor) { 154 LineNumberTable lineNumberTable = getLineNumberTable(visitor); 155 String className = visitor.getDottedClassName(); 156 String sourceFile = visitor.getSourceFile(); 157 Code code = visitor.getMethod().getCode(); 158 int codeSize = (code != null) ? code.getCode().length : 0; 159 if (lineNumberTable == null) { 160 SourceInfoMap.SourceLineRange range = AnalysisContext.currentAnalysisContext() 162 .getSourceInfoMap() 163 .getMethodLine(className, visitor.getMethodName(), visitor.getMethodSig()); 164 if (range != null) { 165 return new SourceLineAnnotation( 166 className, 167 visitor.getSourceFile(), 168 range.getStart().intValue(), 169 range.getEnd().intValue(), 170 0, 171 codeSize - 1); 172 } else { 173 return createUnknown(className, sourceFile, 0, codeSize - 1); 174 } 175 } 176 return forEntireMethod(className, sourceFile, lineNumberTable, codeSize); 177 } 178 179 187 public static SourceLineAnnotation fromVisitedMethod(MethodGen methodGen, String sourceFile) { 188 LineNumberTable lineNumberTable = methodGen.getLineNumberTable(methodGen.getConstantPool()); 189 String className = methodGen.getClassName(); 190 int codeSize = methodGen.getInstructionList().getLength(); 191 if (lineNumberTable == null) 192 return createUnknown(className, sourceFile, 0, codeSize - 1); 193 return forEntireMethod(className, sourceFile, lineNumberTable, codeSize); 194 } 195 196 205 public static SourceLineAnnotation forEntireMethod(String className, String sourceFile, 206 LineNumberTable lineNumberTable, int codeSize) { 207 LineNumber[] table = lineNumberTable.getLineNumberTable(); 208 if (table != null && table.length > 0) { 209 LineNumber first = table[0]; 210 LineNumber last = table[table.length - 1]; 211 return new SourceLineAnnotation(className, sourceFile, first.getLineNumber(), last.getLineNumber(), 212 0, codeSize - 1); 213 } else { 214 return createUnknown(className, sourceFile, 0, codeSize - 1); 215 } 216 } 217 218 225 public static SourceLineAnnotation forEntireMethod(JavaClass javaClass, Method method) { 226 String sourceFile = javaClass.getSourceFileName(); 227 Code code = method.getCode(); 228 LineNumberTable lineNumberTable = method.getLineNumberTable(); 229 if (code == null || lineNumberTable == null) { 230 return createUnknown(javaClass.getClassName(), sourceFile); 231 } 232 233 return forEntireMethod(javaClass.getClassName(), sourceFile, lineNumberTable, code.getLength()); 234 } 235 236 243 public static SourceLineAnnotation forEntireMethod(JavaClass javaClass, XMethod xmethod) { 244 JavaClassAndMethod m = Hierarchy.findMethod(javaClass, xmethod.getName(), xmethod.getSignature()); 245 if (m == null) { 246 return createUnknown(javaClass.getClassName(), javaClass.getSourceFileName()); 247 } else { 248 return forEntireMethod(javaClass, m.getMethod()); 249 } 250 } 251 252 261 public static SourceLineAnnotation fromVisitedInstruction(BytecodeScanningDetector visitor, int pc) { 262 return fromVisitedInstructionRange(visitor.getClassContext(), visitor, pc, pc); 263 } 264 265 275 public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, PreorderVisitor visitor, int pc) { 276 return fromVisitedInstructionRange(classContext, visitor, pc, pc); 277 } 278 279 290 public static SourceLineAnnotation fromVisitedInstructionRange( 291 BytecodeScanningDetector visitor, int startPC, int endPC) { 292 LineNumberTable lineNumberTable = getLineNumberTable(visitor); 293 String className = visitor.getDottedClassName(); 294 String sourceFile = visitor.getSourceFile(); 295 296 if (lineNumberTable == null) 297 return createUnknown(className, sourceFile, startPC, endPC); 298 299 int startLine = lineNumberTable.getSourceLine(startPC); 300 int endLine = lineNumberTable.getSourceLine(endPC); 301 return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC) 302 .addInstructionContext(visitor.getClassContext(), visitor.getMethod()); 303 } 304 305 317 public static SourceLineAnnotation fromVisitedInstructionRange( 318 ClassContext classContext, PreorderVisitor visitor, int startPC, int endPC) { 319 LineNumberTable lineNumberTable = getLineNumberTable(visitor); 320 String className = visitor.getDottedClassName(); 321 String sourceFile = visitor.getSourceFile(); 322 323 if (lineNumberTable == null) 324 return createUnknown(className, sourceFile, startPC, endPC); 325 326 int startLine = lineNumberTable.getSourceLine(startPC); 327 int endLine = lineNumberTable.getSourceLine(endPC); 328 return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC) 329 .addInstructionContext(classContext, visitor.getMethod()); 330 } 331 332 340 public static SourceLineAnnotation fromVisitedInstruction(BytecodeScanningDetector visitor) { 341 return fromVisitedInstruction(visitor.getClassContext(), visitor, visitor.getPC()); 342 } 343 344 354 public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, MethodGen methodGen, String sourceFile, @NonNull InstructionHandle handle) { 355 LineNumberTable table = methodGen.getLineNumberTable(methodGen.getConstantPool()); 356 String className = methodGen.getClassName(); 357 358 int bytecodeOffset = handle.getPosition(); 359 360 if (table == null) 361 return createUnknown(className, sourceFile, bytecodeOffset, bytecodeOffset); 362 363 int lineNumber = table.getSourceLine(handle.getPosition()); 364 return new SourceLineAnnotation( 365 className, sourceFile, lineNumber, lineNumber, bytecodeOffset, bytecodeOffset) 366 .addInstructionContext(classContext, classContext.getMethod(methodGen)); 367 } 368 369 378 public static SourceLineAnnotation fromVisitedInstructionRange( 379 ClassContext classContext, MethodGen methodGen, String sourceFile, InstructionHandle start, InstructionHandle end) { 380 LineNumberTable lineNumberTable = methodGen.getLineNumberTable(methodGen.getConstantPool()); 381 String className = methodGen.getClassName(); 382 383 if (lineNumberTable == null) 384 return createUnknown(className, sourceFile, start.getPosition(), end.getPosition()); 385 386 int startLine = lineNumberTable.getSourceLine(start.getPosition()); 387 int endLine = lineNumberTable.getSourceLine(end.getPosition()); 388 return new SourceLineAnnotation( 389 className, sourceFile, startLine, endLine, start.getPosition(), end.getPosition()) 390 .addInstructionContext(classContext, classContext.getMethod(methodGen)); 391 } 392 393 private static LineNumberTable getLineNumberTable(PreorderVisitor visitor) { 394 Code code = visitor.getMethod().getCode(); 395 if (code == null) 396 return null; 397 return code.getLineNumberTable(); 398 } 399 400 private static final int NUM_CONTEXT_OPCODES = 5; 401 402 408 private SourceLineAnnotation addInstructionContext(ClassContext classContext, Method method) { 409 return this; 411 } 412 413 416 public String getClassName() { 417 return className; 418 } 419 420 423 public String getSourceFile() { 424 return sourceFile; 425 } 426 427 430 public boolean isSourceFileKnown() { 431 return !sourceFile.equals(UNKNOWN_SOURCE_FILE); 432 } 433 434 439 public void setSourceFile(String sourceFile) { 440 this.sourceFile = sourceFile; 441 } 442 443 446 public String getPackageName() { 447 int lastDot = className.lastIndexOf('.'); 448 if (lastDot < 0) 449 return ""; 450 else 451 return className.substring(0, lastDot); 452 } 453 454 457 public int getStartLine() { 458 return startLine; 459 } 460 461 464 public int getEndLine() { 465 return endLine; 466 } 467 468 471 public int getStartBytecode() { 472 return startBytecode; 473 } 474 475 478 public int getEndBytecode() { 479 return endBytecode; 480 } 481 482 485 public boolean isUnknown() { 486 return startLine < 0 || endLine < 0; 487 } 488 489 public void accept(BugAnnotationVisitor visitor) { 490 visitor.visitSourceLineAnnotation(this); 491 } 492 493 public String format(String key, ClassAnnotation primaryClass) { 494 if (key.equals("hash")) return ""; 495 if (key.equals("")) { 496 StringBuffer buf = new StringBuffer (); 497 buf.append(sourceFile); 498 appendLines(buf); 499 return buf.toString(); 500 } else if (key.equals("lineNumber")) { 501 StringBuffer buf = new StringBuffer (); 502 appendLinesRaw(buf); 503 return buf.toString(); 504 } else if (key.equals("full")) { 505 StringBuffer buf = new StringBuffer (); 506 String pkgName = getPackageName(); 507 if (!pkgName.equals("")) { 508 buf.append(pkgName.replace('.', '/')); 509 buf.append('/'); 510 } 511 buf.append(sourceFile); 512 appendLines(buf); 513 return buf.toString(); 514 } else 515 throw new IllegalArgumentException ("Unknown format key " + key); 516 } 517 518 private void appendLines(StringBuffer buf) { 519 if (isUnknown()) return; 520 buf.append(":["); 521 appendLinesRaw(buf); 522 buf.append(']'); 523 } 524 525 private void appendLinesRaw(StringBuffer buf) { 526 if (isUnknown()) return; 527 if (startLine == endLine) { 528 buf.append("line "); 529 buf.append(startLine); 530 } else { 531 buf.append("lines "); 532 buf.append(startLine); 533 buf.append('-'); 534 buf.append(endLine); 535 } 536 537 } 538 539 public String getDescription() { 540 return description; 541 } 542 543 public void setDescription(String description) { 544 this.description = description; 545 } 546 547 @Override 548 public String toString() { 549 String desc = description; 550 if (desc.equals(DEFAULT_ROLE) && isUnknown()) 551 desc = DEFAULT_ROLE_UNKNOWN_LINE; 552 String pattern = I18N.instance().getAnnotationDescription(desc); 553 FindBugsMessageFormat format = new FindBugsMessageFormat(pattern); 554 return format.format(new BugAnnotation[]{this}, null); 555 } 556 557 public int compareTo(BugAnnotation o) { 558 if (!(o instanceof SourceLineAnnotation)) return this.getClass().getName().compareTo(o.getClass().getName()); 560 561 SourceLineAnnotation other = (SourceLineAnnotation) o; 562 563 int cmp = className.compareTo(other.className); 564 if (cmp != 0) 565 return cmp; 566 cmp = startLine - other.startLine; 567 if (cmp != 0) 568 return cmp; 569 return endLine - other.endLine; 570 } 571 572 @Override 573 public int hashCode() { 574 return className.hashCode() + startLine + 3 * endLine; 575 } 576 577 @Override 578 public boolean equals(Object o) { 579 if (!(o instanceof SourceLineAnnotation)) 580 return false; 581 SourceLineAnnotation other = (SourceLineAnnotation) o; 582 return className.equals(other.className) 583 && startLine == other.startLine 584 && endLine == other.endLine; 585 } 586 587 590 591 private static final String ELEMENT_NAME = "SourceLine"; 592 593 public void writeXML(XMLOutput xmlOutput) throws IOException { 594 writeXML(xmlOutput, false); 595 } 596 597 public void writeXML(XMLOutput xmlOutput, boolean addMessages) throws IOException { 598 String classname = getClassName(); 599 String packageName = ""; 600 if (classname.indexOf('.') > 0) 601 packageName = classname.substring(0,1+classname.lastIndexOf('.')); 602 XMLAttributeList attributeList = new XMLAttributeList() 603 .addAttribute("classname", classname); 604 int n = getStartLine(); if (n >= 0) attributeList.addAttribute("start", String.valueOf(n)); 606 n = getEndLine(); 607 if (n >= 0) attributeList.addAttribute("end", String.valueOf(n)); 608 n = getStartBytecode(); if (n >= 0) attributeList.addAttribute("startBytecode", String.valueOf(n)); 610 n = getEndBytecode(); 611 if (n >= 0) attributeList.addAttribute("endBytecode", String.valueOf(n)); 612 613 if (isSourceFileKnown()) { 614 attributeList.addAttribute("sourcefile", sourceFile); 615 attributeList.addAttribute("sourcepath", packageName.replace('.', '/')+sourceFile); 616 } 617 618 String role = getDescription(); 619 if (!role.equals(DEFAULT_ROLE)) 620 attributeList.addAttribute("role", getDescription()); 621 if (synthetic) 622 attributeList.addAttribute("synthetic", "true"); 623 if (addMessages) { 624 xmlOutput.openTag(ELEMENT_NAME, attributeList); 625 xmlOutput.openTag("Message"); 626 xmlOutput.writeText(this.toString()); 627 xmlOutput.closeTag("Message"); 628 xmlOutput.closeTag(ELEMENT_NAME); 629 } else { 630 xmlOutput.openCloseTag(ELEMENT_NAME, attributeList); 631 } 632 } 633 634 637 public void setSynthetic(boolean synthetic) { 638 this.synthetic = synthetic; 639 } 640 641 644 public boolean isSynthetic() { 645 return synthetic; 646 } 647 648 649 public boolean isSignificant() { 650 return false; 651 } 652 } 653 654 | Popular Tags |