1 26 27 package net.sourceforge.groboutils.codecoverage.v2.util; 28 29 import java.io.BufferedReader ; 30 import java.io.File ; 31 import java.io.FileReader ; 32 import java.io.IOException ; 33 import java.io.PrintStream ; 34 import java.util.ArrayList ; 35 import java.util.HashMap ; 36 import java.util.HashSet ; 37 import java.util.Iterator ; 38 import java.util.List ; 39 import java.util.Map ; 40 import java.util.Set ; 41 42 import net.sourceforge.groboutils.codecoverage.v2.IAnalysisModule; 43 import net.sourceforge.groboutils.codecoverage.v2.datastore.AnalysisModuleSet; 44 import net.sourceforge.groboutils.codecoverage.v2.datastore.ClassRecord; 45 import net.sourceforge.groboutils.codecoverage.v2.datastore.DirMetaDataReader; 46 import net.sourceforge.groboutils.codecoverage.v2.datastore.IClassMetaDataReader; 47 import net.sourceforge.groboutils.codecoverage.v2.datastore.IMetaDataReader; 48 import net.sourceforge.groboutils.codecoverage.v2.datastore.MarkRecord; 49 50 import org.apache.bcel.classfile.ClassParser; 51 import org.apache.bcel.classfile.CodeException; 52 import org.apache.bcel.classfile.ConstantPool; 53 import org.apache.bcel.classfile.JavaClass; 54 import org.apache.bcel.classfile.LineNumberTable; 55 import org.apache.bcel.classfile.Method; 56 import org.apache.bcel.generic.InstructionHandle; 57 import org.apache.bcel.generic.InstructionList; 58 59 60 68 public class ClassfileCheckerCLI 69 { 70 public static void main( String args[] ) 71 throws IOException 72 { 73 if (args.length <= 1) 74 { 75 System.out.println("ClassfileCheckerCLI:"); 76 System.out.println(" Compares original vs. covered class files"); 77 System.out.println(""); 78 System.out.println(" Usage:"); 79 System.out.println(" Report the lines in source code that are marked:"); 80 System.out.println(" <jdk stuff> " + 81 ClassfileCheckerCLI.class.getName() + 82 " -m classname datadir srcdir" ); 83 System.out.println(" Report the differences between the original and covered class files:"); 84 System.out.println(" <jdk stuff> " + 85 ClassfileCheckerCLI.class.getName() + 86 " -c classname origclassdir covclassdir" ); 87 System.out.println(" Both reports:"); 88 System.out.println(" <jdk stuff> " + 89 ClassfileCheckerCLI.class.getName() + 90 " -mc classname datadir srcdir origclassdir covclassdir" ); 91 } 92 93 String type = args[0].toLowerCase(); 95 String cname = args[1]; 96 String datadir = null; 97 String srcdir = null; 98 String origclassdir = null; 99 String covclassdir = null; 100 boolean doMarkReport = false; 101 boolean doClassReport = false; 102 103 int pos = 2; 104 if (type.indexOf('m') >= 0) 105 { 106 doMarkReport = true; 107 datadir = args[pos++]; 108 srcdir = args[pos++]; 109 } 110 if (type.indexOf('c') >= 0) 111 { 112 doClassReport = true; 113 origclassdir = args[pos++]; 114 covclassdir = args[pos++]; 115 } 116 117 ClassfileCheckerCLI ccc = new ClassfileCheckerCLI(); 118 119 120 if (doMarkReport) 121 { 122 ccc.reportMarkedSource( cname, new File ( datadir ), 123 new File ( srcdir ), System.out ); 124 } 125 126 if (doClassReport) 127 { 128 ccc.reportClassDiff( cname, new File ( origclassdir ), 129 new File ( covclassdir ), System.out ); 130 } 131 } 132 133 public void reportMarkedSource( String classname, File coveredDataDir, 134 File srcDir, PrintStream out ) 135 throws IOException 136 { 137 IMetaDataReader mdr = createMetaDataReader( coveredDataDir ); 138 File srcFile = getSourceFileForClass( classname, srcDir ); 139 reportMarkedSource( classname, mdr, srcFile, out ); 140 } 141 142 143 147 public void reportMarkedSource( String classname, IMetaDataReader mdr, 148 File srcFile, PrintStream out ) 149 throws IOException 150 { 151 out.println( "Marks on source for class "+classname ); 152 out.println( "" ); 153 154 MarkRecord mrL[] = getMarkRecords( classname, mdr ); 155 Map amIndex = createAnalysisModuleIndexMap( mdr ); 156 int maxAM = printLegend( amIndex, out ); 157 158 FileReader fr = new FileReader ( srcFile ); 159 try 160 { 161 BufferedReader br = new BufferedReader ( fr ); 162 int lineNo = 0; 163 String line = br.readLine(); 164 while (line != null) 165 { 166 ++lineNo; 167 168 out.print( ' ' + format8( lineNo ) ); 169 out.print( " | " ); 170 171 String amnL[] = markedLine( mrL, lineNo ); 173 for (int i = 1; i <= maxAM; ++i) 174 { 175 boolean notfound = true; 176 Integer idx = new Integer (i); 177 for (int j = 0; j < amnL.length; ++j) 178 { 179 if (idx.equals( amIndex.get( amnL[j] ) )) 180 { 181 out.print( indexCode( i ) ); 182 notfound = false; 183 break; 184 } 185 } 186 if (notfound) 187 { 188 out.print( " " ); 189 } 190 } 191 out.println( " | " + line ); 192 193 line = br.readLine(); 194 } 195 } 196 finally 197 { 198 fr.close(); 199 } 200 } 201 202 203 208 public void reportClassDiff( String classname, File originalClassDir, 209 File coveredClassDir, PrintStream out ) 210 throws IOException 211 { 212 File origClassF = getClassFileForClass( classname, originalClassDir ); 213 File covClassF = getClassFileForClass( classname, coveredClassDir ); 214 215 JavaClass origClass = createJavaClass( origClassF ); 216 JavaClass covClass = createJavaClass( covClassF ); 217 218 Method omL[] = origClass.getMethods(); 219 Method cmL[] = covClass.getMethods(); 220 221 out.println( "Comparing covered and original classes for class named "+ 222 classname ); 223 out.println( "(Original is on the left side, covered is on the right)" ); 224 225 Set covNotFound = new HashSet (); 227 for (int i = 0; i < cmL.length; ++i) 228 { 229 covNotFound.add( cmL[i] ); 230 } 231 for (int i = 0; i < omL.length; ++i) 232 { 233 String omName = omL[i].getName(); 234 String omSig = omL[i].getSignature(); 235 boolean notFound = true; 236 for (int j = 0; notFound && j < cmL.length; ++j) 237 { 238 if (omName.equals( cmL[j].getName() ) && 239 omSig.equals( cmL[j].getSignature() )) 240 { 241 notFound = false; 242 covNotFound.remove( cmL[j] ); 243 out.println(""); 244 reportMethodDiff( omL[i], cmL[j], 245 origClass.getConstantPool(), 246 covClass.getConstantPool(), out ); 247 } 248 } 249 if (notFound) 250 { 251 out.println(""); 252 reportRemovedMethod( omL[i], out ); 253 } 254 } 255 Iterator iter = covNotFound.iterator(); 256 while (iter.hasNext()) 257 { 258 out.println(""); 259 reportAddedMethod( (Method)iter.next(), out ); 260 } 261 } 262 263 public void reportMethodDiff( Method orig, Method cov, 264 ConstantPool origCP, ConstantPool covCP, PrintStream out ) 265 { 266 out.println( " = Method " + orig.getName() + " " + 267 orig.getSignature() ); 268 LineNumberTable ot = orig.getLineNumberTable(); 269 LineNumberTable ct = cov.getLineNumberTable(); 270 CodeException oce[] = orig.getCode().getExceptionTable(); 271 CodeException cce[] = cov.getCode().getExceptionTable(); 272 273 InstructionHandle oList[] = (new InstructionList( 275 orig.getCode().getCode() )).getInstructionHandles(); 276 InstructionHandle cList[] = (new InstructionList( 277 cov.getCode().getCode() )).getInstructionHandles(); 278 int oNextI = 0; 279 int cNextI = 0; 280 int lastOLine = -2; 281 int lastCLine = -2; 282 while (oNextI < oList.length && cNextI < cList.length) 283 { 284 InstructionHandle oNext = null; 285 InstructionHandle cNext = null; 286 if (oNextI < oList.length) 287 { 288 oNext = oList[ oNextI ]; 289 } 290 if (cNextI < cList.length) 291 { 292 cNext = cList[ cNextI ]; 293 } 294 if (oNext == null || 295 !oNext.getInstruction().equals( cNext.getInstruction() )) 296 { 297 lastOLine = -2; 298 int lineNo = ct.getSourceLine( cNext.getPosition() ); 299 if (lastCLine != lineNo) 300 { 301 out.println( " + Line: Orig: N/A Covered: " + 302 format8( lineNo ) ); 303 } 304 printCodeException( "Covered", cce, cNext.getPosition(), covCP, out ); 305 printSideBySide( null, cNext.getInstruction().toString( true ), 306 36, " | ", " ", out ); 307 lastCLine = lineNo; 308 ++cNextI; 309 } 310 else if (cNext == null) 311 { 312 lastCLine = -2; 313 int lineNo = ot.getSourceLine( oNext.getPosition() ); 314 if (lastOLine != lineNo) 315 { 316 out.println( " - Line: Orig: " + format8( lineNo ) + 317 " Covered: N/A" ); 318 } 319 printCodeException( "Original", oce, oNext.getPosition(), origCP, out ); 320 printSideBySide( oNext.getInstruction().toString( true ), null, 321 36, " | ", " ", out ); 322 lastOLine = lineNo; 323 ++oNextI; 324 } 325 else 326 { 327 int olineNo = ot.getSourceLine( oNext.getPosition() ); 328 int clineNo = ct.getSourceLine( cNext.getPosition() ); 329 if (lastOLine != olineNo && lastCLine != clineNo) 330 { 331 out.println( " = Line: Orig: " + format8( olineNo ) + 332 " Covered: " + format8( clineNo ) ); 333 } 334 printCodeException( "Original", oce, oNext.getPosition(), origCP, out ); 335 printCodeException( "Covered", cce, cNext.getPosition(), covCP, out ); 336 printSideBySide( oNext.getInstruction().toString( true ), 337 cNext.getInstruction().toString( true ), 338 36, " | ", " ", out ); 339 lastOLine = olineNo; 340 lastCLine = clineNo; 341 ++oNextI; 342 ++cNextI; 343 } 344 } 345 } 346 347 348 public void reportRemovedMethod( Method orig, PrintStream out ) 349 { 350 out.println( " < Method Removed in covered version: " + orig.getName() + 351 " " + orig.getSignature() ); 352 LineNumberTable ot = orig.getLineNumberTable(); 353 354 InstructionHandle oNext = 355 (new InstructionList( orig.getCode().getCode() )).getStart(); 356 while (oNext != null) 357 { 358 out.println( " - Line: Orig: " + 359 format8( ot.getSourceLine( oNext.getPosition() ) ) + 360 " Covered: N/A" ); 361 printSideBySide( oNext.getInstruction().toString( true ), null, 362 36, " | ", " ", out ); 363 oNext = oNext.getNext(); 364 } 365 } 366 367 368 public void reportAddedMethod( Method cov, PrintStream out ) 369 { 370 out.println( " > Method Added in covered version: " + cov.getName() + 371 " " + cov.getSignature() ); 372 LineNumberTable ct = cov.getLineNumberTable(); 373 374 InstructionHandle cNext = 375 (new InstructionList( cov.getCode().getCode() )).getStart(); 376 while (cNext != null) 377 { 378 out.println( " + Line: Orig: N/A Covered: " + 379 format8( ct.getSourceLine( cNext.getPosition() ) ) ); 380 printSideBySide( null, cNext.getInstruction().toString( true ), 381 36, " | ", " ", out ); 382 cNext = cNext.getNext(); 383 } 384 } 385 386 387 391 public String [] markedLine( MarkRecord mrL[], int lineNo ) 392 { 393 Set ret = new HashSet (); 394 for (int i = 0; i < mrL.length; ++i) 395 { 396 if (mrL[i].getLineNumber() == lineNo) 397 { 398 ret.add( mrL[i].getAnalysisModule() ); 399 } 400 } 401 402 return (String [])ret.toArray( new String [ ret.size() ] ); 403 } 404 405 406 407 411 public File [] getClassFilesForClass( String classname, File classdir ) 412 { 413 String pkgname = getPackageName( classname ).replace( '.', 414 File.separatorChar ); 415 String cname = getJustClassName( classname ); 416 List ret = new ArrayList (); 417 if (classdir != null) 418 { 419 String match1 = cname + ".class"; 420 String match2 = cname + '$'; 421 File pkgdir = new File ( classdir, pkgname ); 422 if (pkgdir.exists() && pkgdir.isDirectory()) 423 { 424 String files[] = pkgdir.list(); 425 for (int i = 0; i < files.length; ++i) 426 { 427 if (files[i].equals( match1 ) || 428 (files[i].startsWith( match2 ) && 429 files[i].endsWith( ".class" ))) 430 { 431 ret.add( new File ( pkgdir, files[i] ) ); 432 } 433 } 434 } 435 } 436 return (File [])ret.toArray( new File [ ret.size() ] ); 437 } 438 439 440 443 public File getClassFileForClass( String classname, File classdir ) 444 throws IOException 445 { 446 String fname = classname.replace( '.', File.separatorChar ) + ".class"; 447 File cf = new File ( classdir, fname ); 448 if (!cf.exists() || !cf.isFile()) 449 { 450 throw new java.io.FileNotFoundException ( 451 "No such class file '"+fname+"' in directory '"+ 452 classdir+"'" ); 453 } 454 return cf; 455 } 456 457 458 461 public File getSourceFileForClass( String classname, File srcdir ) 462 throws IOException 463 { 464 int pos = classname.indexOf( '$' ); 465 if (pos > 0) 466 { 467 classname = classname.substring( 0, pos ); 468 } 469 String fname = classname.replace( '.', File.separatorChar ) + ".java"; 470 File cf = new File ( srcdir, fname ); 471 if (!cf.exists() || !cf.isFile()) 474 { 475 throw new java.io.FileNotFoundException ( 476 "No such source file '"+fname+"' in directory '"+ 477 srcdir ); 478 } 479 return cf; 480 } 481 482 483 public MarkRecord[] getMarkRecords( String classname, IMetaDataReader mdr ) 484 throws IOException 485 { 486 AnalysisModuleSet ams = mdr.getAnalysisModuleSet(); 487 IAnalysisModule amL[] = ams.getAnalysisModules(); 488 List ret = new ArrayList (); 489 for (int i = 0; i < amL.length; ++i) 490 { 491 IClassMetaDataReader cmdr = mdr.getClassReader( amL[i] ); 492 String [] sigs = getMachingClassSignatures( classname, 493 cmdr.getClassSignatures() ); 494 for (int j = 0; j < sigs.length; ++j) 495 { 496 ClassRecord cr = cmdr.readClass( sigs[j] ); 497 if (cr != null) 498 { 499 MarkRecord[] mrL = cr.getMarksForAnalysisModule( amL[i] ); 500 for (int k = 0; k < mrL.length; ++k) 501 { 502 ret.add( mrL[k] ); 503 } 504 } 505 } 506 } 507 508 return (MarkRecord[])ret.toArray( new MarkRecord[ ret.size() ] ); 509 } 510 511 512 protected JavaClass createJavaClass( File filename ) 513 throws IOException 514 { 515 ClassParser cp = new ClassParser( filename.getAbsolutePath() ); 516 return cp.parse(); 517 } 518 519 520 protected String [] getMachingClassSignatures( String classname, 521 String sigs[] ) 522 { 523 List ret = new ArrayList (); 524 String match1 = classname + '-'; 525 String match2 = classname + '$'; 526 if (sigs != null) 527 { 528 for (int i = 0; i < sigs.length; ++i) 529 { 530 if (sigs[i].startsWith( match1 ) || 531 sigs[i].startsWith( match2 )) 532 { 533 ret.add( sigs[i] ); 534 } 535 } 536 } 537 return (String [])ret.toArray( new String [ ret.size() ] ); 538 } 539 540 541 protected String getPackageName( String classname ) 542 { 543 String pkgname = ""; 544 int i = classname.lastIndexOf( '.' ); 545 if (i >= 0) 546 { 547 pkgname = classname.substring( 0, i-1 ); 548 } 549 return pkgname; 550 } 551 552 553 protected String getJustClassName( String classname ) 554 { 555 String cname = classname; 556 int i = classname.lastIndexOf( '.' ); 557 if (i >= 0) 558 { 559 cname = classname.substring( i+1 ); 560 } 561 return cname; 562 } 563 564 565 protected IMetaDataReader createMetaDataReader( File datadir ) 566 throws IOException 567 { 568 return new DirMetaDataReader( datadir ); 569 } 570 571 572 protected Map createAnalysisModuleIndexMap( IMetaDataReader mdr ) 573 throws IOException 574 { 575 HashMap map = new HashMap (); 576 int count = 0; 577 AnalysisModuleSet ams = mdr.getAnalysisModuleSet(); 578 IAnalysisModule amL[] = ams.getAnalysisModules(); 579 for (int j = 0; j < amL.length; ++j) 580 { 581 if (!map.containsKey( amL[j].getMeasureName() )) 582 { 583 map.put( amL[j].getMeasureName(), new Integer ( ++count ) ); 584 } 585 } 586 return map; 587 } 588 589 590 protected int printLegend( Map amToIndex, PrintStream out ) 591 { 592 out.println( "Legend of Analysis Modules:" ); 593 int index = 0; 594 Set entries = amToIndex.entrySet(); 595 boolean found = true; 596 while (found) 597 { 598 Integer x = new Integer ( ++index ); 599 found = false; 600 Iterator iter = entries.iterator(); 601 while (iter.hasNext()) 602 { 603 Map.Entry e = (Map.Entry )iter.next(); 604 if (e.getValue().equals( x )) 605 { 606 found = true; 607 out.println( " "+indexCode( index )+" = "+ 608 (String )e.getKey() ); 609 } 610 } 611 } 612 613 out.println( "" ); 614 out.print( " lineno | " ); 615 for (int i = 1; i < index; ++i) 616 { 617 out.print( indexCode( i ) ); 618 } 619 out.println( " | source code" ); 620 for (int i = 0; i < 78; ++i) 621 { 622 out.print( "-" ); 623 } 624 out.println( "-" ); 625 626 return index - 1; 627 } 628 629 630 protected String indexCode( int i ) 631 { 632 return ""+(char)('A' + i - 1); 633 } 634 635 636 protected String format8( int i ) 637 { 638 StringBuffer sb = new StringBuffer (); 639 for (int top = 1000000; top > 1 && i < top; top /= 10) 640 { 641 sb.append( ' ' ); 642 } 643 sb.append( i ); 644 return sb.toString(); 645 } 646 647 648 protected String [] formatWidth( String t, int width ) 649 { 650 List ret = new ArrayList (); 651 while (t.length() > width) 652 { 653 ret.add( t.substring( 0, width ) ); 654 t = t.substring( width ); 655 } 656 StringBuffer sb = new StringBuffer ( t ); 657 while (sb.length() < width) 658 { 659 sb.append( " " ); 660 } 661 ret.add( sb.toString() ); 662 return (String [])ret.toArray( new String [ ret.size() ] ); 663 } 664 665 666 protected void printCodeException( String type, CodeException[] ce, int pos, 667 ConstantPool cp, PrintStream out ) 668 { 669 for (int i = 0; i < ce.length; ++i) 670 { 671 if (pos == ce[i].getStartPC()) 672 { 673 out.println( " { " + type + 674 ": start try block for exception " + 675 getClassName( cp, ce[i].getCatchType() ) ); 676 } 677 if (pos == ce[i].getEndPC()) 678 { 679 out.println( " } " + type + 680 ": end try block for exception " + 681 getClassName( cp, ce[i].getCatchType() ) ); 682 } 683 if (pos == ce[i].getHandlerPC()) 684 { 685 out.println( " > " + type + 686 ": exception handler block for " + 687 getClassName( cp, ce[i].getCatchType() ) ); 688 } 689 } 690 } 691 692 693 protected void printSideBySide( String left, String right, int width, 694 String sep, String indent, PrintStream out ) 695 { 696 if (left == null) 697 { 698 left = ""; 699 } 700 if (right == null) 701 { 702 right = ""; 703 } 704 String blank = formatWidth( "", width )[0]; 705 String fl[] = formatWidth( left, width ); 706 String rl[] = formatWidth( right, width ); 707 int len = (fl.length > rl.length) ? fl.length : rl.length; 708 for (int i = 0; i < len; ++i) 709 { 710 out.print( indent ); 711 if (i >= fl.length) 712 { 713 out.print( blank ); 714 } 715 else 716 { 717 out.print( fl[i] ); 718 } 719 out.print( sep ); 720 if (i >= rl.length) 721 { 722 out.println( blank ); 723 } 724 else 725 { 726 out.println( rl[i] ); 727 } 728 } 729 } 730 731 732 protected String getClassName( ConstantPool cp, int typeIndex ) 733 { 734 String name; 735 try 736 { 737 name = org.apache.bcel.classfile.Utility. 738 compactClassName( cp.getConstantString( typeIndex, 739 org.apache.bcel.Constants.CONSTANT_Class), false ); 740 } 741 catch (org.apache.bcel.classfile.ClassFormatException ex) 742 { 743 name = "<unknown: "+typeIndex+">"; 745 } 746 return name; 747 } 748 } | Popular Tags |