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