View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.unpack200;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  import org.apache.commons.compress.harmony.pack200.BHSDCodec;
26  import org.apache.commons.compress.harmony.pack200.Codec;
27  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
28  import org.apache.commons.compress.utils.IOUtils;
29  
30  /**
31   * SegmentHeader is the header band of a {@link Segment}
32   */
33  public class SegmentHeader {
34  
35      private static final byte[] EMPTY_BYTE_ARRAY = {};
36  
37      /**
38       * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they get their inspiration from ...
39       */
40      private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
41  
42      private int archiveMajor;
43  
44      private int archiveMinor;
45  
46      private long archiveModtime;
47  
48      private long archiveSize;
49  
50      private int attributeDefinitionCount;
51  
52      private InputStream bandHeadersInputStream;
53  
54      private int bandHeadersSize;
55  
56      private int classCount;
57  
58      private int cpClassCount;
59  
60      private int cpDescriptorCount;
61  
62      private int cpDoubleCount;
63  
64      private int cpFieldCount;
65  
66      private int cpFloatCount;
67  
68      private int cpIMethodCount;
69  
70      private int cpIntCount;
71  
72      private int cpLongCount;
73  
74      private int cpMethodCount;
75  
76      private int cpSignatureCount;
77  
78      private int cpStringCount;
79  
80      private int cpUTF8Count;
81  
82      private int defaultClassMajorVersion;
83  
84      private int defaultClassMinorVersion;
85  
86      private int innerClassCount;
87  
88      private int numberOfFiles;
89  
90      private int segmentsRemaining;
91  
92      private SegmentOptions options;
93  
94      private final Segment segment;
95  
96      private int archiveSizeOffset;
97  
98      public SegmentHeader(final Segment segment) {
99          this.segment = segment;
100     }
101 
102     /**
103      * Decode a scalar from the band file. A scalar is like a band, but does not perform any band code switching.
104      *
105      * @param name  the name of the scalar (primarily for logging/debugging purposes)
106      * @param in    the input stream to read from
107      * @param codec the codec for this scalar
108      * @return the decoded value
109      * @throws IOException      if there is a problem reading from the underlying input stream
110      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
111      */
112     private int decodeScalar(final String name, final InputStream in, final BHSDCodec codec) throws IOException, Pack200Exception {
113         final int ret = codec.decode(in);
114         segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " as " + ret);
115         return ret;
116     }
117 
118     /**
119      * Decode a number of scalars from the band file. A scalar is like a band, but does not perform any band code switching.
120      *
121      * @param name  the name of the scalar (primarily for logging/debugging purposes)
122      * @param in    the input stream to read from
123      * @param codec the codec for this scalar
124      * @return an array of decoded {@code long[]} values
125      * @throws IOException      if there is a problem reading from the underlying input stream
126      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
127      */
128     private int[] decodeScalar(final String name, final InputStream in, final BHSDCodec codec, final int n) throws IOException, Pack200Exception {
129         segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " (" + n + ")");
130         return codec.decodeInts(n, in);
131     }
132 
133     public long getArchiveModtime() {
134         return archiveModtime;
135     }
136 
137     public long getArchiveSize() {
138         return archiveSize;
139     }
140 
141     public int getArchiveSizeOffset() {
142         return archiveSizeOffset;
143     }
144 
145     public int getAttributeDefinitionCount() {
146         return attributeDefinitionCount;
147     }
148 
149     /**
150      * 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
151      * taking place.
152      *
153      * 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
154      * information needs to be decoded from the stream itself.
155      *
156      * @return the band headers input stream
157      */
158     public InputStream getBandHeadersInputStream() {
159         if (bandHeadersInputStream == null) {
160             bandHeadersInputStream = new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
161         }
162         return bandHeadersInputStream;
163 
164     }
165 
166     public int getBandHeadersSize() {
167         return bandHeadersSize;
168     }
169 
170     public int getClassCount() {
171         return classCount;
172     }
173 
174     public int getCpClassCount() {
175         return cpClassCount;
176     }
177 
178     public int getCpDescriptorCount() {
179         return cpDescriptorCount;
180     }
181 
182     public int getCpDoubleCount() {
183         return cpDoubleCount;
184     }
185 
186     public int getCpFieldCount() {
187         return cpFieldCount;
188     }
189 
190     public int getCpFloatCount() {
191         return cpFloatCount;
192     }
193 
194     public int getCpIMethodCount() {
195         return cpIMethodCount;
196     }
197 
198     public int getCpIntCount() {
199         return cpIntCount;
200     }
201 
202     public int getCpLongCount() {
203         return cpLongCount;
204     }
205 
206     public int getCpMethodCount() {
207         return cpMethodCount;
208     }
209 
210     public int getCpSignatureCount() {
211         return cpSignatureCount;
212     }
213 
214     public int getCpStringCount() {
215         return cpStringCount;
216     }
217 
218     public int getCpUTF8Count() {
219         return cpUTF8Count;
220     }
221 
222     public int getDefaultClassMajorVersion() {
223         return defaultClassMajorVersion;
224     }
225 
226     public int getDefaultClassMinorVersion() {
227         return defaultClassMinorVersion;
228     }
229 
230     public int getInnerClassCount() {
231         return innerClassCount;
232     }
233 
234     public int getNumberOfFiles() {
235         return numberOfFiles;
236     }
237 
238     public SegmentOptions getOptions() {
239         return options;
240     }
241 
242     public int getSegmentsRemaining() {
243         return segmentsRemaining;
244     }
245 
246     private void parseArchiveFileCounts(final InputStream in) throws IOException, Pack200Exception {
247         if (options.hasArchiveFileCounts()) {
248             setArchiveSize((long) decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5));
249             archiveSizeOffset = in.available();
250             setSegmentsRemaining(decodeScalar("archive_next_count", in, Codec.UNSIGNED5));
251             setArchiveModtime(decodeScalar("archive_modtime", in, Codec.UNSIGNED5));
252             numberOfFiles = decodeScalar("file_count", in, Codec.UNSIGNED5);
253         }
254     }
255 
256     private void parseArchiveSpecialCounts(final InputStream in) throws IOException, Pack200Exception {
257         if (getOptions().hasSpecialFormats()) {
258             bandHeadersSize = decodeScalar("band_headers_size", in, Codec.UNSIGNED5);
259             setAttributeDefinitionCount(decodeScalar("attr_definition_count", in, Codec.UNSIGNED5));
260         }
261     }
262 
263     private void parseClassCounts(final InputStream in) throws IOException, Pack200Exception {
264         innerClassCount = decodeScalar("ic_count", in, Codec.UNSIGNED5);
265         defaultClassMinorVersion = decodeScalar("default_class_minver", in, Codec.UNSIGNED5);
266         defaultClassMajorVersion = decodeScalar("default_class_majver", in, Codec.UNSIGNED5);
267         classCount = decodeScalar("class_count", in, Codec.UNSIGNED5);
268     }
269 
270     private void parseCpCounts(final InputStream in) throws IOException, Pack200Exception {
271         cpUTF8Count = decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5);
272         if (getOptions().hasCPNumberCounts()) {
273             cpIntCount = decodeScalar("cp_Int_count", in, Codec.UNSIGNED5);
274             cpFloatCount = decodeScalar("cp_Float_count", in, Codec.UNSIGNED5);
275             cpLongCount = decodeScalar("cp_Long_count", in, Codec.UNSIGNED5);
276             cpDoubleCount = decodeScalar("cp_Double_count", in, Codec.UNSIGNED5);
277         }
278         cpStringCount = decodeScalar("cp_String_count", in, Codec.UNSIGNED5);
279         cpClassCount = decodeScalar("cp_Class_count", in, Codec.UNSIGNED5);
280         cpSignatureCount = decodeScalar("cp_Signature_count", in, Codec.UNSIGNED5);
281         cpDescriptorCount = decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5);
282         cpFieldCount = decodeScalar("cp_Field_count", in, Codec.UNSIGNED5);
283         cpMethodCount = decodeScalar("cp_Method_count", in, Codec.UNSIGNED5);
284         cpIMethodCount = decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5);
285     }
286 
287     public void read(final InputStream in) throws IOException, Error, Pack200Exception {
288 
289         final int[] word = decodeScalar("archive_magic_word", in, Codec.BYTE1, magic.length);
290         for (int m = 0; m < magic.length; m++) {
291             if (word[m] != magic[m]) {
292                 throw new Error("Bad header");
293             }
294         }
295         setArchiveMinorVersion(decodeScalar("archive_minver", in, Codec.UNSIGNED5));
296         setArchiveMajorVersion(decodeScalar("archive_majver", in, Codec.UNSIGNED5));
297         options = new SegmentOptions(decodeScalar("archive_options", in, Codec.UNSIGNED5));
298         parseArchiveFileCounts(in);
299         parseArchiveSpecialCounts(in);
300         parseCpCounts(in);
301         parseClassCounts(in);
302 
303         if (getBandHeadersSize() > 0) {
304             setBandHeadersData(IOUtils.readRange(in, getBandHeadersSize()));
305         }
306 
307         archiveSizeOffset -= in.available();
308     }
309 
310     /**
311      * Sets the major version of this archive.
312      *
313      * @param version the minor version of the archive
314      * @throws Pack200Exception if the major version is not 150
315      */
316     private void setArchiveMajorVersion(final int version) throws Pack200Exception {
317         if (version != 150) {
318             throw new Pack200Exception("Invalid segment major version: " + version);
319         }
320         archiveMajor = version;
321     }
322 
323     /**
324      * Sets the minor version of this archive
325      *
326      * @param version the minor version of the archive
327      * @throws Pack200Exception if the minor version is not 7
328      */
329     private void setArchiveMinorVersion(final int version) throws Pack200Exception {
330         if (version != 7) {
331             throw new Pack200Exception("Invalid segment minor version");
332         }
333         archiveMinor = version;
334     }
335 
336     public void setArchiveModtime(final long archiveModtime) {
337         this.archiveModtime = archiveModtime;
338     }
339 
340     public void setArchiveSize(final long archiveSize) {
341         this.archiveSize = archiveSize;
342     }
343 
344     private void setAttributeDefinitionCount(final long valuie) {
345         this.attributeDefinitionCount = (int) valuie;
346     }
347 
348     private void setBandHeadersData(final byte[] bandHeaders) {
349         this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders);
350     }
351 
352     public void setSegmentsRemaining(final long value) {
353         segmentsRemaining = (int) value;
354     }
355 
356     public void unpack() {
357 
358     }
359 }