    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}