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}