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.ByteArrayInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022
023import org.apache.commons.compress.harmony.pack200.BHSDCodec;
024import org.apache.commons.compress.harmony.pack200.Codec;
025import org.apache.commons.compress.harmony.pack200.Pack200Exception;
026import org.apache.commons.compress.utils.IOUtils;
027
028/**
029 * SegmentHeader is the header band of a {@link Segment}
030 */
031public class SegmentHeader {
032
033    private static final byte[] EMPTY_BYTE_ARRAY = {};
034
035    /**
036     * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they get their inspiration from ...
037     */
038    private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
039
040    private int archiveMajor;
041
042    private int archiveMinor;
043
044    private long archiveModtime;
045
046    private long archiveSize;
047
048    private int attributeDefinitionCount;
049
050    private InputStream bandHeadersInputStream;
051
052    private int bandHeadersSize;
053
054    private int classCount;
055
056    private int cpClassCount;
057
058    private int cpDescriptorCount;
059
060    private int cpDoubleCount;
061
062    private int cpFieldCount;
063
064    private int cpFloatCount;
065
066    private int cpIMethodCount;
067
068    private int cpIntCount;
069
070    private int cpLongCount;
071
072    private int cpMethodCount;
073
074    private int cpSignatureCount;
075
076    private int cpStringCount;
077
078    private int cpUTF8Count;
079
080    private int defaultClassMajorVersion;
081
082    private int defaultClassMinorVersion;
083
084    private int innerClassCount;
085
086    private int numberOfFiles;
087
088    private int segmentsRemaining;
089
090    private SegmentOptions options;
091
092    private final Segment segment;
093
094    private int archiveSizeOffset;
095
096    public SegmentHeader(final Segment segment) {
097        this.segment = segment;
098    }
099
100    /**
101     * Decode a scalar from the band file. A scalar is like a band, but does not perform any band code switching.
102     *
103     * @param name  the name of the scalar (primarily for logging/debugging purposes)
104     * @param in    the input stream to read from
105     * @param codec the codec for this scalar
106     * @return the decoded value
107     * @throws IOException      if there is a problem reading from the underlying input stream
108     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
109     */
110    private int decodeScalar(final String name, final InputStream in, final BHSDCodec codec) throws IOException, Pack200Exception {
111        final int ret = codec.decode(in);
112        segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " as " + ret);
113        return ret;
114    }
115
116    /**
117     * Decode a number of scalars from the band file. A scalar is like a band, but does not perform any band code switching.
118     *
119     * @param name  the name of the scalar (primarily for logging/debugging purposes)
120     * @param in    the input stream to read from
121     * @param codec the codec for this scalar
122     * @return an array of decoded {@code long[]} values
123     * @throws IOException      if there is a problem reading from the underlying input stream
124     * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
125     */
126    private int[] decodeScalar(final String name, final InputStream in, final BHSDCodec codec, final int n) throws IOException, Pack200Exception {
127        segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " (" + n + ")");
128        return codec.decodeInts(n, in);
129    }
130
131    public long getArchiveModtime() {
132        return archiveModtime;
133    }
134
135    public long getArchiveSize() {
136        return archiveSize;
137    }
138
139    public int getArchiveSizeOffset() {
140        return archiveSizeOffset;
141    }
142
143    public int getAttributeDefinitionCount() {
144        return attributeDefinitionCount;
145    }
146
147    /**
148     * Obtain the band headers data as an input stream. If no band headers are present, this will return an empty input stream to prevent any further reads
149     * taking place.
150     *
151     * Note that as a stream, data consumed from this input stream can't be re-used. Data is only read from this stream if the encoding is such that additional
152     * information needs to be decoded from the stream itself.
153     *
154     * @return the band headers input stream
155     */
156    public InputStream getBandHeadersInputStream() {
157        if (bandHeadersInputStream == null) {
158            bandHeadersInputStream = new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
159        }
160        return bandHeadersInputStream;
161
162    }
163
164    public int getBandHeadersSize() {
165        return bandHeadersSize;
166    }
167
168    public int getClassCount() {
169        return classCount;
170    }
171
172    public int getCpClassCount() {
173        return cpClassCount;
174    }
175
176    public int getCpDescriptorCount() {
177        return cpDescriptorCount;
178    }
179
180    public int getCpDoubleCount() {
181        return cpDoubleCount;
182    }
183
184    public int getCpFieldCount() {
185        return cpFieldCount;
186    }
187
188    public int getCpFloatCount() {
189        return cpFloatCount;
190    }
191
192    public int getCpIMethodCount() {
193        return cpIMethodCount;
194    }
195
196    public int getCpIntCount() {
197        return cpIntCount;
198    }
199
200    public int getCpLongCount() {
201        return cpLongCount;
202    }
203
204    public int getCpMethodCount() {
205        return cpMethodCount;
206    }
207
208    public int getCpSignatureCount() {
209        return cpSignatureCount;
210    }
211
212    public int getCpStringCount() {
213        return cpStringCount;
214    }
215
216    public int getCpUTF8Count() {
217        return cpUTF8Count;
218    }
219
220    public int getDefaultClassMajorVersion() {
221        return defaultClassMajorVersion;
222    }
223
224    public int getDefaultClassMinorVersion() {
225        return defaultClassMinorVersion;
226    }
227
228    public int getInnerClassCount() {
229        return innerClassCount;
230    }
231
232    public int getNumberOfFiles() {
233        return numberOfFiles;
234    }
235
236    public SegmentOptions getOptions() {
237        return options;
238    }
239
240    public int getSegmentsRemaining() {
241        return segmentsRemaining;
242    }
243
244    private void parseArchiveFileCounts(final InputStream in) throws IOException, Pack200Exception {
245        if (options.hasArchiveFileCounts()) {
246            setArchiveSize((long) decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5));
247            archiveSizeOffset = in.available();
248            setSegmentsRemaining(decodeScalar("archive_next_count", in, Codec.UNSIGNED5));
249            setArchiveModtime(decodeScalar("archive_modtime", in, Codec.UNSIGNED5));
250            numberOfFiles = decodeScalar("file_count", in, Codec.UNSIGNED5);
251        }
252    }
253
254    private void parseArchiveSpecialCounts(final InputStream in) throws IOException, Pack200Exception {
255        if (getOptions().hasSpecialFormats()) {
256            bandHeadersSize = decodeScalar("band_headers_size", in, Codec.UNSIGNED5);
257            setAttributeDefinitionCount(decodeScalar("attr_definition_count", in, Codec.UNSIGNED5));
258        }
259    }
260
261    private void parseClassCounts(final InputStream in) throws IOException, Pack200Exception {
262        innerClassCount = decodeScalar("ic_count", in, Codec.UNSIGNED5);
263        defaultClassMinorVersion = decodeScalar("default_class_minver", in, Codec.UNSIGNED5);
264        defaultClassMajorVersion = decodeScalar("default_class_majver", in, Codec.UNSIGNED5);
265        classCount = decodeScalar("class_count", in, Codec.UNSIGNED5);
266    }
267
268    private void parseCpCounts(final InputStream in) throws IOException, Pack200Exception {
269        cpUTF8Count = decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5);
270        if (getOptions().hasCPNumberCounts()) {
271            cpIntCount = decodeScalar("cp_Int_count", in, Codec.UNSIGNED5);
272            cpFloatCount = decodeScalar("cp_Float_count", in, Codec.UNSIGNED5);
273            cpLongCount = decodeScalar("cp_Long_count", in, Codec.UNSIGNED5);
274            cpDoubleCount = decodeScalar("cp_Double_count", in, Codec.UNSIGNED5);
275        }
276        cpStringCount = decodeScalar("cp_String_count", in, Codec.UNSIGNED5);
277        cpClassCount = decodeScalar("cp_Class_count", in, Codec.UNSIGNED5);
278        cpSignatureCount = decodeScalar("cp_Signature_count", in, Codec.UNSIGNED5);
279        cpDescriptorCount = decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5);
280        cpFieldCount = decodeScalar("cp_Field_count", in, Codec.UNSIGNED5);
281        cpMethodCount = decodeScalar("cp_Method_count", in, Codec.UNSIGNED5);
282        cpIMethodCount = decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5);
283    }
284
285    public void read(final InputStream in) throws IOException, Error, Pack200Exception {
286
287        final int[] word = decodeScalar("archive_magic_word", in, Codec.BYTE1, magic.length);
288        for (int m = 0; m < magic.length; m++) {
289            if (word[m] != magic[m]) {
290                throw new Error("Bad header");
291            }
292        }
293        setArchiveMinorVersion(decodeScalar("archive_minver", in, Codec.UNSIGNED5));
294        setArchiveMajorVersion(decodeScalar("archive_majver", in, Codec.UNSIGNED5));
295        options = new SegmentOptions(decodeScalar("archive_options", in, Codec.UNSIGNED5));
296        parseArchiveFileCounts(in);
297        parseArchiveSpecialCounts(in);
298        parseCpCounts(in);
299        parseClassCounts(in);
300
301        if (getBandHeadersSize() > 0) {
302            setBandHeadersData(IOUtils.readRange(in, getBandHeadersSize()));
303        }
304
305        archiveSizeOffset -= in.available();
306    }
307
308    /**
309     * Sets the major version of this archive.
310     *
311     * @param version the minor version of the archive
312     * @throws Pack200Exception if the major version is not 150
313     */
314    private void setArchiveMajorVersion(final int version) throws Pack200Exception {
315        if (version != 150) {
316            throw new Pack200Exception("Invalid segment major version: " + version);
317        }
318        archiveMajor = version;
319    }
320
321    /**
322     * Sets the minor version of this archive
323     *
324     * @param version the minor version of the archive
325     * @throws Pack200Exception if the minor version is not 7
326     */
327    private void setArchiveMinorVersion(final int version) throws Pack200Exception {
328        if (version != 7) {
329            throw new Pack200Exception("Invalid segment minor version");
330        }
331        archiveMinor = version;
332    }
333
334    public void setArchiveModtime(final long archiveModtime) {
335        this.archiveModtime = archiveModtime;
336    }
337
338    public void setArchiveSize(final long archiveSize) {
339        this.archiveSize = archiveSize;
340    }
341
342    private void setAttributeDefinitionCount(final long valuie) {
343        this.attributeDefinitionCount = (int) valuie;
344    }
345
346    private void setBandHeadersData(final byte[] bandHeaders) {
347        this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders);
348    }
349
350    public void setSegmentsRemaining(final long value) {
351        segmentsRemaining = (int) value;
352    }
353
354    public void unpack() {
355
356    }
357}