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}