    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}