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