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