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