1 34 package jarg; 35 36 import java.io.*; 37 import java.util.*; 38 import java.util.jar.*; 39 import java.util.zip.*; 40 41 import org.apache.bcel.classfile.*; 42 import org.apache.bcel.generic.*; 43 import org.apache.bcel.Constants; 44 45 55 public class Jarg { 56 private boolean isPrintVersion = false; 57 private boolean isShowTime = false; 58 private String renameLogFileName; 59 private String jarFile; 60 61 boolean isVerbose = false; 62 boolean isVerboseRN = false; 63 boolean isVerboseBCO = false; 64 boolean isVerboseUCI = false; 65 boolean isVerboseUFM = false; 66 boolean isVerboseAll = false; 67 boolean isShowStat = false; 68 69 private boolean isCompressed = true; 70 private boolean isRemoveDirectryEntry = true; 71 boolean isRemoveLocalVariable = true; 72 boolean isRemoveLineNumber = true; 73 boolean isRemoveSourceFile = true; 74 boolean isRemoveSynthetic = true; 75 boolean isRemoveInnerClasses = true; 76 boolean isRemoveExceptions = true; 77 boolean isRenameClass = true; 78 boolean isRenameField = true; 79 boolean isRenameMethod = true; 80 boolean isByteCodeOptimizing = true; 81 ArrayList main_class = new ArrayList(); 82 83 boolean isJ2ME = false; 84 String j2me_prev; 85 String j2me_cp; 86 87 Statistics statistics = new Statistics(); 88 89 private Set _excpPathes = new TreeSet(); 91 String [] excpPathes; 92 Set excpPackages = new HashSet(); 93 Set excpClasses = new HashSet(); 94 Set excpFields = new HashSet(); 95 Set excpMethods = new HashSet(); 96 97 PrintStream renameLog; 99 100 private static void printVersion() { 101 System.out.println("jarg - Java Archive Grinder (jarg) - ver 0.9.15-pre1"); 102 } 103 104 private static void printUsage() { 105 printVersion(); 106 System.out.println("[usage] java -jar jarg.jar {options} xxxx.jar"); 107 System.out.println(" options"); 108 System.out.println(" -version : show version"); 109 System.out.println(" -verbose : verbose mode"); 110 System.out.println(" -verbosern : verbose of renaming"); 111 System.out.println(" -verboseuci : verbose of removing unused classes and interfaces"); 112 System.out.println(" -verboseufm : verbose of removing unused fields and methods"); 113 System.out.println(" -verbosebco : verbose of byte-code optimizing"); 114 System.out.println(" -verboseall : verbose all"); 115 116 System.out.println(" -showstat : show statistics"); 117 System.out.println(" -showtime : show time"); 118 119 System.out.println(" -main : main class name. "); 120 System.out.println(" if this option is specified, unused classes are removed."); 121 System.out.println(" These one or more options can be specified."); 122 123 System.out.println(" -nocomp : no commpressed output jar file"); 124 125 System.out.println(" -normdir : no remove directry entry in jar file"); 126 System.out.println(" -normlv : no remove local variable"); 127 System.out.println(" -normln : no remove line number"); 128 System.out.println(" -normsf : no remove source file"); 129 System.out.println(" -normsy : no remove 'Synthetic'"); 130 System.out.println(" -normin : no remove 'InnerClasses'"); 131 System.out.println(" -normex : no remove 'Exceptions'"); 132 133 System.out.println(" -nornc : no rename class"); 134 System.out.println(" -nornf : no rename field"); 135 System.out.println(" -nornm : no rename method"); 136 137 System.out.println(" -nobco : no byte-code optimizing"); 138 139 System.out.println(" -excpp @file : list of excepting package"); 140 System.out.println(" -excpc @file : list of excepting class"); 141 System.out.println(" -excpf @file : list of excepting field"); 142 System.out.println(" -excpm @file : list of excepting method"); 143 System.out.println(" -rnlog file : rename log file"); 144 145 System.out.println(" -j2me-prev preverify : preverify command path"); 146 System.out.println(" -j2me-cp classpath : preverify class path"); 147 System.out.println(); 148 System.out.println(" remove class example > java -jar jarg.jar -main path.to.class -main path.to.class2"); 149 System.out.println(" j2me example > java -jar jarg.jar -j2me-prev c:\\j2mewtk\\bin\\preverify.exe -j2me-cp c:\\j2mewtk\\lib\\midpapi.zip xxx.jar"); 150 } 151 152 Jarg() { 153 setupExcepting(); 154 } 155 156 private void setupExcepting() { 157 _excpPathes.add("java.lang"); 159 _excpPathes.add("java.security"); 160 excpClasses.add("sun.awt.font.StandardGlyphVector"); 164 excpClasses.add("java.io.ObjectStreamField"); 165 excpClasses.add("java.io.File"); 166 excpClasses.add("java.io.Win32FileSystem"); 167 } 168 169 private void fixupExcpPathes() { 170 excpPathes = new String [_excpPathes.size()]; 171 _excpPathes.toArray(excpPathes); 172 _excpPathes = null; 173 } 174 175 private void readExcpPackagFile(String fnm) throws FileNotFoundException, IOException { 176 FileReader frd = new FileReader(fnm); 177 LineNumberReader rd = new LineNumberReader(frd); 178 String line; 179 while ((line = rd.readLine()) != null) { 180 line = line.trim(); 181 if (line.startsWith("#")) { 182 } else if (line.endsWith(".*")) { 184 _excpPathes.add(line.substring(0, line.length()-2)); 185 } else { 186 excpPackages.add(line); 187 } 188 } 189 rd.close(); 190 frd.close(); 191 } 192 193 private List readExcpFile(String fnm) throws FileNotFoundException, IOException { 194 List lst = new ArrayList(); 195 FileReader frd = new FileReader(fnm); 196 LineNumberReader rd = new LineNumberReader(frd); 197 String line; 198 while ((line = rd.readLine()) != null) { 199 line = line.trim(); 200 if (line.startsWith("#")) { 201 } else { 203 lst.add(line); 204 } 205 } 206 rd.close(); 207 frd.close(); 208 return lst; 209 } 210 211 private boolean setOptions(String [] args) throws FileNotFoundException, IOException { 212 for (int i=0; i < args.length; i++) { 213 String arg = args[i]; 214 if (arg.startsWith("-")) { 215 if (arg.equals("-version")) { 216 this.isPrintVersion = true; 217 return false; 218 } else if (arg.equals("-verbose")) { 219 this.isVerbose = true; 220 this.isShowStat = true; 221 } else if (arg.equals("-verbosern")) { 222 this.isVerboseRN = true; 223 } else if (arg.equals("-verboseuci")) { 224 this.isVerboseUCI = true; 225 } else if (arg.equals("-verboseufm")) { 226 this.isVerboseUFM = true; 227 } else if (arg.equals("-verbosebco")) { 228 this.isVerboseBCO = true; 229 } else if (arg.equals("-verboseall")) { 230 this.isVerbose = true; 231 this.isVerboseRN = true; 232 this.isVerboseUCI = true; 233 this.isVerboseUFM = true; 234 this.isVerboseBCO = true; 235 this.isVerboseAll = true; 236 this.isShowStat = true; 237 } else if (arg.equals("-showstat")) { 238 this.isShowStat = true; 239 } else if (arg.equals("-showtime")) { 240 this.isShowTime = true; 241 } else if (arg.equals("-nocomp")) { 242 this.isCompressed = false; 243 } else if (arg.equals("-normdir")) { 244 this.isRemoveDirectryEntry = false; 245 } else if (arg.equals("-normlv")) { 246 this.isRemoveLocalVariable = false; 247 this.isRemoveSourceFile = false; 248 } else if (arg.equals("-normln")) { 249 this.isRemoveLineNumber = false; 250 this.isRemoveSourceFile = false; 251 } else if (arg.equals("-normsf")) { 252 this.isRemoveSourceFile = false; 253 } else if (arg.equals("-normsy")) { 254 this.isRemoveSynthetic = false; 255 } else if (arg.equals("-normin")) { 256 this.isRemoveInnerClasses = false; 257 } else if (arg.equals("-normex")) { 258 this.isRemoveExceptions = false; 259 } else if (arg.equals("-nornc")) { 260 this.isRenameClass = false; 261 } else if (arg.equals("-nornf")) { 262 this.isRenameField = false; 263 } else if (arg.equals("-nornm")) { 264 this.isRenameMethod = false; 265 } else if (arg.equals("-nobco")) { 266 this.isByteCodeOptimizing = false; 267 } else if (arg.equals("-main")) { 268 i++; 269 if (i < args.length) { 270 this.main_class.add(args[i]); 271 } 272 } else if (arg.equals("-excpp")) { 273 i++; 274 if (i < args.length && args[i].startsWith("@")) { 275 this.readExcpPackagFile(args[i].substring(1)); 276 } else { 277 System.out.println("illigal option : " + arg + " : need @"); 278 printUsage(); 279 return false; 280 } 281 } else if (arg.equals("-excpc")) { 282 i++; 283 if (i < args.length && args[i].startsWith("@")) { 284 List lst = this.readExcpFile(args[i].substring(1)); 285 this.excpClasses.addAll(lst); 286 } else { 287 System.out.println("illigal option : " + arg + " : need @"); 288 printUsage(); 289 return false; 290 } 291 } else if (arg.equals("-excpf")) { 292 i++; 293 if (i < args.length && args[i].startsWith("@")) { 294 List lst = this.readExcpFile(args[i].substring(1)); 295 this.excpFields.addAll(lst); 296 } else { 297 System.out.println("illigal option : " + arg + " : need @"); 298 printUsage(); 299 return false; 300 } 301 } else if (arg.equals("-excpm")) { 302 i++; 303 if (i < args.length && args[i].startsWith("@")) { 304 List lst = this.readExcpFile(args[i].substring(1)); 305 this.excpMethods.addAll(lst); 306 } else { 307 System.out.println("illigal option : " + arg + " : need @"); 308 printUsage(); 309 return false; 310 } 311 } else if (arg.equals("-rnlog")) { 312 i++; 313 if (i < args.length) { 314 this.renameLogFileName = args[i]; 315 } else { 316 System.out.println("illigal option : " + arg + " : need a file name"); 317 printUsage(); 318 return false; 319 } 320 } else if (arg.equals("-j2me-prev")) { 321 i++; 322 if (i < args.length) { 323 this.j2me_prev = args[i]; 324 this.isJ2ME = true; 325 } else { 326 System.out.println("illigal option : " + arg + " : need a command path to preverify"); 327 printUsage(); 328 return false; 329 } 330 } else if (arg.equals("-j2me-cp")) { 331 i++; 332 if (i < args.length) { 333 this.j2me_cp = args[i]; 334 this.isJ2ME = true; 335 } else { 336 System.out.println("illigal option : " + arg + " : need a class path for preverify"); 337 printUsage(); 338 return false; 339 } 340 } else { 341 System.out.println("illigal option : " + arg); 342 printUsage(); 343 return false; 344 } 345 } else if (arg.endsWith(".jar")) { 346 this.jarFile = args[i]; 347 } 348 } 349 return true; 350 } 351 352 public static void main(String [] args) { 353 Jarg rn = new Jarg(); 354 try { 355 if (!rn.setOptions(args)) { 356 return; 357 } 358 if (rn.isJ2ME) { 359 if (rn.j2me_prev == null || rn.j2me_cp == null) { 360 System.out.println("illigal option for j2me: need 2 options : -j2me-prev -j2me-cp"); 361 printUsage(); 362 } 363 } 364 if (rn.jarFile != null) { 365 if (rn.renameLogFileName != null) { 366 FileOutputStream logout = new FileOutputStream(rn.renameLogFileName); 367 rn.renameLog = new PrintStream(logout); 368 rn.renameLog.println("jarg : " + rn.jarFile); 369 rn.renameLog.println(); 370 } 371 long st = System.currentTimeMillis(); 372 rn.fixupExcpPathes(); 373 rn.procJarFile(rn.jarFile); 374 long ed = System.currentTimeMillis(); 375 if (rn.isShowTime) { 376 System.out.println("" + (ed - st) + "[msec]"); 377 } 378 if (rn.renameLog != null) { 379 rn.renameLog.close(); 380 } 381 if (rn.isShowStat) { 382 rn.statistics.print(); 383 } 384 } else { 385 printUsage(); 386 } 387 } catch(Exception e) { 388 System.err.println(e.getMessage()); 389 e.printStackTrace(System.err); 390 } 391 } 392 393 private List metainf = new ArrayList(); 394 private List image = new ArrayList(); 395 396 private void procJarFile(String jarfn) throws IOException { 397 File jarf = new File(jarfn); 399 if (!jarf.exists()) { 400 System.err.println("File not found : " + jarfn); 401 return; 402 } 403 JarFile jarin = new JarFile(jarf); 404 405 String jarfn2 = jarfn.substring(0, jarfn.length()-4) + "_s.jar"; 407 File jarfout = new File(jarfn2); 408 if (jarfout.exists()) { 409 jarfout.delete(); 410 } 411 FileOutputStream fout = new FileOutputStream(jarfout); 412 JarOutputStream jarout = new JarOutputStream(fout); 413 414 Object [][] zess = getEntries(jarin); 416 417 ZipEntry[] zes = (ZipEntry[])zess[0]; 419 { 420 Set storedSet = new HashSet(); 421 for (int i=0; i<zes.length; i++) { 422 ZipEntry ze = zes[i]; 423 String zeNm = ze.getName(); 424 if (storedSet.contains(zeNm)) { 425 continue; 426 } 427 byte[] buf = null; 428 if (ze.isDirectory()) { 429 if (!isRemoveDirectryEntry && zeNm != null & zeNm.length() > 0) { 430 outJarEntry(jarout, zeNm, null); 431 if (this.isVerbose) { 432 System.out.println(zeNm); 433 } 434 } 435 } else { 436 buf = inJarEntry2(jarin, ze); 437 outJarEntry(jarout, zeNm, buf); 438 if (this.isVerbose) { 439 System.out.println(zeNm); 440 } 441 } 442 storedSet.add(zeNm); 443 } 444 storedSet.clear(); 445 storedSet = null; 446 } 447 448 if (this.isVerbose) { 450 System.out.println("Reading class files ..."); 451 } 452 List imglst = new ArrayList(); 453 PackageCollection pkgcol = new PackageCollection(this); 454 455 DirectoryHandler[] dhs = (DirectoryHandler[])zess[1]; 456 for (int i=0; i<dhs.length; i++) { 457 DirectoryHandler dh = dhs[i]; 458 459 PackageHandler pkgh = new PackageHandler(this, dh.path); 460 461 for (int j=0; j<dh.zes.length; j++) { 463 ZipEntry ze = dh.zes[j]; 464 if (ze.isDirectory()) { 465 outJarEntry(jarout, ze.getName(), null); 466 if (this.isVerboseAll) { 467 System.out.println(ze.getName()); 468 } 469 } else if (ze.getName().endsWith(".class")) { 470 byte[] buf = inJarEntry2(jarin, ze); 471 ClassHandler clsh = new ClassHandler(this, ze.getName(), buf); 472 pkgh.addClassHandler(clsh); 473 } else { 474 byte[] buf = inJarEntry2(jarin, ze); 475 ImageHandler imh = new ImageHandler(this, ze.getName(), buf); 476 imglst.add(imh); 477 } 478 } 479 480 pkgcol.addPackageHandler(pkgh); 481 } 482 jarin.close(); 483 484 if (main_class.size() > 0) { 486 if (this.isVerbose) { 487 System.out.println("Scan class files for checking used classes ..."); 488 } 489 pkgcol.doCheckUsedClass(); 490 for (int i=0; i<main_class.size(); i++) { 491 pkgcol.markUsedClass(((String )main_class.get(i)).replace('.','/')); 492 } 493 } 494 495 if (this.isVerbose) { 497 System.out.println("Optimizing class files ..."); 498 } 499 pkgcol.doOptimize(); 500 501 File outdir2 = new File(".jarg.me.tmp.2"); 503 if (isJ2ME) { 504 outdir2.mkdir(); 505 procJ2ME(outdir2, pkgcol); 506 } 507 508 if (!isRemoveDirectryEntry) { 510 for (int i=0; i<dhs.length; i++) { 511 DirectoryHandler dh = dhs[i]; 512 513 if (dh.path != null & dh.path.length() > 0) { 514 outJarEntry(jarout, dh.path, null); 515 if (this.isVerbose) { 516 System.out.println(dh.path); 517 } 518 } 519 } 520 } 521 522 if (this.isVerbose) { 524 System.out.println("Writing class files ..."); 525 } 526 Iterator pit = pkgcol.packages.keySet().iterator(); 527 while (pit.hasNext()) { 528 String pkey = (String )pit.next(); 529 PackageHandler pkgh = (PackageHandler)pkgcol.packages.get(pkey); 530 531 Iterator cit = pkgh.classes.keySet().iterator(); 533 while (cit.hasNext()) { 534 String ckey = (String )cit.next(); 535 ClassHandler clsh = (ClassHandler)pkgh.classes.get(ckey); 536 if (main_class.size() > 0 && !clsh.isUsed) { 537 if (this.isVerboseUCI) { 538 System.out.println("" + clsh.filename + " is removed"); 539 } 540 } else { 541 clsh.setupNewStatistics(); 542 byte[] buf = clsh.getBytes(); 543 this.statistics.addNewClassFileSize(buf.length); 544 545 try { 546 byte[] buff; 547 if (isJ2ME) { 548 buff = inClassFile(new File(outdir2, clsh.filename)); 549 } else { 550 buff = clsh.getBytes(); 551 } 552 outJarEntry(jarout, clsh.filename, buff); 553 } catch (IOException ex) { 554 jarout.close(); 555 fout.close(); 556 throw ex; 557 } 558 559 if (this.isVerbose) { 560 int cmp = (int)((100.0 * buf.length)/(double)clsh.orgsize); 561 System.out.println("" + clsh.filename + " (" + cmp + "%)"); 562 } 563 } 564 } 565 } 566 567 if (this.isVerbose) { 569 System.out.println("Writing other files ..."); 570 } 571 Iterator iit = imglst.iterator(); 572 while (iit.hasNext()) { 573 ImageHandler imh = (ImageHandler)iit.next(); 574 outJarEntry(jarout, imh.filename, imh.getBytes()); 575 if (this.isVerbose) { 576 System.out.println(imh.filename); 577 } 578 } 579 580 jarout.close(); 581 fout.close(); 582 if (isJ2ME) { 583 deleteDirs(outdir2); 584 } 585 } 586 587 private Object [][] getEntries(JarFile jarin) { 588 List lstmt = new ArrayList(); 589 Map map = new HashMap(); 590 List lst = new ArrayList(); 591 592 Enumeration en = jarin.entries(); 593 while (en.hasMoreElements()) { 594 ZipEntry ze = (ZipEntry)en.nextElement(); 596 if (ze.getName().startsWith("META-INF/")) { 597 lstmt.add(ze); 598 } else { 599 String path = ze.getName(); 600 if (!ze.isDirectory()) { 601 int idx = path.lastIndexOf('/'); 602 if (idx < 0) { 603 path = ""; 604 } else { 605 path = path.substring(0, idx+1); 606 } 607 } 608 DirectoryHandler dh = (DirectoryHandler)map.get(path); 609 if (dh == null) { 610 dh = new DirectoryHandler(this, path, jarin); 611 map.put(path, dh); 612 lst.add(dh); 613 } 614 if (!ze.isDirectory()) { 615 dh.addFile(ze); 616 } 617 } 618 } 619 620 ZipEntry[] zesmt = new ZipEntry[lstmt.size()]; 622 lstmt.toArray(zesmt); 623 624 DirectoryHandler[] dhs = new DirectoryHandler[lst.size()]; 626 lst.toArray(dhs); 627 Arrays.sort(dhs); 628 for (int j=0; j<dhs.length; j++) { 629 DirectoryHandler dh = dhs[j]; 630 dh.fixEntry(); 631 } 632 633 Object [][] r = new Object [2][]; 635 r[0] = zesmt; 636 r[1] = dhs; 637 638 return r; 639 } 640 641 private byte[] inJarEntry2(JarFile jarin, ZipEntry ze) throws IOException { 642 long sz = ze.getSize(); 643 649 byte[] buf = new byte[(int)sz]; 651 InputStream zein = jarin.getInputStream(ze); 652 DataInputStream din = new DataInputStream(zein); 653 din.readFully(buf); 654 din.close(); 655 zein.close(); 656 657 return buf; 658 } 659 660 private void outJarEntry(JarOutputStream jarout, String filename, byte[] buf) throws IOException { 661 ZipEntry ze = new ZipEntry(filename); 663 CRC32 crc32 = new CRC32(); 664 int zeM = (this.isCompressed) ? ZipEntry.DEFLATED : ZipEntry.STORED; 665 666 ze.setMethod(zeM); 667 ze.setCompressedSize(-1); 668 if (buf == null) { 669 ze.setSize(0); 670 ze.setCrc(0); 671 } else { 672 ze.setSize(buf.length); 673 crc32.reset(); 674 crc32.update(buf); 675 ze.setCrc(crc32.getValue()); 676 } 677 jarout.putNextEntry(ze); 678 679 if (buf != null) { 681 if (this.isCompressed) { 682 jarout.setMethod(ZipOutputStream.DEFLATED); 683 jarout.setLevel(Deflater.BEST_COMPRESSION); 684 } 685 jarout.write(buf); 686 } 687 jarout.closeEntry(); 688 } 689 690 private void procJ2ME(File outdir2, PackageCollection pkgcol) throws IOException { 691 File outdir = new File(".jarg.me.tmp"); 692 outdir.mkdir(); 693 694 procJ2MEout(pkgcol, outdir); 695 696 String [] cmdarray = { 699 j2me_prev, 700 "-classpath", j2me_cp + File.pathSeparator + outdir.getPath(), 701 "-d", outdir2.getPath(), 702 outdir.getPath() 703 }; 704 705 Process proc = Runtime.getRuntime().exec(cmdarray); 706 InputStream ein = new BufferedInputStream(proc.getErrorStream()); 707 int r = 0; 708 try { 709 r = proc.waitFor(); 710 } catch (InterruptedException ex) { 711 } 712 for (;;) { 713 int i = ein.read(); 714 if (i < 0) { 715 break; 716 } 717 System.err.write(i); 718 } 719 ein.close(); 720 721 deleteDirs(outdir); 722 723 if (r != 0) { 724 throw new IOException("not exec preverify command."); 726 } 727 } 728 729 private void deleteDirs(File dir) throws IOException { 730 if (dir.isDirectory()) { 731 File[] children = dir.listFiles(); 732 for (int i=0; i<children.length; i++) { 733 deleteDirs(children[i]); 734 } 735 } 736 dir.delete(); 737 } 738 739 private void procJ2MEout(PackageCollection pkgcol, File outdir) throws IOException { 740 Iterator pit = pkgcol.packages.keySet().iterator(); 741 while (pit.hasNext()) { 742 String pkey = (String )pit.next(); 743 PackageHandler pkgh = (PackageHandler)pkgcol.packages.get(pkey); 744 745 Iterator cit = pkgh.classes.keySet().iterator(); 747 while (cit.hasNext()) { 748 String ckey = (String )cit.next(); 749 ClassHandler clsh = (ClassHandler)pkgh.classes.get(ckey); 750 byte[] buf = clsh.getBytes(); 751 752 try { 753 outClassFile(outdir, clsh.filename, clsh.getBytes()); 754 } catch (IOException ex) { 755 throw ex; 756 } 757 } 758 } 759 } 760 761 private void outClassFile(File baseDir, String filepath, byte[] data) throws IOException { 762 File newfile = new File(baseDir, filepath); 763 File parent = newfile.getParentFile(); 764 if (!parent.exists()) { 765 parent.mkdirs(); 766 } 767 OutputStream out = new BufferedOutputStream(new FileOutputStream(newfile)); 768 out.write(data); 769 out.close(); 770 } 771 772 private byte[] inClassFile(File infile) throws IOException { 773 byte[] buff = new byte[(int)(infile.length())]; 774 InputStream in = new BufferedInputStream(new FileInputStream(infile)); 775 DataInputStream din = new DataInputStream(in); 776 din.readFully(buff); 777 din.close(); 778 return buff; 779 } 780 } 781 | Popular Tags |