001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with this 004 * work for additional information regarding copyright ownership. The ASF 005 * licenses this file to You under the Apache License, Version 2.0 (the 006 * "License"); you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations under 015 * the License. 016 */ 017package org.apache.commons.compress.harmony.pack200; 018 019import java.io.ByteArrayOutputStream; 020import java.io.IOException; 021import java.io.OutputStream; 022import java.util.ArrayList; 023import java.util.List; 024 025import org.apache.commons.compress.harmony.pack200.Archive.PackingFile; 026import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit; 027import org.objectweb.asm.AnnotationVisitor; 028import org.objectweb.asm.Attribute; 029import org.objectweb.asm.ClassReader; 030import org.objectweb.asm.ClassVisitor; 031import org.objectweb.asm.FieldVisitor; 032import org.objectweb.asm.Label; 033import org.objectweb.asm.MethodVisitor; 034import org.objectweb.asm.Opcodes; 035import org.objectweb.asm.Type; 036 037/** 038 * A Pack200 archive consists of one or more Segments. 039 */ 040public class Segment extends ClassVisitor { 041 042 public class ArrayVisitor extends AnnotationVisitor { 043 044 private final int indexInCaseArrayN; 045 private final List<Integer> caseArrayN; 046 private final List<Object> values; 047 private final List<String> nameRU; 048 private final List<String> tags; 049 050 public ArrayVisitor(final List<Integer> caseArrayN, final List<String> tags, final List<String> nameRU, final List<Object> values) { 051 super(ASM_API); 052 053 this.caseArrayN = caseArrayN; 054 this.tags = tags; 055 this.nameRU = nameRU; 056 this.values = values; 057 this.indexInCaseArrayN = caseArrayN.size() - 1; 058 } 059 060 @Override 061 public void visit(String name, final Object value) { 062 final Integer numCases = caseArrayN.remove(indexInCaseArrayN); 063 caseArrayN.add(indexInCaseArrayN, Integer.valueOf(numCases.intValue() + 1)); 064 if (name == null) { 065 name = ""; 066 } 067 addValueAndTag(value, tags, values); 068 } 069 070 @Override 071 public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) { 072 throw new UnsupportedOperationException("Not yet supported"); 073 } 074 075 @Override 076 public AnnotationVisitor visitArray(String name) { 077 tags.add("["); 078 if (name == null) { 079 name = ""; 080 } 081 nameRU.add(name); 082 caseArrayN.add(Integer.valueOf(0)); 083 return new ArrayVisitor(caseArrayN, tags, nameRU, values); 084 } 085 086 @Override 087 public void visitEnd() { 088 // override to noop 089 } 090 091 @Override 092 public void visitEnum(final String name, final String desc, final String value) { 093 final Integer numCases = caseArrayN.remove(caseArrayN.size() - 1); 094 caseArrayN.add(Integer.valueOf(numCases.intValue() + 1)); 095 tags.add("e"); 096 values.add(desc); 097 values.add(value); 098 } 099 } 100 101 /** 102 * Exception indicating that the class currently being visited contains an unknown attribute, which means that by default the class file needs to be passed 103 * through as-is in the file_bands rather than being packed with pack200. 104 */ 105 public static class PassException extends RuntimeException { 106 107 private static final long serialVersionUID = 1L; 108 109 } 110 111 /** 112 * SegmentAnnotationVisitor implements {@code AnnotationVisitor} to visit Annotations found in a class file. 113 */ 114 public class SegmentAnnotationVisitor extends AnnotationVisitor { 115 116 private int context = -1; 117 private int parameter = -1; 118 private String desc; 119 private boolean visible; 120 121 private final List<String> nameRU = new ArrayList<>(); 122 private final List<String> tags = new ArrayList<>(); // tags 123 private final List<Object> values = new ArrayList<>(); 124 private final List<Integer> caseArrayN = new ArrayList<>(); 125 private final List<String> nestTypeRS = new ArrayList<>(); 126 private final List<String> nestNameRU = new ArrayList<>(); 127 private final List<Integer> nestPairN = new ArrayList<>(); 128 129 public SegmentAnnotationVisitor(final int context) { 130 super(ASM_API); 131 this.context = context; 132 } 133 134 public SegmentAnnotationVisitor(final int context, final int parameter, final String desc, final boolean visible) { 135 super(ASM_API); 136 this.context = context; 137 this.parameter = parameter; 138 this.desc = desc; 139 this.visible = visible; 140 } 141 142 public SegmentAnnotationVisitor(final int context, final String desc, final boolean visible) { 143 super(ASM_API); 144 this.context = context; 145 this.desc = desc; 146 this.visible = visible; 147 } 148 149 @Override 150 public void visit(String name, final Object value) { 151 if (name == null) { 152 name = ""; 153 } 154 nameRU.add(name); 155 addValueAndTag(value, tags, values); 156 } 157 158 @Override 159 public AnnotationVisitor visitAnnotation(String name, final String desc) { 160 tags.add("@"); 161 if (name == null) { 162 name = ""; 163 } 164 nameRU.add(name); 165 nestTypeRS.add(desc); 166 nestPairN.add(Integer.valueOf(0)); 167 return new AnnotationVisitor(context, av) { 168 @Override 169 public void visit(final String name, final Object value) { 170 final Integer numPairs = nestPairN.remove(nestPairN.size() - 1); 171 nestPairN.add(Integer.valueOf(numPairs.intValue() + 1)); 172 nestNameRU.add(name); 173 addValueAndTag(value, tags, values); 174 } 175 176 @Override 177 public AnnotationVisitor visitAnnotation(final String arg0, final String arg1) { 178 throw new UnsupportedOperationException("Not yet supported"); 179// return null; 180 } 181 182 @Override 183 public AnnotationVisitor visitArray(final String arg0) { 184 throw new UnsupportedOperationException("Not yet supported"); 185// return null; 186 } 187 188 @Override 189 public void visitEnd() { 190 } 191 192 @Override 193 public void visitEnum(final String name, final String desc, final String value) { 194 final Integer numPairs = nestPairN.remove(nestPairN.size() - 1); 195 nestPairN.add(Integer.valueOf(numPairs.intValue() + 1)); 196 tags.add("e"); 197 nestNameRU.add(name); 198 values.add(desc); 199 values.add(value); 200 } 201 }; 202 } 203 204 @Override 205 public AnnotationVisitor visitArray(String name) { 206 tags.add("["); 207 if (name == null) { 208 name = ""; 209 } 210 nameRU.add(name); 211 caseArrayN.add(Integer.valueOf(0)); 212 return new ArrayVisitor(caseArrayN, tags, nameRU, values); 213 } 214 215 @Override 216 public void visitEnd() { 217 if (desc == null) { 218 Segment.this.classBands.addAnnotationDefault(nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN); 219 } else if (parameter != -1) { 220 Segment.this.classBands.addParameterAnnotation(parameter, desc, visible, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN); 221 } else { 222 Segment.this.classBands.addAnnotation(context, desc, visible, nameRU, tags, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN); 223 } 224 } 225 226 @Override 227 public void visitEnum(String name, final String desc, final String value) { 228 tags.add("e"); 229 if (name == null) { 230 name = ""; 231 } 232 nameRU.add(name); 233 values.add(desc); 234 values.add(value); 235 } 236 } 237 238 /** 239 * SegmentFieldVisitor implements {@code FieldVisitor} to visit the metadata relating to fields in a class file. 240 */ 241 public class SegmentFieldVisitor extends FieldVisitor { 242 243 public SegmentFieldVisitor() { 244 super(ASM_API); 245 } 246 247 @Override 248 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { 249 return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_FIELD, desc, visible); 250 } 251 252 @Override 253 public void visitAttribute(final Attribute attribute) { 254 if (attribute.isUnknown()) { 255 final String action = options.getUnknownAttributeAction(); 256 if (action.equals(PackingOptions.PASS)) { 257 passCurrentClass(); 258 } else if (action.equals(PackingOptions.ERROR)) { 259 throw new Error("Unknown attribute encountered"); 260 } // else skip 261 } else if (attribute instanceof NewAttribute) { 262 final NewAttribute newAttribute = (NewAttribute) attribute; 263 if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_FIELD)) { 264 final String action = options.getUnknownFieldAttributeAction(newAttribute.type); 265 if (action.equals(PackingOptions.PASS)) { 266 passCurrentClass(); 267 } else if (action.equals(PackingOptions.ERROR)) { 268 throw new Error("Unknown attribute encountered"); 269 } // else skip 270 } 271 classBands.addFieldAttribute(newAttribute); 272 } else { 273 throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type); 274 } 275 } 276 277 @Override 278 public void visitEnd() { 279 } 280 } 281 282 /** 283 * This class implements MethodVisitor to visit the contents and metadata related to methods in a class file. 284 * 285 * It delegates to BcBands for bytecode related visits and to ClassBands for everything else. 286 */ 287 public class SegmentMethodVisitor extends MethodVisitor { 288 289 public SegmentMethodVisitor() { 290 super(ASM_API); 291 } 292 293 @Override 294 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { 295 return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD, desc, visible); 296 } 297 298 @Override 299 public AnnotationVisitor visitAnnotationDefault() { 300 return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD); 301 } 302 303 @Override 304 public void visitAttribute(final Attribute attribute) { 305 if (attribute.isUnknown()) { 306 final String action = options.getUnknownAttributeAction(); 307 if (action.equals(PackingOptions.PASS)) { 308 passCurrentClass(); 309 } else if (action.equals(PackingOptions.ERROR)) { 310 throw new Error("Unknown attribute encountered"); 311 } // else skip 312 } else if (attribute instanceof NewAttribute) { 313 final NewAttribute newAttribute = (NewAttribute) attribute; 314 if (attribute.isCodeAttribute()) { 315 if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CODE)) { 316 final String action = options.getUnknownCodeAttributeAction(newAttribute.type); 317 if (action.equals(PackingOptions.PASS)) { 318 passCurrentClass(); 319 } else if (action.equals(PackingOptions.ERROR)) { 320 throw new Error("Unknown attribute encountered"); 321 } // else skip 322 } 323 classBands.addCodeAttribute(newAttribute); 324 } else { 325 if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_METHOD)) { 326 final String action = options.getUnknownMethodAttributeAction(newAttribute.type); 327 if (action.equals(PackingOptions.PASS)) { 328 passCurrentClass(); 329 } else if (action.equals(PackingOptions.ERROR)) { 330 throw new Error("Unknown attribute encountered"); 331 } // else skip 332 } 333 classBands.addMethodAttribute(newAttribute); 334 } 335 } else { 336 throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type); 337 } 338 } 339 340 @Override 341 public void visitCode() { 342 classBands.addCode(); 343 } 344 345 @Override 346 public void visitEnd() { 347 classBands.endOfMethod(); 348 bcBands.visitEnd(); 349 } 350 351 @Override 352 public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { 353 bcBands.visitFieldInsn(opcode, owner, name, desc); 354 } 355 356 @Override 357 public void visitFrame(final int arg0, final int arg1, final Object[] arg2, final int arg3, final Object[] arg4) { 358 // TODO: Java 6 - implement support for this 359 360 } 361 362 @Override 363 public void visitIincInsn(final int var, final int increment) { 364 bcBands.visitIincInsn(var, increment); 365 } 366 367 @Override 368 public void visitInsn(final int opcode) { 369 bcBands.visitInsn(opcode); 370 } 371 372 @Override 373 public void visitIntInsn(final int opcode, final int operand) { 374 bcBands.visitIntInsn(opcode, operand); 375 } 376 377 @Override 378 public void visitJumpInsn(final int opcode, final Label label) { 379 bcBands.visitJumpInsn(opcode, label); 380 } 381 382 @Override 383 public void visitLabel(final Label label) { 384 bcBands.visitLabel(label); 385 } 386 387 @Override 388 public void visitLdcInsn(final Object cst) { 389 bcBands.visitLdcInsn(cst); 390 } 391 392 @Override 393 public void visitLineNumber(final int line, final Label start) { 394 if (!stripDebug) { 395 classBands.addLineNumber(line, start); 396 } 397 } 398 399 @Override 400 public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { 401 if (!stripDebug) { 402 classBands.addLocalVariable(name, desc, signature, start, end, index); 403 } 404 } 405 406 @Override 407 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { 408 bcBands.visitLookupSwitchInsn(dflt, keys, labels); 409 } 410 411 @Override 412 public void visitMaxs(final int maxStack, final int maxLocals) { 413 classBands.addMaxStack(maxStack, maxLocals); 414 } 415 416 @Override 417 public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { 418 bcBands.visitMethodInsn(opcode, owner, name, desc); 419 } 420 421 @Override 422 public void visitMultiANewArrayInsn(final String desc, final int dimensions) { 423 bcBands.visitMultiANewArrayInsn(desc, dimensions); 424 } 425 426 @Override 427 public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { 428 return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_METHOD, parameter, desc, visible); 429 } 430 431 @Override 432 public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { 433 bcBands.visitTableSwitchInsn(min, max, dflt, labels); 434 } 435 436 @Override 437 public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { 438 classBands.addHandler(start, end, handler, type); 439 } 440 441 @Override 442 public void visitTypeInsn(final int opcode, final String type) { 443 bcBands.visitTypeInsn(opcode, type); 444 } 445 446 @Override 447 public void visitVarInsn(final int opcode, final int var) { 448 bcBands.visitVarInsn(opcode, var); 449 } 450 451 } 452 453 /** See https://asm.ow2.io/Javadoc/org/objectweb/asm/Opcodes.html#ASM4 */ 454 public static int ASM_API = Opcodes.ASM4; 455 private SegmentHeader segmentHeader; 456 private CpBands cpBands; 457 private AttributeDefinitionBands attributeDefinitionBands; 458 459 private IcBands icBands; 460 private ClassBands classBands; 461 private BcBands bcBands; 462 private FileBands fileBands; 463 private final SegmentFieldVisitor fieldVisitor = new SegmentFieldVisitor(); 464 private final SegmentMethodVisitor methodVisitor = new SegmentMethodVisitor(); 465 466 private Pack200ClassReader currentClassReader; 467 468 private PackingOptions options; 469 470 private boolean stripDebug; 471 472 private Attribute[] nonStandardAttributePrototypes; 473 474 public Segment() { 475 super(ASM_API); 476 } 477 478 // helper method for annotation visitors 479 private void addValueAndTag(final Object value, final List<String> tags, final List<Object> values) { 480 if (value instanceof Integer) { 481 tags.add("I"); 482 values.add(value); 483 } else if (value instanceof Double) { 484 tags.add("D"); 485 values.add(value); 486 } else if (value instanceof Float) { 487 tags.add("F"); 488 values.add(value); 489 } else if (value instanceof Long) { 490 tags.add("J"); 491 values.add(value); 492 } else if (value instanceof Byte) { 493 tags.add("B"); 494 values.add(Integer.valueOf(((Byte) value).intValue())); 495 } else if (value instanceof Character) { 496 tags.add("C"); 497 values.add(Integer.valueOf(((Character) value).charValue())); 498 } else if (value instanceof Short) { 499 tags.add("S"); 500 values.add(Integer.valueOf(((Short) value).intValue())); 501 } else if (value instanceof Boolean) { 502 tags.add("Z"); 503 values.add(Integer.valueOf(((Boolean) value).booleanValue() ? 1 : 0)); 504 } else if (value instanceof String) { 505 tags.add("s"); 506 values.add(value); 507 } else if (value instanceof Type) { 508 tags.add("c"); 509 values.add(((Type) value).toString()); 510 } 511 } 512 513 public AttributeDefinitionBands getAttrBands() { 514 return attributeDefinitionBands; 515 } 516 517 public ClassBands getClassBands() { 518 return classBands; 519 } 520 521 public CpBands getCpBands() { 522 return cpBands; 523 } 524 525 public Pack200ClassReader getCurrentClassReader() { 526 return currentClassReader; 527 } 528 529 public IcBands getIcBands() { 530 return icBands; 531 } 532 533 public SegmentHeader getSegmentHeader() { 534 return segmentHeader; 535 } 536 537 public boolean lastConstantHadWideIndex() { 538 return currentClassReader.lastConstantHadWideIndex(); 539 } 540 541 /** 542 * The main method on Segment. Reads in all the class files, packs them and then writes the packed segment out to the given OutputStream. 543 * 544 * @param segmentUnit TODO 545 * @param out the OutputStream to write the packed Segment to 546 * @param options packing options 547 * @throws IOException If an I/O error occurs. 548 * @throws Pack200Exception TODO 549 */ 550 public void pack(final SegmentUnit segmentUnit, final OutputStream out, final PackingOptions options) throws IOException, Pack200Exception { 551 this.options = options; 552 this.stripDebug = options.isStripDebug(); 553 final int effort = options.getEffort(); 554 nonStandardAttributePrototypes = options.getUnknownAttributePrototypes(); 555 556 PackingUtils.log("Start to pack a new segment with " + segmentUnit.fileListSize() + " files including " + segmentUnit.classListSize() + " classes"); 557 558 PackingUtils.log("Initialize a header for the segment"); 559 segmentHeader = new SegmentHeader(); 560 segmentHeader.setFile_count(segmentUnit.fileListSize()); 561 segmentHeader.setHave_all_code_flags(!stripDebug); 562 if (!options.isKeepDeflateHint()) { 563 segmentHeader.setDeflate_hint(Boolean.parseBoolean(options.getDeflateHint())); 564 } 565 566 PackingUtils.log("Setup constant pool bands for the segment"); 567 cpBands = new CpBands(this, effort); 568 569 PackingUtils.log("Setup attribute definition bands for the segment"); 570 attributeDefinitionBands = new AttributeDefinitionBands(this, effort, nonStandardAttributePrototypes); 571 572 PackingUtils.log("Setup internal class bands for the segment"); 573 icBands = new IcBands(segmentHeader, cpBands, effort); 574 575 PackingUtils.log("Setup class bands for the segment"); 576 classBands = new ClassBands(this, segmentUnit.classListSize(), effort, stripDebug); 577 578 PackingUtils.log("Setup byte code bands for the segment"); 579 bcBands = new BcBands(cpBands, this, effort); 580 581 PackingUtils.log("Setup file bands for the segment"); 582 fileBands = new FileBands(cpBands, segmentHeader, options, segmentUnit, effort); 583 584 processClasses(segmentUnit, nonStandardAttributePrototypes); 585 586 cpBands.finaliseBands(); 587 attributeDefinitionBands.finaliseBands(); 588 icBands.finaliseBands(); 589 classBands.finaliseBands(); 590 bcBands.finaliseBands(); 591 fileBands.finaliseBands(); 592 593 // Using a temporary stream because we have to pack the other bands 594 // before segmentHeader because the band_headers band is only created 595 // when the other bands are packed, but comes before them in the packed 596 // file. 597 final ByteArrayOutputStream bandsOutputStream = new ByteArrayOutputStream(); 598 599 PackingUtils.log("Packing..."); 600 final int finalNumberOfClasses = classBands.numClassesProcessed(); 601 segmentHeader.setClass_count(finalNumberOfClasses); 602 cpBands.pack(bandsOutputStream); 603 if (finalNumberOfClasses > 0) { 604 attributeDefinitionBands.pack(bandsOutputStream); 605 icBands.pack(bandsOutputStream); 606 classBands.pack(bandsOutputStream); 607 bcBands.pack(bandsOutputStream); 608 } 609 fileBands.pack(bandsOutputStream); 610 611 final ByteArrayOutputStream headerOutputStream = new ByteArrayOutputStream(); 612 segmentHeader.pack(headerOutputStream); 613 614 headerOutputStream.writeTo(out); 615 bandsOutputStream.writeTo(out); 616 617 segmentUnit.addPackedByteAmount(headerOutputStream.size()); 618 segmentUnit.addPackedByteAmount(bandsOutputStream.size()); 619 620 PackingUtils.log("Wrote total of " + segmentUnit.getPackedByteAmount() + " bytes"); 621 PackingUtils.log("Transmitted " + segmentUnit.fileListSize() + " files of " + segmentUnit.getByteAmount() + " input bytes in a segment of " 622 + segmentUnit.getPackedByteAmount() + " bytes"); 623 } 624 625 private void passCurrentClass() { 626 throw new PassException(); 627 } 628 629 private void processClasses(final SegmentUnit segmentUnit, final Attribute[] attributes) throws Pack200Exception { 630 segmentHeader.setClass_count(segmentUnit.classListSize()); 631 for (final Pack200ClassReader classReader : segmentUnit.getClassList()) { 632 currentClassReader = classReader; 633 int flags = 0; 634 if (stripDebug) { 635 flags |= ClassReader.SKIP_DEBUG; 636 } 637 try { 638 classReader.accept(this, attributes, flags); 639 } catch (final PassException pe) { 640 // Pass this class through as-is rather than packing it 641 // TODO: probably need to deal with any inner classes 642 classBands.removeCurrentClass(); 643 final String name = classReader.getFileName(); 644 options.addPassFile(name); 645 cpBands.addCPUtf8(name); 646 boolean found = false; 647 for (final PackingFile file : segmentUnit.getFileList()) { 648 if (file.getName().equals(name)) { 649 found = true; 650 file.setContents(classReader.b); 651 break; 652 } 653 } 654 if (!found) { 655 throw new Pack200Exception("Error passing file " + name); 656 } 657 } 658 } 659 } 660 661 @Override 662 public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { 663 bcBands.setCurrentClass(name, superName); 664 segmentHeader.addMajorVersion(version); 665 classBands.addClass(version, access, name, signature, superName, interfaces); 666 } 667 668 @Override 669 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { 670 return new SegmentAnnotationVisitor(MetadataBandGroup.CONTEXT_CLASS, desc, visible); 671 } 672 673 @Override 674 public void visitAttribute(final Attribute attribute) { 675 if (attribute.isUnknown()) { 676 final String action = options.getUnknownAttributeAction(); 677 if (action.equals(PackingOptions.PASS)) { 678 passCurrentClass(); 679 } else if (action.equals(PackingOptions.ERROR)) { 680 throw new Error("Unknown attribute encountered"); 681 } // else skip 682 } else if (attribute instanceof NewAttribute) { 683 final NewAttribute newAttribute = (NewAttribute) attribute; 684 if (newAttribute.isUnknown(AttributeDefinitionBands.CONTEXT_CLASS)) { 685 final String action = options.getUnknownClassAttributeAction(newAttribute.type); 686 if (action.equals(PackingOptions.PASS)) { 687 passCurrentClass(); 688 } else if (action.equals(PackingOptions.ERROR)) { 689 throw new Error("Unknown attribute encountered"); 690 } // else skip 691 } 692 classBands.addClassAttribute(newAttribute); 693 } else { 694 throw new IllegalArgumentException("Unexpected attribute encountered: " + attribute.type); 695 } 696 } 697 698 @Override 699 public void visitEnd() { 700 classBands.endOfClass(); 701 } 702 703 @Override 704 public FieldVisitor visitField(final int flags, final String name, final String desc, final String signature, final Object value) { 705 classBands.addField(flags, name, desc, signature, value); 706 return fieldVisitor; 707 } 708 709 @Override 710 public void visitInnerClass(final String name, final String outerName, final String innerName, final int flags) { 711 icBands.addInnerClass(name, outerName, innerName, flags); 712 } 713 714 @Override 715 public MethodVisitor visitMethod(final int flags, final String name, final String desc, final String signature, final String[] exceptions) { 716 classBands.addMethod(flags, name, desc, signature, exceptions); 717 return methodVisitor; 718 } 719 720 @Override 721 public void visitOuterClass(final String owner, final String name, final String desc) { 722 classBands.addEnclosingMethod(owner, name, desc); 723 724 } 725 726 @Override 727 public void visitSource(final String source, final String debug) { 728 if (!stripDebug) { 729 classBands.addSourceFile(source); 730 } 731 } 732}