1 30 package org.objectweb.asm.xml; 31 32 import java.io.BufferedOutputStream ; 33 import java.io.ByteArrayInputStream ; 34 import java.io.ByteArrayOutputStream ; 35 import java.io.FileInputStream ; 36 import java.io.FileOutputStream ; 37 import java.io.IOException ; 38 import java.io.InputStream ; 39 import java.io.OutputStream ; 40 import java.io.OutputStreamWriter ; 41 import java.io.Writer ; 42 import java.util.zip.ZipEntry ; 43 import java.util.zip.ZipInputStream ; 44 import java.util.zip.ZipOutputStream ; 45 46 import javax.xml.transform.Source ; 47 import javax.xml.transform.Templates ; 48 import javax.xml.transform.TransformerConfigurationException ; 49 import javax.xml.transform.TransformerException ; 50 import javax.xml.transform.TransformerFactory ; 51 import javax.xml.transform.sax.SAXResult ; 52 import javax.xml.transform.sax.SAXSource ; 53 import javax.xml.transform.sax.SAXTransformerFactory ; 54 import javax.xml.transform.sax.TransformerHandler ; 55 import javax.xml.transform.stream.StreamSource ; 56 57 import org.objectweb.asm.ClassReader; 58 59 import org.xml.sax.Attributes ; 60 import org.xml.sax.ContentHandler ; 61 import org.xml.sax.InputSource ; 62 import org.xml.sax.SAXException ; 63 import org.xml.sax.XMLReader ; 64 import org.xml.sax.ext.LexicalHandler ; 65 import org.xml.sax.helpers.AttributesImpl ; 66 import org.xml.sax.helpers.DefaultHandler ; 67 import org.xml.sax.helpers.XMLReaderFactory ; 68 69 90 public class Processor { 91 92 public static final int BYTECODE = 1; 93 94 public static final int MULTI_XML = 2; 95 96 public static final int SINGLE_XML = 3; 97 98 private static final String SINGLE_XML_NAME = "classes.xml"; 99 100 private int inRepresentation; 101 102 private int outRepresentation; 103 104 private InputStream input = null; 105 106 private OutputStream output = null; 107 108 private Source xslt = null; 109 110 private boolean computeMax; 111 112 private int n = 0; 113 114 public Processor( 115 int inRepresenation, 116 int outRepresentation, 117 InputStream input, 118 OutputStream output, 119 Source xslt) 120 { 121 this.inRepresentation = inRepresenation; 122 this.outRepresentation = outRepresentation; 123 this.input = input; 124 this.output = output; 125 this.xslt = xslt; 126 this.computeMax = true; 127 } 128 129 public int process() throws TransformerException , IOException , SAXException 130 { 131 ZipInputStream zis = new ZipInputStream (input); 132 final ZipOutputStream zos = new ZipOutputStream (output); 133 final OutputStreamWriter osw = new OutputStreamWriter (zos); 134 135 Thread.currentThread() 136 .setContextClassLoader(getClass().getClassLoader()); 137 138 TransformerFactory tf = TransformerFactory.newInstance(); 139 if (!tf.getFeature(SAXSource.FEATURE) 140 || !tf.getFeature(SAXResult.FEATURE)) 141 return 0; 142 143 SAXTransformerFactory saxtf = (SAXTransformerFactory ) tf; 144 Templates templates = null; 145 if (xslt != null) { 146 templates = saxtf.newTemplates(xslt); 147 } 148 149 152 EntryElement entryElement = getEntryElement(zos); 153 154 ContentHandler outDocHandler = null; 155 switch (outRepresentation) { 156 case BYTECODE: 157 outDocHandler = new OutputSlicingHandler(new ASMContentHandlerFactory(zos, 158 computeMax), 159 entryElement, 160 false); 161 break; 162 163 case MULTI_XML: 164 outDocHandler = new OutputSlicingHandler(new SAXWriterFactory(osw, 165 true), 166 entryElement, 167 true); 168 break; 169 170 case SINGLE_XML: 171 ZipEntry outputEntry = new ZipEntry (SINGLE_XML_NAME); 172 zos.putNextEntry(outputEntry); 173 outDocHandler = new SAXWriter(osw, false); 174 break; 175 176 } 177 178 ContentHandler inDocHandler = null; 181 if (templates == null) { 182 inDocHandler = outDocHandler; 183 } else { 184 inDocHandler = new InputSlicingHandler("class", 185 outDocHandler, 186 new TransformerHandlerFactory(saxtf, 187 templates, 188 outDocHandler)); 189 } 190 ContentHandlerFactory inDocHandlerFactory = new SubdocumentHandlerFactory(inDocHandler); 191 192 if (inDocHandler != null && inRepresentation != SINGLE_XML) { 193 inDocHandler.startDocument(); 194 inDocHandler.startElement("", 195 "classes", 196 "classes", 197 new AttributesImpl ()); 198 } 199 200 int i = 0; 201 ZipEntry ze = null; 202 while ((ze = zis.getNextEntry()) != null) { 203 update(ze.getName(), n++); 204 if (isClassEntry(ze)) { 205 processEntry(zis, ze, inDocHandlerFactory); 206 } else { 207 OutputStream os = entryElement.openEntry(getName(ze)); 208 copyEntry(zis, os); 209 entryElement.closeEntry(); 210 } 211 212 i++; 213 } 214 215 if (inDocHandler != null && inRepresentation != SINGLE_XML) { 216 inDocHandler.endElement("", "classes", "classes"); 217 inDocHandler.endDocument(); 218 } 219 220 if (outRepresentation == SINGLE_XML) { 221 zos.closeEntry(); 222 } 223 zos.flush(); 224 zos.close(); 225 226 return i; 227 } 228 229 private void copyEntry(InputStream is, OutputStream os) throws IOException { 230 if (outRepresentation == SINGLE_XML) 231 return; 232 233 byte[] buff = new byte[2048]; 234 int i; 235 while ((i = is.read(buff)) != -1) { 236 os.write(buff, 0, i); 237 } 238 } 239 240 private boolean isClassEntry(ZipEntry ze) { 241 String name = ze.getName(); 242 return inRepresentation == SINGLE_XML && name.equals(SINGLE_XML_NAME) 243 || name.endsWith(".class") || name.endsWith(".class.xml"); 244 } 245 246 private void processEntry( 247 final ZipInputStream zis, 248 ZipEntry ze, 249 ContentHandlerFactory handlerFactory) 250 { 251 ContentHandler handler = handlerFactory.createContentHandler(); 252 try { 253 254 261 boolean singleInputDocument = inRepresentation == SINGLE_XML; 262 if (inRepresentation == BYTECODE) { ClassReader cr = new ClassReader(readEntry(zis, ze)); 265 cr.accept(new SAXClassAdapter(handler, singleInputDocument), 266 false); 267 268 } else { XMLReader reader = XMLReaderFactory.createXMLReader(); 270 reader.setContentHandler(handler); 271 reader.parse(new InputSource (singleInputDocument 272 ? (InputStream ) new ProtectedInputStream(zis) 273 : new ByteArrayInputStream (readEntry(zis, ze)))); 274 275 } 276 } catch (Exception ex) { 277 update(ze.getName(), 0); 278 update(ex, 0); 279 } 280 } 281 282 private EntryElement getEntryElement(ZipOutputStream zos) { 283 if (outRepresentation == SINGLE_XML) { 284 return new SingleDocElement(zos); 285 } 286 return new ZipEntryElement(zos); 287 } 288 289 319 private String getName(ZipEntry ze) { 320 String name = ze.getName(); 321 if (isClassEntry(ze)) { 322 if (inRepresentation != BYTECODE && outRepresentation == BYTECODE) { 323 name = name.substring(0, name.length() - 4); } else if (inRepresentation == BYTECODE 326 && outRepresentation != BYTECODE) 327 { 328 name = name.concat(".xml"); } 330 } 333 return name; 334 } 335 336 private byte[] readEntry(ZipInputStream zis, ZipEntry ze) 337 throws IOException 338 { 339 long size = ze.getSize(); 340 if (size > -1) { 341 byte[] buff = new byte[(int) size]; 342 zis.read(buff); 347 return buff; 348 } 349 350 ByteArrayOutputStream bos = new ByteArrayOutputStream (); 351 byte[] buff = new byte[4096]; 352 int i; 353 while ((i = zis.read(buff)) != -1) { 354 bos.write(buff, 0, i); 355 } 356 return bos.toByteArray(); 357 } 358 359 364 protected void update(Object arg, int n) { 365 if (arg instanceof Throwable ) { 366 ((Throwable ) arg).printStackTrace(); 367 } else { 368 if ((n % 100) == 0) { 369 System.err.println(n + " " + arg); 370 } 371 } 372 } 373 374 public static void main(String [] args) throws Exception { 375 if (args.length < 2) { 376 showUsage(); 377 return; 378 } 379 380 int inRepresentation = getRepresentation(args[0]); 381 int outRepresentation = getRepresentation(args[1]); 382 383 InputStream is = null; 384 OutputStream os = null; 385 386 Source xslt = null; 387 389 for (int i = 2; i < args.length; i++) { 390 if ("-in".equals(args[i])) { 391 is = new FileInputStream (args[++i]); 392 393 } else if ("-out".equals(args[i])) { 394 os = new BufferedOutputStream (new FileOutputStream (args[++i])); 395 396 } else if ("-xslt".equals(args[i])) { 397 xslt = new StreamSource (new FileInputStream (args[++i])); 398 399 402 } else { 403 showUsage(); 404 return; 405 406 } 407 } 408 409 if (is == null || os == null || inRepresentation == 0 410 || outRepresentation == 0) 411 { 412 showUsage(); 413 return; 414 } 415 416 Processor m = new Processor(inRepresentation, 417 outRepresentation, 418 is, 419 os, 420 xslt); 421 422 long l1 = System.currentTimeMillis(); 423 int n = m.process(); 424 long l2 = System.currentTimeMillis(); 425 System.err.println(n); 426 System.err.println("" + (l2 - l1) + "ms " + (1000f * n / (l2 - l1)) 427 + " resources/sec"); 428 } 429 430 private static int getRepresentation(String s) { 431 if ("code".equals(s)) { 432 return BYTECODE; 433 } else if ("xml".equals(s)) { 434 return MULTI_XML; 435 } else if ("singlexml".equals(s)) { 436 return SINGLE_XML; 437 } 438 return 0; 439 } 440 441 private static void showUsage() { 442 System.err.println("Usage: Main <in format> <out format> -in <input jar> -out <output jar> [-xslt <xslt fiel>]"); 443 System.err.println(" <in format> and <out format> - code | xml | singlexml"); 444 } 445 446 450 private static final class ProtectedInputStream extends InputStream { 451 private final InputStream is; 452 453 private ProtectedInputStream(InputStream is) { 454 super(); 455 this.is = is; 456 } 457 458 public final void close() throws IOException { 459 } 460 461 public final int read() throws IOException { 462 return is.read(); 463 } 464 465 public final int read(byte[] b, int off, int len) throws IOException { 466 return is.read(b, off, len); 467 } 468 469 public final int available() throws IOException { 470 return is.available(); 471 } 472 } 473 474 479 private static interface ContentHandlerFactory { 480 481 486 ContentHandler createContentHandler(); 487 488 } 489 490 493 private static final class SAXWriterFactory implements 494 ContentHandlerFactory 495 { 496 private Writer w; 497 498 private boolean optimizeEmptyElements; 499 500 public SAXWriterFactory(Writer w, boolean optimizeEmptyElements) { 501 this.w = w; 502 this.optimizeEmptyElements = optimizeEmptyElements; 503 } 504 505 public final ContentHandler createContentHandler() { 506 return new SAXWriter(w, optimizeEmptyElements); 507 } 508 509 } 510 511 514 private static final class ASMContentHandlerFactory implements 515 ContentHandlerFactory 516 { 517 private OutputStream os; 518 519 private boolean computeMax; 520 521 public ASMContentHandlerFactory(OutputStream os, boolean computeMax) { 522 this.os = os; 523 this.computeMax = computeMax; 524 } 525 526 public final ContentHandler createContentHandler() { 527 return new ASMContentHandler(os, computeMax); 528 } 529 530 } 531 532 535 private static final class TransformerHandlerFactory implements 536 ContentHandlerFactory 537 { 538 private SAXTransformerFactory saxtf; 539 540 private Templates templates; 541 542 private ContentHandler outputHandler; 543 544 public TransformerHandlerFactory( 545 SAXTransformerFactory saxtf, 546 Templates templates, 547 ContentHandler outputHandler) 548 { 549 this.saxtf = saxtf; 550 this.templates = templates; 551 this.outputHandler = outputHandler; 552 } 553 554 public final ContentHandler createContentHandler() { 555 try { 556 TransformerHandler handler = saxtf.newTransformerHandler(templates); 557 handler.setResult(new SAXResult (outputHandler)); 558 return handler; 559 } catch (TransformerConfigurationException ex) { 560 throw new RuntimeException (ex.toString()); 561 } 562 } 563 } 564 565 568 private final static class SubdocumentHandlerFactory implements 569 ContentHandlerFactory 570 { 571 private ContentHandler subdocumentHandler; 572 573 public SubdocumentHandlerFactory(ContentHandler subdocumentHandler) { 574 this.subdocumentHandler = subdocumentHandler; 575 } 576 577 public final ContentHandler createContentHandler() { 578 return subdocumentHandler; 579 } 580 581 } 582 583 591 private final static class SAXWriter extends DefaultHandler implements 592 LexicalHandler 593 { 594 private static final char[] OFF = " ".toCharArray(); 595 596 private Writer w; 597 598 private boolean optimizeEmptyElements; 599 600 private boolean openElement = false; 601 602 private int ident = 0; 603 604 611 public SAXWriter(Writer w, boolean optimizeEmptyElements) { 612 this.w = w; 613 this.optimizeEmptyElements = optimizeEmptyElements; 614 } 615 616 public final void startElement( 617 String ns, 618 String localName, 619 String qName, 620 Attributes atts) throws SAXException 621 { 622 try { 623 closeElement(); 624 625 writeIdent(); 626 w.write("<".concat(qName)); 627 if (atts != null && atts.getLength() > 0) 628 writeAttributes(atts); 629 630 if (!optimizeEmptyElements) { 631 w.write(">\n"); 632 } else { 633 openElement = true; 634 } 635 ident += 2; 636 637 } catch (IOException ex) { 638 throw new SAXException (ex); 639 640 } 641 } 642 643 public final void endElement(String ns, String localName, String qName) 644 throws SAXException 645 { 646 ident -= 2; 647 try { 648 if (openElement) { 649 w.write("/>\n"); 650 openElement = false; 651 } else { 652 writeIdent(); 653 w.write("</" + qName + ">\n"); 654 } 655 656 } catch (IOException ex) { 657 throw new SAXException (ex); 658 659 } 660 } 661 662 public final void endDocument() throws SAXException { 663 try { 664 w.flush(); 665 666 } catch (IOException ex) { 667 throw new SAXException (ex); 668 669 } 670 } 671 672 public final void comment(char[] ch, int off, int len) 673 throws SAXException 674 { 675 try { 676 closeElement(); 677 678 writeIdent(); 679 w.write("<!-- "); 680 w.write(ch, off, len); 681 w.write(" -->\n"); 682 683 } catch (IOException ex) { 684 throw new SAXException (ex); 685 686 } 687 } 688 689 public final void startDTD(String arg0, String arg1, String arg2) 690 throws SAXException 691 { 692 } 693 694 public final void endDTD() throws SAXException { 695 } 696 697 public final void startEntity(String arg0) throws SAXException { 698 } 699 700 public final void endEntity(String arg0) throws SAXException { 701 } 702 703 public final void startCDATA() throws SAXException { 704 } 705 706 public final void endCDATA() throws SAXException { 707 } 708 709 private final void writeAttributes(Attributes atts) throws IOException { 710 StringBuffer sb = new StringBuffer (); 711 int len = atts.getLength(); 712 for (int i = 0; i < len; i++) { 713 sb.append(" ") 714 .append(atts.getLocalName(i)) 715 .append("=\"") 716 .append(esc(atts.getValue(i))) 717 .append("\""); 718 } 719 w.write(sb.toString()); 720 } 721 722 728 private final String esc(String str) { 729 StringBuffer sb = new StringBuffer (str.length()); 730 for (int i = 0; i < str.length(); i++) { 731 char ch = str.charAt(i); 732 switch (ch) { 733 case '&': 734 sb.append("&"); 735 break; 736 737 case '<': 738 sb.append("<"); 739 break; 740 741 case '>': 742 sb.append(">"); 743 break; 744 745 case '\"': 746 sb.append("""); 747 break; 748 749 default: 750 if (ch > 0x7f) { 751 sb.append("&#") 752 .append(Integer.toString(ch)) 753 .append(';'); 754 } else { 755 sb.append(ch); 756 } 757 758 } 759 } 760 return sb.toString(); 761 } 762 763 private final void writeIdent() throws IOException { 764 int n = ident; 765 while (n > 0) { 766 if (n > OFF.length) { 767 w.write(OFF); 768 n -= OFF.length; 769 } else { 770 w.write(OFF, 0, n); 771 n = 0; 772 } 773 } 774 } 775 776 private final void closeElement() throws IOException { 777 if (openElement) { 778 w.write(">\n"); 779 } 780 openElement = false; 781 } 782 783 } 784 785 794 private final static class InputSlicingHandler extends DefaultHandler { 795 private String subdocumentRoot; 796 797 private ContentHandler rootHandler; 798 799 private ContentHandlerFactory subdocumentHandlerFactory; 800 801 private boolean subdocument = false; 802 803 private ContentHandler subdocumentHandler; 804 805 818 public InputSlicingHandler( 819 String subdocumentRoot, 820 ContentHandler rootHandler, 821 ContentHandlerFactory subdocumentHandlerFactory) 822 { 823 this.subdocumentRoot = subdocumentRoot; 824 this.rootHandler = rootHandler; 825 this.subdocumentHandlerFactory = subdocumentHandlerFactory; 826 } 827 828 public final void startElement( 829 String namespaceURI, 830 String localName, 831 String qName, 832 Attributes list) throws SAXException 833 { 834 if (subdocument) { 835 subdocumentHandler.startElement(namespaceURI, 836 localName, 837 qName, 838 list); 839 } else if (localName.equals(subdocumentRoot)) { 840 subdocumentHandler = subdocumentHandlerFactory.createContentHandler(); 841 subdocumentHandler.startDocument(); 842 subdocumentHandler.startElement(namespaceURI, 843 localName, 844 qName, 845 list); 846 subdocument = true; 847 } else if (rootHandler != null) { 848 rootHandler.startElement(namespaceURI, localName, qName, list); 849 } 850 } 851 852 public final void endElement( 853 String namespaceURI, 854 String localName, 855 String qName) throws SAXException 856 { 857 if (subdocument) { 858 subdocumentHandler.endElement(namespaceURI, localName, qName); 859 if (localName.equals(subdocumentRoot)) { 860 subdocumentHandler.endDocument(); 861 subdocument = false; 862 } 863 } else if (rootHandler != null) { 864 rootHandler.endElement(namespaceURI, localName, qName); 865 } 866 } 867 868 public final void startDocument() throws SAXException { 869 if (rootHandler != null) { 870 rootHandler.startDocument(); 871 } 872 } 873 874 public final void endDocument() throws SAXException { 875 if (rootHandler != null) { 876 rootHandler.endDocument(); 877 878 } 879 } 880 881 public final void characters(char[] buff, int offset, int size) 882 throws SAXException 883 { 884 if (subdocument) { 885 subdocumentHandler.characters(buff, offset, size); 886 } else if (rootHandler != null) { 887 rootHandler.characters(buff, offset, size); 888 } 889 } 890 891 } 892 893 902 private static final class OutputSlicingHandler extends DefaultHandler { 903 private String subdocumentRoot; 904 905 private ContentHandlerFactory subdocumentHandlerFactory; 906 907 private EntryElement entryElement; 908 909 private boolean isXml; 910 911 private boolean subdocument = false; 912 913 private ContentHandler subdocumentHandler; 914 915 928 public OutputSlicingHandler( 929 ContentHandlerFactory subdocumentHandlerFactory, 930 EntryElement entryElement, 931 boolean isXml) 932 { 933 this.subdocumentRoot = "class"; 934 this.subdocumentHandlerFactory = subdocumentHandlerFactory; 935 this.entryElement = entryElement; 936 this.isXml = isXml; 937 } 938 939 public final void startElement( 940 String namespaceURI, 941 String localName, 942 String qName, 943 Attributes list) throws SAXException 944 { 945 if (subdocument) { 946 subdocumentHandler.startElement(namespaceURI, 947 localName, 948 qName, 949 list); 950 } else if (localName.equals(subdocumentRoot)) { 951 String name = list.getValue("name"); 952 if (name == null || name.length() == 0) 953 throw new SAXException ("Class element without name attribute."); 954 try { 955 entryElement.openEntry(isXml 956 ? name.concat(".class.xml") 957 : name.concat(".class")); 958 } catch (IOException ex) { 959 throw new SAXException (ex.toString(), ex); 960 } 961 subdocumentHandler = subdocumentHandlerFactory.createContentHandler(); 962 subdocumentHandler.startDocument(); 963 subdocumentHandler.startElement(namespaceURI, 964 localName, 965 qName, 966 list); 967 subdocument = true; 968 } 969 } 970 971 public final void endElement( 972 String namespaceURI, 973 String localName, 974 String qName) throws SAXException 975 { 976 if (subdocument) { 977 subdocumentHandler.endElement(namespaceURI, localName, qName); 978 if (localName.equals(subdocumentRoot)) { 979 subdocumentHandler.endDocument(); 980 subdocument = false; 981 try { 982 entryElement.closeEntry(); 983 } catch (IOException ex) { 984 throw new SAXException (ex.toString(), ex); 985 } 986 } 987 } 988 } 989 990 public final void startDocument() throws SAXException { 991 } 992 993 public final void endDocument() throws SAXException { 994 } 995 996 public final void characters(char[] buff, int offset, int size) 997 throws SAXException 998 { 999 if (subdocument) { 1000 subdocumentHandler.characters(buff, offset, size); 1001 } 1002 } 1003 1004 } 1005 1006 private static interface EntryElement { 1007 1008 OutputStream openEntry(String name) throws IOException ; 1009 1010 void closeEntry() throws IOException ; 1011 1012 } 1013 1014 private static final class SingleDocElement implements EntryElement { 1015 private OutputStream os; 1016 1017 public SingleDocElement(OutputStream os) { 1018 this.os = os; 1019 } 1020 1021 public OutputStream openEntry(String name) throws IOException { 1022 return os; 1023 } 1024 1025 public void closeEntry() throws IOException { 1026 os.flush(); 1027 } 1028 1029 } 1030 1031 private static final class ZipEntryElement implements EntryElement { 1032 private ZipOutputStream zos; 1033 1034 public ZipEntryElement(ZipOutputStream zos) { 1035 this.zos = zos; 1036 } 1037 1038 public OutputStream openEntry(String name) throws IOException { 1039 ZipEntry entry = new ZipEntry (name); 1040 zos.putNextEntry(entry); 1041 return zos; 1042 } 1043 1044 public void closeEntry() throws IOException { 1045 zos.flush(); 1046 zos.closeEntry(); 1047 } 1048 1049 } 1050 1051} 1052 | Popular Tags |