    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  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,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.pack200;
    018
    019import java.io.ByteArrayInputStream;
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.io.OutputStream;
    023import java.io.StringReader;
    024import java.util.ArrayList;
    025import java.util.Collections;
    026import java.util.Iterator;
    027import java.util.List;
    028import java.util.Map;
    029
    030import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
    031import org.objectweb.asm.Label;
    032
    033/**
    034 * Set of bands relating to a non-predefined attribute that has had a layout definition given to pack200 (e.g. via one
    035 * of the -C, -M, -F or -D command line options)
    036 */
    037public class NewAttributeBands extends BandSet {
    038
    039    protected List attributeLayoutElements;
    040    private int[] backwardsCallCounts;
    041    private final CpBands cpBands;
    042    private final AttributeDefinition def;
    043    private boolean usedAtLeastOnce;
    044
    045    // used when parsing
    046    private Integral lastPIntegral;
    047
    048    public NewAttributeBands(final int effort, final CpBands cpBands, final SegmentHeader header,
    049        final AttributeDefinition def) throws IOException {
    050        super(effort, header);
    051        this.def = def;
    052        this.cpBands = cpBands;
    053        parseLayout();
    054    }
    055
    056    public void addAttribute(final NewAttribute attribute) {
    057        usedAtLeastOnce = true;
    058        final InputStream stream = new ByteArrayInputStream(attribute.getBytes());
    059        for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
    060            final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    061            layoutElement.addAttributeToBand(attribute, stream);
    062        }
    063    }
    064
    065    @Override
    066    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    067        for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
    068            final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    069            layoutElement.pack(out);
    070        }
    071    }
    072
    073    public String getAttributeName() {
    074        return def.name.getUnderlyingString();
    075    }
    076
    077    public int getFlagIndex() {
    078        return def.index;
    079    }
    080
    081    public int[] numBackwardsCalls() {
    082        return backwardsCallCounts;
    083    }
    084
    085    public boolean isUsedAtLeastOnce() {
    086        return usedAtLeastOnce;
    087    }
    088
    089    private void parseLayout() throws IOException {
    090        final String layout = def.layout.getUnderlyingString();
    091        if (attributeLayoutElements == null) {
    092            attributeLayoutElements = new ArrayList();
    093            final StringReader stream = new StringReader(layout);
    094            AttributeLayoutElement e;
    095            while ((e = readNextAttributeElement(stream)) != null) {
    096                attributeLayoutElements.add(e);
    097            }
    098            resolveCalls();
    099        }
    100    }
    101
    102    /**
    103     * Resolve calls in the attribute layout and returns the number of backwards callables
    104     *
    105     * @param tokens - the attribute layout as a List of AttributeElements
    106     */
    107    private void resolveCalls() {
    108        for (int i = 0; i < attributeLayoutElements.size(); i++) {
    109            final AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i);
    110            if (element instanceof Callable) {
    111                final Callable callable = (Callable) element;
    112                final List body = callable.body; // Look for calls in the body
    113                for (int iIndex = 0; iIndex < body.size(); iIndex++) {
    114                    final LayoutElement layoutElement = (LayoutElement) body.get(iIndex);
    115                    // Set the callable for each call
    116                    resolveCallsForElement(i, callable, layoutElement);
    117                }
    118            }
    119        }
    120        int backwardsCallableIndex = 0;
    121        for (int i = 0; i < attributeLayoutElements.size(); i++) {
    122            final AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i);
    123            if (element instanceof Callable) {
    124                final Callable callable = (Callable) element;
    125                if (callable.isBackwardsCallable) {
    126                    callable.setBackwardsCallableIndex(backwardsCallableIndex);
    127                    backwardsCallableIndex++;
    128                }
    129            }
    130        }
    131        backwardsCallCounts = new int[backwardsCallableIndex];
    132    }
    133
    134    private void resolveCallsForElement(final int i, final Callable currentCallable,
    135        final LayoutElement layoutElement) {
    136        if (layoutElement instanceof Call) {
    137            final Call call = (Call) layoutElement;
    138            int index = call.callableIndex;
    139            if (index == 0) { // Calls the parent callable
    140                call.setCallable(currentCallable);
    141            } else if (index > 0) { // Forwards call
    142                for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
    143                    final AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements.get(k);
    144                    if (el instanceof Callable) {
    145                        index--;
    146                        if (index == 0) {
    147                            call.setCallable((Callable) el);
    148                            break;
    149                        }
    150                    }
    151                }
    152            } else { // Backwards call
    153                for (int k = i - 1; k >= 0; k--) {
    154                    final AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements.get(k);
    155                    if (el instanceof Callable) {
    156                        index++;
    157                        if (index == 0) {
    158                            call.setCallable((Callable) el);
    159                            break;
    160                        }
    161                    }
    162                }
    163            }
    164        } else if (layoutElement instanceof Replication) {
    165            final List children = ((Replication) layoutElement).layoutElements;
    166            for (final Iterator iterator = children.iterator(); iterator.hasNext();) {
    167                final LayoutElement object = (LayoutElement) iterator.next();
    168                resolveCallsForElement(i, currentCallable, object);
    169            }
    170        }
    171    }
    172
    173    private AttributeLayoutElement readNextAttributeElement(final StringReader stream) throws IOException {
    174        stream.mark(1);
    175        final int nextChar = stream.read();
    176        if (nextChar == -1) {
    177            return null;
    178        }
    179        if (nextChar == '[') {
    180            final List body = readBody(getStreamUpToMatchingBracket(stream));
    181            return new Callable(body);
    182        }
    183        stream.reset();
    184        return readNextLayoutElement(stream);
    185    }
    186
    187    private LayoutElement readNextLayoutElement(final StringReader stream) throws IOException {
    188        final int nextChar = stream.read();
    189        if (nextChar == -1) {
    190            return null;
    191        }
    192
    193        switch (nextChar) {
    194        // Integrals
    195        case 'B':
    196        case 'H':
    197        case 'I':
    198        case 'V':
    199            return new Integral(new String(new char[] {(char) nextChar}));
    200        case 'S':
    201        case 'F':
    202            return new Integral(new String(new char[] {(char) nextChar, (char) stream.read()}));
    203        case 'P':
    204            stream.mark(1);
    205            if (stream.read() != 'O') {
    206                stream.reset();
    207                lastPIntegral = new Integral("P" + (char) stream.read());
    208                return lastPIntegral;
    209            } else {
    210                lastPIntegral = new Integral("PO" + (char) stream.read(), lastPIntegral);
    211                return lastPIntegral;
    212            }
    213        case 'O':
    214            stream.mark(1);
    215            if (stream.read() != 'S') {
    216                stream.reset();
    217                return new Integral("O" + (char) stream.read(), lastPIntegral);
    218            } else {
    219                return new Integral("OS" + (char) stream.read(), lastPIntegral);
    220            }
    221
    222            // Replication
    223        case 'N':
    224            final char uint_type = (char) stream.read();
    225            stream.read(); // '['
    226            final String str = readUpToMatchingBracket(stream);
    227            return new Replication("" + uint_type, str);
    228
    229        // Union
    230        case 'T':
    231            String int_type = "" + (char) stream.read();
    232            if (int_type.equals("S")) {
    233                int_type += (char) stream.read();
    234            }
    235            final List unionCases = new ArrayList();
    236            UnionCase c;
    237            while ((c = readNextUnionCase(stream)) != null) {
    238                unionCases.add(c);
    239            }
    240            stream.read(); // '('
    241            stream.read(); // ')'
    242            stream.read(); // '['
    243            List body = null;
    244            stream.mark(1);
    245            final char next = (char) stream.read();
    246            if (next != ']') {
    247                stream.reset();
    248                body = readBody(getStreamUpToMatchingBracket(stream));
    249            }
    250            return new Union(int_type, unionCases, body);
    251
    252        // Call
    253        case '(':
    254            final int number = readNumber(stream).intValue();
    255            stream.read(); // ')'
    256            return new Call(number);
    257        // Reference
    258        case 'K':
    259        case 'R':
    260            final StringBuilder string = new StringBuilder("").append((char) nextChar).append((char) stream.read());
    261            final char nxt = (char) stream.read();
    262            string.append(nxt);
    263            if (nxt == 'N') {
    264                string.append((char) stream.read());
    265            }
    266            return new Reference(string.toString());
    267        }
    268        return null;
    269    }
    270
    271    /**
    272     * Read a UnionCase from the stream
    273     *
    274     * @param stream
    275     * @return
    276     * @throws IOException If an I/O error occurs.
    277     */
    278    private UnionCase readNextUnionCase(final StringReader stream) throws IOException {
    279        stream.mark(2);
    280        stream.read(); // '('
    281        char next = (char) stream.read();
    282        if (next == ')') {
    283            stream.reset();
    284            return null;
    285        }
    286        stream.reset();
    287        stream.read(); // '('
    288        final List tags = new ArrayList();
    289        Integer nextTag;
    290        do {
    291            nextTag = readNumber(stream);
    292            if (nextTag != null) {
    293                tags.add(nextTag);
    294                stream.read(); // ',' or ')'
    295            }
    296        } while (nextTag != null);
    297        stream.read(); // '['
    298        stream.mark(1);
    299        next = (char) stream.read();
    300        if (next == ']') {
    301            return new UnionCase(tags);
    302        }
    303        stream.reset();
    304        return new UnionCase(tags, readBody(getStreamUpToMatchingBracket(stream)));
    305    }
    306
    307    /**
    308     * An AttributeLayoutElement is a part of an attribute layout and has one or more bands associated with it, which
    309     * transmit the AttributeElement data for successive Attributes of this type.
    310     */
    311    public interface AttributeLayoutElement {
    312
    313        void addAttributeToBand(NewAttribute attribute, InputStream stream);
    314
    315        void pack(OutputStream out) throws IOException, Pack200Exception;
    316
    317        void renumberBci(IntList bciRenumbering, Map labelsToOffsets);
    318
    319    }
    320
    321    public abstract class LayoutElement implements AttributeLayoutElement {
    322
    323        protected int getLength(final char uint_type) {
    324            int length = 0;
    325            switch (uint_type) {
    326            case 'B':
    327                length = 1;
    328                break;
    329            case 'H':
    330                length = 2;
    331                break;
    332            case 'I':
    333                length = 4;
    334                break;
    335            case 'V':
    336                length = 0;
    337                break;
    338            }
    339            return length;
    340        }
    341    }
    342
    343    public class Integral extends LayoutElement {
    344
    345        private final String tag;
    346
    347        private final List band = new ArrayList();
    348        private final BHSDCodec defaultCodec;
    349
    350        // used for bytecode offsets (OH and POH)
    351        private Integral previousIntegral;
    352        private int previousPValue;
    353
    354        public Integral(final String tag) {
    355            this.tag = tag;
    356            this.defaultCodec = getCodec(tag);
    357        }
    358
    359        public Integral(final String tag, final Integral previousIntegral) {
    360            this.tag = tag;
    361            this.defaultCodec = getCodec(tag);
    362            this.previousIntegral = previousIntegral;
    363        }
    364
    365        public String getTag() {
    366            return tag;
    367        }
    368
    369        @Override
    370        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    371            Object val = null;
    372            int value = 0;
    373            if (tag.equals("B") || tag.equals("FB")) {
    374                value = readInteger(1, stream) & 0xFF; // unsigned byte
    375            } else if (tag.equals("SB")) {
    376                value = readInteger(1, stream);
    377            } else if (tag.equals("H") || tag.equals("FH")) {
    378                value = readInteger(2, stream) & 0xFFFF; // unsigned short
    379            } else if (tag.equals("SH")) {
    380                value = readInteger(2, stream);
    381            } else if (tag.equals("I") || tag.equals("FI")) {
    382                value = readInteger(4, stream);
    383            } else if (tag.equals("SI")) {
    384                value = readInteger(4, stream);
    385            } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
    386                // Not currently supported
    387            } else if (tag.startsWith("PO") || tag.startsWith("OS")) {
    388                final char uint_type = tag.substring(2).toCharArray()[0];
    389                final int length = getLength(uint_type);
    390                value = readInteger(length, stream);
    391                value += previousIntegral.previousPValue;
    392                val = attribute.getLabel(value);
    393                previousPValue = value;
    394            } else if (tag.startsWith("P")) {
    395                final char uint_type = tag.substring(1).toCharArray()[0];
    396                final int length = getLength(uint_type);
    397                value = readInteger(length, stream);
    398                val = attribute.getLabel(value);
    399                previousPValue = value;
    400            } else if (tag.startsWith("O")) {
    401                final char uint_type = tag.substring(1).toCharArray()[0];
    402                final int length = getLength(uint_type);
    403                value = readInteger(length, stream);
    404                value += previousIntegral.previousPValue;
    405                val = attribute.getLabel(value);
    406                previousPValue = value;
    407            }
    408            if (val == null) {
    409                val = Integer.valueOf(value);
    410            }
    411            band.add(val);
    412        }
    413
    414        @Override
    415        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    416            PackingUtils.log("Writing new attribute bands...");
    417            final byte[] encodedBand = encodeBandInt(tag, integerListToArray(band), defaultCodec);
    418            out.write(encodedBand);
    419            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + band.size() + "]");
    420        }
    421
    422        public int latestValue() {
    423            return ((Integer) band.get(band.size() - 1)).intValue();
    424        }
    425
    426        @Override
    427        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    428            if (tag.startsWith("O") || tag.startsWith("PO")) {
    429                renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
    430            } else if (tag.startsWith("P")) {
    431                for (int i = band.size() - 1; i >= 0; i--) {
    432                    final Object label = band.get(i);
    433                    if (label instanceof Integer) {
    434                        break;
    435                    }
    436                    if (label instanceof Label) {
    437                        band.remove(i);
    438                        final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
    439                        band.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())));
    440                    }
    441                }
    442            }
    443        }
    444
    445        private void renumberOffsetBci(final List relative, final IntList bciRenumbering, final Map labelsToOffsets) {
    446            for (int i = band.size() - 1; i >= 0; i--) {
    447                final Object label = band.get(i);
    448                if (label instanceof Integer) {
    449                    break;
    450                }
    451                if (label instanceof Label) {
    452                    band.remove(i);
    453                    final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
    454                    final Integer renumberedOffset = Integer
    455                        .valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue());
    456                    band.add(i, renumberedOffset);
    457                }
    458            }
    459        }
    460
    461    }
    462
    463    /**
    464     * A replication is an array of layout elements, with an associated count
    465     */
    466    public class Replication extends LayoutElement {
    467
    468        private final Integral countElement;
    469
    470        private final List layoutElements = new ArrayList();
    471
    472        public Integral getCountElement() {
    473            return countElement;
    474        }
    475
    476        public List getLayoutElements() {
    477            return layoutElements;
    478        }
    479
    480        public Replication(final String tag, final String contents) throws IOException {
    481            this.countElement = new Integral(tag);
    482            final StringReader stream = new StringReader(contents);
    483            LayoutElement e;
    484            while ((e = readNextLayoutElement(stream)) != null) {
    485                layoutElements.add(e);
    486            }
    487        }
    488
    489        @Override
    490        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    491            countElement.addAttributeToBand(attribute, stream);
    492            final int count = countElement.latestValue();
    493            for (int i = 0; i < count; i++) {
    494                for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
    495                    final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    496                    layoutElement.addAttributeToBand(attribute, stream);
    497                }
    498            }
    499        }
    500
    501        @Override
    502        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    503            countElement.pack(out);
    504            for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
    505                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    506                layoutElement.pack(out);
    507            }
    508        }
    509
    510        @Override
    511        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    512            for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
    513                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    514                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
    515            }
    516        }
    517    }
    518
    519    /**
    520     * A Union is a type of layout element where the tag value acts as a selector for one of the union cases
    521     */
    522    public class Union extends LayoutElement {
    523
    524        private final Integral unionTag;
    525        private final List unionCases;
    526        private final List defaultCaseBody;
    527
    528        public Union(final String tag, final List unionCases, final List body) {
    529            this.unionTag = new Integral(tag);
    530            this.unionCases = unionCases;
    531            this.defaultCaseBody = body;
    532        }
    533
    534        @Override
    535        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    536            unionTag.addAttributeToBand(attribute, stream);
    537            final long tag = unionTag.latestValue();
    538            boolean defaultCase = true;
    539            for (int i = 0; i < unionCases.size(); i++) {
    540                final UnionCase element = (UnionCase) unionCases.get(i);
    541                if (element.hasTag(tag)) {
    542                    defaultCase = false;
    543                    element.addAttributeToBand(attribute, stream);
    544                }
    545            }
    546            if (defaultCase) {
    547                for (int i = 0; i < defaultCaseBody.size(); i++) {
    548                    final LayoutElement element = (LayoutElement) defaultCaseBody.get(i);
    549                    element.addAttributeToBand(attribute, stream);
    550                }
    551            }
    552        }
    553
    554        @Override
    555        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    556            unionTag.pack(out);
    557            for (final Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
    558                final UnionCase unionCase = (UnionCase) iterator.next();
    559                unionCase.pack(out);
    560            }
    561            for (final Iterator iterator = defaultCaseBody.iterator(); iterator.hasNext();) {
    562                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    563                layoutElement.pack(out);
    564            }
    565        }
    566
    567        @Override
    568        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    569            for (final Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
    570                final UnionCase unionCase = (UnionCase) iterator.next();
    571                unionCase.renumberBci(bciRenumbering, labelsToOffsets);
    572            }
    573            for (final Iterator iterator = defaultCaseBody.iterator(); iterator.hasNext();) {
    574                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    575                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
    576            }
    577        }
    578
    579        public Integral getUnionTag() {
    580            return unionTag;
    581        }
    582
    583        public List getUnionCases() {
    584            return unionCases;
    585        }
    586
    587        public List getDefaultCaseBody() {
    588            return defaultCaseBody;
    589        }
    590    }
    591
    592    public class Call extends LayoutElement {
    593
    594        private final int callableIndex;
    595        private Callable callable;
    596
    597        public Call(final int callableIndex) {
    598            this.callableIndex = callableIndex;
    599        }
    600
    601        public void setCallable(final Callable callable) {
    602            this.callable = callable;
    603            if (callableIndex < 1) {
    604                callable.setBackwardsCallable();
    605            }
    606        }
    607
    608        @Override
    609        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    610            callable.addAttributeToBand(attribute, stream);
    611            if (callableIndex < 1) {
    612                callable.addBackwardsCall();
    613            }
    614        }
    615
    616        @Override
    617        public void pack(final OutputStream out) {
    618            // do nothing here as pack will be called on the callable at another time
    619        }
    620
    621        @Override
    622        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    623            // do nothing here as renumberBci will be called on the callable at another time
    624        }
    625
    626        public int getCallableIndex() {
    627            return callableIndex;
    628        }
    629
    630        public Callable getCallable() {
    631            return callable;
    632        }
    633    }
    634
    635    /**
    636     * Constant Pool Reference
    637     */
    638    public class Reference extends LayoutElement {
    639
    640        private final String tag;
    641
    642        private List band;
    643
    644        private boolean nullsAllowed = false;
    645
    646        public Reference(final String tag) {
    647            this.tag = tag;
    648            nullsAllowed = tag.indexOf('N') != -1;
    649        }
    650
    651        @Override
    652        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    653            final int index = readInteger(4, stream);
    654            if (tag.startsWith("RC")) { // Class
    655                band.add(cpBands.getCPClass(attribute.readClass(index)));
    656            } else if (tag.startsWith("RU")) { // UTF8 String
    657                band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
    658            } else if (tag.startsWith("RS")) { // Signature
    659                band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
    660            } else { // Constant
    661                band.add(cpBands.getConstant(attribute.readConst(index)));
    662            }
    663            // TODO method and field references
    664        }
    665
    666        public String getTag() {
    667            return tag;
    668        }
    669
    670        @Override
    671        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    672            int[] ints;
    673            if (nullsAllowed) {
    674                ints = cpEntryOrNullListToArray(band);
    675            } else {
    676                ints = cpEntryListToArray(band);
    677            }
    678            final byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
    679            out.write(encodedBand);
    680            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + ints.length + "]");
    681        }
    682
    683        @Override
    684        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    685            // nothing to do here
    686        }
    687
    688    }
    689
    690    public class Callable implements AttributeLayoutElement {
    691
    692        private final List body;
    693
    694        private boolean isBackwardsCallable;
    695
    696        private int backwardsCallableIndex;
    697
    698        public Callable(final List body) throws IOException {
    699            this.body = body;
    700        }
    701
    702        public void setBackwardsCallableIndex(final int backwardsCallableIndex) {
    703            this.backwardsCallableIndex = backwardsCallableIndex;
    704        }
    705
    706        public void addBackwardsCall() {
    707            backwardsCallCounts[backwardsCallableIndex]++;
    708        }
    709
    710        public boolean isBackwardsCallable() {
    711            return isBackwardsCallable;
    712        }
    713
    714        /**
    715         * Tells this Callable that it is a backwards callable
    716         */
    717        public void setBackwardsCallable() {
    718            this.isBackwardsCallable = true;
    719        }
    720
    721        @Override
    722        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    723            for (final Iterator iterator = body.iterator(); iterator.hasNext();) {
    724                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    725                layoutElement.addAttributeToBand(attribute, stream);
    726            }
    727        }
    728
    729        @Override
    730        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    731            for (final Iterator iterator = body.iterator(); iterator.hasNext();) {
    732                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    733                layoutElement.pack(out);
    734            }
    735        }
    736
    737        @Override
    738        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    739            for (final Iterator iterator = body.iterator(); iterator.hasNext();) {
    740                final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
    741                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
    742            }
    743        }
    744
    745        public List getBody() {
    746            return body;
    747        }
    748    }
    749
    750    /**
    751     * A Union case
    752     */
    753    public class UnionCase extends LayoutElement {
    754
    755        private final List body;
    756
    757        private final List tags;
    758
    759        public UnionCase(final List tags) {
    760            this.tags = tags;
    761            this.body = Collections.EMPTY_LIST;
    762        }
    763
    764        public boolean hasTag(final long l) {
    765            return tags.contains(Integer.valueOf((int) l));
    766        }
    767
    768        public UnionCase(final List tags, final List body) throws IOException {
    769            this.tags = tags;
    770            this.body = body;
    771        }
    772
    773        @Override
    774        public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) {
    775            for (int i = 0; i < body.size(); i++) {
    776                final LayoutElement element = (LayoutElement) body.get(i);
    777                element.addAttributeToBand(attribute, stream);
    778            }
    779        }
    780
    781        @Override
    782        public void pack(final OutputStream out) throws IOException, Pack200Exception {
    783            for (int i = 0; i < body.size(); i++) {
    784                final LayoutElement element = (LayoutElement) body.get(i);
    785                element.pack(out);
    786            }
    787        }
    788
    789        @Override
    790        public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    791            for (int i = 0; i < body.size(); i++) {
    792                final LayoutElement element = (LayoutElement) body.get(i);
    793                element.renumberBci(bciRenumbering, labelsToOffsets);
    794            }
    795        }
    796
    797        public List getBody() {
    798            return body;
    799        }
    800    }
    801
    802    /**
    803     * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and
    804     * ']')
    805     *
    806     * @param stream
    807     * @return
    808     * @throws IOException If an I/O error occurs.
    809     */
    810    private StringReader getStreamUpToMatchingBracket(final StringReader stream) throws IOException {
    811        final StringBuffer sb = new StringBuffer();
    812        int foundBracket = -1;
    813        while (foundBracket != 0) {
    814            final char c = (char) stream.read();
    815            if (c == ']') {
    816                foundBracket++;
    817            }
    818            if (c == '[') {
    819                foundBracket--;
    820            }
    821            if (!(foundBracket == 0)) {
    822                sb.append(c);
    823            }
    824        }
    825        return new StringReader(sb.toString());
    826    }
    827
    828    private int readInteger(final int i, final InputStream stream) {
    829        int result = 0;
    830        for (int j = 0; j < i; j++) {
    831            try {
    832                result = result << 8 | stream.read();
    833            } catch (final IOException e) {
    834                throw new RuntimeException("Error reading unknown attribute");
    835            }
    836        }
    837        // use casting to preserve sign
    838        if (i == 1) {
    839            result = (byte) result;
    840        }
    841        if (i == 2) {
    842            result = (short) result;
    843        }
    844        return result;
    845    }
    846
    847    /**
    848     * Returns the {@link BHSDCodec} that should be used for the given layout element
    849     *
    850     * @param layoutElement
    851     */
    852    private BHSDCodec getCodec(final String layoutElement) {
    853        if (layoutElement.indexOf('O') >= 0) {
    854            return Codec.BRANCH5;
    855        }
    856        if (layoutElement.indexOf('P') >= 0) {
    857            return Codec.BCI5;
    858        }
    859        if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$
    860            && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
    861            return Codec.SIGNED5;
    862        }
    863        if (layoutElement.indexOf('B') >= 0) {
    864            return Codec.BYTE1;
    865        }
    866        return Codec.UNSIGNED5;
    867    }
    868
    869    /**
    870     * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and
    871     * ']')
    872     *
    873     * @param stream
    874     * @return
    875     * @throws IOException If an I/O error occurs.
    876     */
    877    private String readUpToMatchingBracket(final StringReader stream) throws IOException {
    878        final StringBuffer sb = new StringBuffer();
    879        int foundBracket = -1;
    880        while (foundBracket != 0) {
    881            final char c = (char) stream.read();
    882            if (c == ']') {
    883                foundBracket++;
    884            }
    885            if (c == '[') {
    886                foundBracket--;
    887            }
    888            if (!(foundBracket == 0)) {
    889                sb.append(c);
    890            }
    891        }
    892        return sb.toString();
    893    }
    894
    895    /**
    896     * Read a number from the stream and return it
    897     *
    898     * @param stream
    899     * @return
    900     * @throws IOException If an I/O error occurs.
    901     */
    902    private Integer readNumber(final StringReader stream) throws IOException {
    903        stream.mark(1);
    904        final char first = (char) stream.read();
    905        final boolean negative = first == '-';
    906        if (!negative) {
    907            stream.reset();
    908        }
    909        stream.mark(100);
    910        int i;
    911        int length = 0;
    912        while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) {
    913            length++;
    914        }
    915        stream.reset();
    916        if (length == 0) {
    917            return null;
    918        }
    919        final char[] digits = new char[length];
    920        final int read = stream.read(digits);
    921        if (read != digits.length) {
    922            throw new IOException("Error reading from the input stream");
    923        }
    924        return Integer.valueOf(Integer.parseInt((negative ? "-" : "") + new String(digits)));
    925    }
    926
    927    /**
    928     * Read a 'body' section of the layout from the given stream
    929     *
    930     * @param stream
    931     * @return List of LayoutElements
    932     * @throws IOException If an I/O error occurs.
    933     */
    934    private List readBody(final StringReader stream) throws IOException {
    935        final List layoutElements = new ArrayList();
    936        LayoutElement e;
    937        while ((e = readNextLayoutElement(stream)) != null) {
    938            layoutElements.add(e);
    939        }
    940        return layoutElements;
    941    }
    942
    943    /**
    944     * Renumber any bytecode indexes or offsets as described in section 5.5.2 of the pack200 specification
    945     *
    946     * @param bciRenumbering TODO
    947     * @param labelsToOffsets TODO
    948     */
    949    public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) {
    950        for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
    951            final AttributeLayoutElement element = (AttributeLayoutElement) iterator.next();
    952            element.renumberBci(bciRenumbering, labelsToOffsets);
    953        }
    954    }
    955
    956}