    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.unpack200;
    018
    019import java.io.IOException;
    020import java.io.InputStream;
    021import java.util.Arrays;
    022
    023import org.apache.commons.compress.harmony.pack200.BHSDCodec;
    024import org.apache.commons.compress.harmony.pack200.Codec;
    025import org.apache.commons.compress.harmony.pack200.CodecEncoding;
    026import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    027import org.apache.commons.compress.harmony.pack200.PopulationCodec;
    028import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
    029import org.apache.commons.compress.harmony.unpack200.bytecode.CPDouble;
    030import org.apache.commons.compress.harmony.unpack200.bytecode.CPFieldRef;
    031import org.apache.commons.compress.harmony.unpack200.bytecode.CPFloat;
    032import org.apache.commons.compress.harmony.unpack200.bytecode.CPInteger;
    033import org.apache.commons.compress.harmony.unpack200.bytecode.CPInterfaceMethodRef;
    034import org.apache.commons.compress.harmony.unpack200.bytecode.CPLong;
    035import org.apache.commons.compress.harmony.unpack200.bytecode.CPMethodRef;
    036import org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
    037import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
    038import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
    039
    040/**
    041 * Abstract superclass for a set of bands
    042 */
    043public abstract class BandSet {
    044
    045    public abstract void read(InputStream inputStream) throws IOException, Pack200Exception;
    046
    047    public abstract void unpack() throws IOException, Pack200Exception;
    048
    049    public void unpack(final InputStream in) throws IOException, Pack200Exception {
    050        read(in);
    051        unpack();
    052    }
    053
    054    protected Segment segment;
    055
    056    protected SegmentHeader header;
    057
    058    public BandSet(final Segment segment) {
    059        this.segment = segment;
    060        this.header = segment.getSegmentHeader();
    061    }
    062
    063    /**
    064     * Decode a band and return an array of <code>int</code> values
    065     *
    066     * @param name the name of the band (primarily for logging/debugging purposes)
    067     * @param in the InputStream to decode from
    068     * @param codec the default Codec for this band
    069     * @param count the number of elements to read
    070     * @return an array of decoded <code>int</code> values
    071     * @throws IOException if there is a problem reading from the underlying input stream
    072     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
    073     */
    074    public int[] decodeBandInt(final String name, final InputStream in, final BHSDCodec codec, final int count)
    075        throws IOException, Pack200Exception {
    076        int[] band;
    077        // Useful for debugging
    078//        if(count > 0) {
    079//            System.out.println("decoding " + name + " " + count);
    080//        }
    081        Codec codecUsed = codec;
    082        if (codec.getB() == 1 || count == 0) {
    083            return codec.decodeInts(count, in);
    084        }
    085        final int[] getFirst = codec.decodeInts(1, in);
    086        if (getFirst.length == 0) {
    087            return getFirst;
    088        }
    089        final int first = getFirst[0];
    090        if (codec.isSigned() && first >= -256 && first <= -1) {
    091            // Non-default codec should be used
    092            codecUsed = CodecEncoding.getCodec((-1 - first), header.getBandHeadersInputStream(), codec);
    093            band = codecUsed.decodeInts(count, in);
    094        } else if (!codec.isSigned() && first >= codec.getL() && first <= codec.getL() + 255) {
    095            // Non-default codec should be used
    096            codecUsed = CodecEncoding.getCodec(first - codec.getL(), header.getBandHeadersInputStream(), codec);
    097            band = codecUsed.decodeInts(count, in);
    098        } else {
    099            // First element should not be discarded
    100            band = codec.decodeInts(count - 1, in, first);
    101        }
    102        // Useful for debugging -E options:
    103        // if(!codecUsed.equals(codec)) {
    104        // int bytes = codecUsed.lastBandLength;
    105        // System.out.println(count + " " + name + " encoded with " + codecUsed + " " + bytes);
    106        // }
    107        if (codecUsed instanceof PopulationCodec) {
    108            final PopulationCodec popCodec = (PopulationCodec) codecUsed;
    109            final int[] favoured = (int[]) popCodec.getFavoured().clone();
    110            Arrays.sort(favoured);
    111            for (int i = 0; i < band.length; i++) {
    112                final boolean favouredValue = Arrays.binarySearch(favoured, band[i]) > -1;
    113                final Codec theCodec = favouredValue ? popCodec.getFavouredCodec() : popCodec.getUnfavouredCodec();
    114                if (theCodec instanceof BHSDCodec && ((BHSDCodec) theCodec).isDelta()) {
    115                    final BHSDCodec bhsd = (BHSDCodec) theCodec;
    116                    final long cardinality = bhsd.cardinality();
    117                    while (band[i] > bhsd.largest()) {
    118                        band[i] -= cardinality;
    119                    }
    120                    while (band[i] < bhsd.smallest()) {
    121                        band[i] += cardinality;
    122                    }
    123                }
    124            }
    125        }
    126        return band;
    127    }
    128
    129    /**
    130     * Decode a band and return an array of <code>int[]</code> values
    131     *
    132     * @param name the name of the band (primarily for logging/debugging purposes)
    133     * @param in the InputStream to decode from
    134     * @param defaultCodec the default codec for this band
    135     * @param counts the numbers of elements to read for each int array within the array to be returned
    136     * @return an array of decoded <code>int[]</code> values
    137     * @throws IOException if there is a problem reading from the underlying input stream
    138     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
    139     */
    140    public int[][] decodeBandInt(final String name, final InputStream in, final BHSDCodec defaultCodec,
    141        final int[] counts) throws IOException, Pack200Exception {
    142        final int[][] result = new int[counts.length][];
    143        int totalCount = 0;
    144        for (int i = 0; i < counts.length; i++) {
    145            totalCount += counts[i];
    146        }
    147        final int[] twoDResult = decodeBandInt(name, in, defaultCodec, totalCount);
    148        int index = 0;
    149        for (int i = 0; i < result.length; i++) {
    150            result[i] = new int[counts[i]];
    151            for (int j = 0; j < result[i].length; j++) {
    152                result[i][j] = twoDResult[index];
    153                index++;
    154            }
    155        }
    156        return result;
    157    }
    158
    159    public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec codec,
    160        final boolean hasHi) throws IOException, Pack200Exception {
    161        return parseFlags(name, in, new int[] {count}, (hasHi ? codec : null), codec)[0];
    162    }
    163
    164    public long[][] parseFlags(final String name, final InputStream in, final int counts[], final BHSDCodec codec,
    165        final boolean hasHi) throws IOException, Pack200Exception {
    166        return parseFlags(name, in, counts, (hasHi ? codec : null), codec);
    167    }
    168
    169    public long[] parseFlags(final String name, final InputStream in, final int count, final BHSDCodec hiCodec,
    170        final BHSDCodec loCodec) throws IOException, Pack200Exception {
    171        return parseFlags(name, in, new int[] {count}, hiCodec, loCodec)[0];
    172    }
    173
    174    public long[][] parseFlags(final String name, final InputStream in, final int counts[], final BHSDCodec hiCodec,
    175        final BHSDCodec loCodec) throws IOException, Pack200Exception {
    176        final int count = counts.length;
    177        if (count == 0) {
    178            return new long[][] {{}};
    179        }
    180        int sum = 0;
    181        final long[][] result = new long[count][];
    182        for (int i = 0; i < count; i++) {
    183            result[i] = new long[counts[i]];
    184            sum += counts[i];
    185        }
    186        int[] hi = null;
    187        int[] lo;
    188        if (hiCodec != null) {
    189            hi = decodeBandInt(name, in, hiCodec, sum);
    190            lo = decodeBandInt(name, in, loCodec, sum);
    191        } else {
    192            lo = decodeBandInt(name, in, loCodec, sum);
    193        }
    194
    195        int index = 0;
    196        for (int i = 0; i < result.length; i++) {
    197            for (int j = 0; j < result[i].length; j++) {
    198                if (hi != null) {
    199                    result[i][j] = ((long) hi[index] << 32) | (lo[index] & 4294967295L);
    200                } else {
    201                    result[i][j] = lo[index];
    202                }
    203                index++;
    204            }
    205        }
    206        return result;
    207    }
    208
    209    /**
    210     * Parses <i>count</i> references from <code>in</code>, using <code>codec</code> to decode the values as indexes
    211     * into <code>reference</code> (which is populated prior to this call). An exception is thrown if a decoded index
    212     * falls outside the range [0..reference.length-1].
    213     *
    214     * @param name the band name
    215     * @param in the input stream to read from
    216     * @param codec the BHSDCodec to use for decoding
    217     * @param count the number of references to decode
    218     * @param reference the array of values to use for the references
    219     * @return Parsed references.
    220     *
    221     * @throws IOException if a problem occurs during reading from the underlying stream
    222     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec
    223     */
    224    public String[] parseReferences(final String name, final InputStream in, final BHSDCodec codec, final int count,
    225        final String[] reference) throws IOException, Pack200Exception {
    226        return parseReferences(name, in, codec, new int[] {count}, reference)[0];
    227    }
    228
    229    /**
    230     * Parses <i>count</i> references from <code>in</code>, using <code>codec</code> to decode the values as indexes
    231     * into <code>reference</code> (which is populated prior to this call). An exception is thrown if a decoded index
    232     * falls outside the range [0..reference.length-1]. Unlike the other parseReferences, this post-processes the result
    233     * into an array of results.
    234     *
    235     * @param name TODO
    236     * @param in the input stream to read from
    237     * @param codec the BHSDCodec to use for decoding
    238     * @param counts the numbers of references to decode for each array entry
    239     * @param reference the array of values to use for the references
    240     * @return Parsed references.
    241     *
    242     * @throws IOException if a problem occurs during reading from the underlying stream
    243     * @throws Pack200Exception if a problem occurs with an unexpected value or unsupported Codec
    244     */
    245    public String[][] parseReferences(final String name, final InputStream in, final BHSDCodec codec,
    246        final int counts[], final String[] reference) throws IOException, Pack200Exception {
    247        final int count = counts.length;
    248        if (count == 0) {
    249            return new String[][] {{}};
    250        }
    251        final String[][] result = new String[count][];
    252        int sum = 0;
    253        for (int i = 0; i < count; i++) {
    254            result[i] = new String[counts[i]];
    255            sum += counts[i];
    256        }
    257        // TODO Merge the decode and parsing of a multiple structure into one
    258        final String[] result1 = new String[sum];
    259        final int[] indices = decodeBandInt(name, in, codec, sum);
    260        for (int i1 = 0; i1 < sum; i1++) {
    261            final int index = indices[i1];
    262            if (index < 0 || index >= reference.length) {
    263                throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index
    264                    + ", array size = " + reference.length);
    265            }
    266            result1[i1] = reference[index];
    267        }
    268        final String[] refs = result1;
    269        // TODO Merge the decode and parsing of a multiple structure into one
    270        int pos = 0;
    271        for (int i = 0; i < count; i++) {
    272            final int num = counts[i];
    273            result[i] = new String[num];
    274            System.arraycopy(refs, pos, result[i], 0, num);
    275            pos += num;
    276        }
    277        return result;
    278    }
    279
    280    public CPInteger[] parseCPIntReferences(final String name, final InputStream in, final BHSDCodec codec,
    281        final int count) throws IOException, Pack200Exception {
    282        final int[] reference = segment.getCpBands().getCpInt();
    283        final int[] indices = decodeBandInt(name, in, codec, count);
    284        final CPInteger[] result = new CPInteger[indices.length];
    285        for (int i1 = 0; i1 < count; i1++) {
    286            final int index = indices[i1];
    287            if (index < 0 || index >= reference.length) {
    288                throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index
    289                    + ", array size = " + reference.length);
    290            }
    291            result[i1] = segment.getCpBands().cpIntegerValue(index);
    292        }
    293        return result;
    294    }
    295
    296    public CPDouble[] parseCPDoubleReferences(final String name, final InputStream in, final BHSDCodec codec,
    297        final int count) throws IOException, Pack200Exception {
    298        final int[] indices = decodeBandInt(name, in, codec, count);
    299        final CPDouble[] result = new CPDouble[indices.length];
    300        for (int i1 = 0; i1 < count; i1++) {
    301            final int index = indices[i1];
    302            result[i1] = segment.getCpBands().cpDoubleValue(index);
    303        }
    304        return result;
    305    }
    306
    307    public CPFloat[] parseCPFloatReferences(final String name, final InputStream in, final BHSDCodec codec,
    308        final int count) throws IOException, Pack200Exception {
    309        final int[] indices = decodeBandInt(name, in, codec, count);
    310        final CPFloat[] result = new CPFloat[indices.length];
    311        for (int i1 = 0; i1 < count; i1++) {
    312            final int index = indices[i1];
    313            result[i1] = segment.getCpBands().cpFloatValue(index);
    314        }
    315        return result;
    316    }
    317
    318    public CPLong[] parseCPLongReferences(final String name, final InputStream in, final BHSDCodec codec,
    319        final int count) throws IOException, Pack200Exception {
    320        final long[] reference = segment.getCpBands().getCpLong();
    321        final int[] indices = decodeBandInt(name, in, codec, count);
    322        final CPLong[] result = new CPLong[indices.length];
    323        for (int i1 = 0; i1 < count; i1++) {
    324            final int index = indices[i1];
    325            if (index < 0 || index >= reference.length) {
    326                throw new Pack200Exception("Something has gone wrong during parsing references, index = " + index
    327                    + ", array size = " + reference.length);
    328            }
    329            result[i1] = segment.getCpBands().cpLongValue(index);
    330        }
    331        return result;
    332    }
    333
    334    public CPUTF8[] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec,
    335        final int count) throws IOException, Pack200Exception {
    336        final int[] indices = decodeBandInt(name, in, codec, count);
    337        final CPUTF8[] result = new CPUTF8[indices.length];
    338        for (int i1 = 0; i1 < count; i1++) {
    339            final int index = indices[i1];
    340            result[i1] = segment.getCpBands().cpUTF8Value(index);
    341        }
    342        return result;
    343    }
    344
    345    public CPUTF8[][] parseCPUTF8References(final String name, final InputStream in, final BHSDCodec codec,
    346        final int[] counts) throws IOException, Pack200Exception {
    347        final CPUTF8[][] result = new CPUTF8[counts.length][];
    348        int sum = 0;
    349        for (int i = 0; i < counts.length; i++) {
    350            result[i] = new CPUTF8[counts[i]];
    351            sum += counts[i];
    352        }
    353        final CPUTF8[] result1 = new CPUTF8[sum];
    354        final int[] indices = decodeBandInt(name, in, codec, sum);
    355        for (int i1 = 0; i1 < sum; i1++) {
    356            final int index = indices[i1];
    357            result1[i1] = segment.getCpBands().cpUTF8Value(index);
    358        }
    359        final CPUTF8[] refs = result1;
    360        int pos = 0;
    361        for (int i = 0; i < counts.length; i++) {
    362            final int num = counts[i];
    363            result[i] = new CPUTF8[num];
    364            System.arraycopy(refs, pos, result[i], 0, num);
    365            pos += num;
    366        }
    367        return result;
    368    }
    369
    370    public CPString[] parseCPStringReferences(final String name, final InputStream in, final BHSDCodec codec,
    371        final int count) throws IOException, Pack200Exception {
    372        final int[] indices = decodeBandInt(name, in, codec, count);
    373        final CPString[] result = new CPString[indices.length];
    374        for (int i1 = 0; i1 < count; i1++) {
    375            final int index = indices[i1];
    376            result[i1] = segment.getCpBands().cpStringValue(index);
    377        }
    378        return result;
    379    }
    380
    381    public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(final String name, final InputStream in,
    382        final BHSDCodec codec, final int count) throws IOException, Pack200Exception {
    383        final CpBands cpBands = segment.getCpBands();
    384        final int[] indices = decodeBandInt(name, in, codec, count);
    385        final CPInterfaceMethodRef[] result = new CPInterfaceMethodRef[indices.length];
    386        for (int i1 = 0; i1 < count; i1++) {
    387            final int index = indices[i1];
    388            result[i1] = cpBands.cpIMethodValue(index);
    389        }
    390        return result;
    391    }
    392
    393    public CPMethodRef[] parseCPMethodRefReferences(final String name, final InputStream in, final BHSDCodec codec,
    394        final int count) throws IOException, Pack200Exception {
    395        final CpBands cpBands = segment.getCpBands();
    396        final int[] indices = decodeBandInt(name, in, codec, count);
    397        final CPMethodRef[] result = new CPMethodRef[indices.length];
    398        for (int i1 = 0; i1 < count; i1++) {
    399            final int index = indices[i1];
    400            result[i1] = cpBands.cpMethodValue(index);
    401        }
    402        return result;
    403    }
    404
    405    public CPFieldRef[] parseCPFieldRefReferences(final String name, final InputStream in, final BHSDCodec codec,
    406        final int count) throws IOException, Pack200Exception {
    407        final CpBands cpBands = segment.getCpBands();
    408        final int[] indices = decodeBandInt(name, in, codec, count);
    409        final CPFieldRef[] result = new CPFieldRef[indices.length];
    410        for (int i1 = 0; i1 < count; i1++) {
    411            final int index = indices[i1];
    412            result[i1] = cpBands.cpFieldValue(index);
    413        }
    414        return result;
    415    }
    416
    417    public CPNameAndType[] parseCPDescriptorReferences(final String name, final InputStream in, final BHSDCodec codec,
    418        final int count) throws IOException, Pack200Exception {
    419        final CpBands cpBands = segment.getCpBands();
    420        final int[] indices = decodeBandInt(name, in, codec, count);
    421        final CPNameAndType[] result = new CPNameAndType[indices.length];
    422        for (int i1 = 0; i1 < count; i1++) {
    423            final int index = indices[i1];
    424            result[i1] = cpBands.cpNameAndTypeValue(index);
    425        }
    426        return result;
    427    }
    428
    429    public CPUTF8[] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec,
    430        final int count) throws IOException, Pack200Exception {
    431        final int[] indices = decodeBandInt(name, in, codec, count);
    432        final CPUTF8[] result = new CPUTF8[indices.length];
    433        for (int i1 = 0; i1 < count; i1++) {
    434            final int index = indices[i1];
    435            result[i1] = segment.getCpBands().cpSignatureValue(index);
    436        }
    437        return result;
    438    }
    439
    440    protected CPUTF8[][] parseCPSignatureReferences(final String name, final InputStream in, final BHSDCodec codec,
    441        final int[] counts) throws IOException, Pack200Exception {
    442        final CPUTF8[][] result = new CPUTF8[counts.length][];
    443        int sum = 0;
    444        for (int i = 0; i < counts.length; i++) {
    445            result[i] = new CPUTF8[counts[i]];
    446            sum += counts[i];
    447        }
    448        final CPUTF8[] result1 = new CPUTF8[sum];
    449        final int[] indices = decodeBandInt(name, in, codec, sum);
    450        for (int i1 = 0; i1 < sum; i1++) {
    451            final int index = indices[i1];
    452            result1[i1] = segment.getCpBands().cpSignatureValue(index);
    453        }
    454        final CPUTF8[] refs = result1;
    455        int pos = 0;
    456        for (int i = 0; i < counts.length; i++) {
    457            final int num = counts[i];
    458            result[i] = new CPUTF8[num];
    459            System.arraycopy(refs, pos, result[i], 0, num);
    460            pos += num;
    461        }
    462        return result;
    463    }
    464
    465    public CPClass[] parseCPClassReferences(final String name, final InputStream in, final BHSDCodec codec,
    466        final int count) throws IOException, Pack200Exception {
    467        final int[] indices = decodeBandInt(name, in, codec, count);
    468        final CPClass[] result = new CPClass[indices.length];
    469        for (int i1 = 0; i1 < count; i1++) {
    470            final int index = indices[i1];
    471            result[i1] = segment.getCpBands().cpClassValue(index);
    472        }
    473        return result;
    474    }
    475
    476    protected String[] getReferences(final int[] ints, final String[] reference) {
    477        final String[] result = new String[ints.length];
    478        for (int i = 0; i < result.length; i++) {
    479            result[i] = reference[ints[i]];
    480        }
    481        return result;
    482    }
    483
    484    protected String[][] getReferences(final int[][] ints, final String[] reference) {
    485        final String[][] result = new String[ints.length][];
    486        for (int i = 0; i < result.length; i++) {
    487            result[i] = new String[ints[i].length];
    488            for (int j = 0; j < result[i].length; j++) {
    489                result[i][j] = reference[ints[i][j]];
    490
    491            }
    492        }
    493        return result;
    494    }
    495
    496}