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