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}