View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.unpack200;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  
23  import org.apache.commons.compress.harmony.pack200.BHSDCodec;
24  import org.apache.commons.compress.harmony.pack200.Codec;
25  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
26  import org.apache.commons.compress.utils.IOUtils;
27  
28  /**
29   * SegmentHeader is the header band of a {@link Segment}
30   */
31  public class SegmentHeader {
32  
33      private static final byte[] EMPTY_BYTE_ARRAY = {};
34  
35      /**
36       * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they get their inspiration from ...
37       */
38      private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };
39  
40      private int archiveMajor;
41  
42      private int archiveMinor;
43  
44      private long archiveModtime;
45  
46      private long archiveSize;
47  
48      private int attributeDefinitionCount;
49  
50      private InputStream bandHeadersInputStream;
51  
52      private int bandHeadersSize;
53  
54      private int classCount;
55  
56      private int cpClassCount;
57  
58      private int cpDescriptorCount;
59  
60      private int cpDoubleCount;
61  
62      private int cpFieldCount;
63  
64      private int cpFloatCount;
65  
66      private int cpIMethodCount;
67  
68      private int cpIntCount;
69  
70      private int cpLongCount;
71  
72      private int cpMethodCount;
73  
74      private int cpSignatureCount;
75  
76      private int cpStringCount;
77  
78      private int cpUTF8Count;
79  
80      private int defaultClassMajorVersion;
81  
82      private int defaultClassMinorVersion;
83  
84      private int innerClassCount;
85  
86      private int numberOfFiles;
87  
88      private int segmentsRemaining;
89  
90      private SegmentOptions options;
91  
92      private final Segment segment;
93  
94      private int archiveSizeOffset;
95  
96      public SegmentHeader(final Segment segment) {
97          this.segment = segment;
98      }
99  
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 }