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