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}