SegmentHeader.java

  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. import java.io.ByteArrayInputStream;
  19. import java.io.IOException;
  20. import java.io.InputStream;

  21. import org.apache.commons.compress.harmony.pack200.BHSDCodec;
  22. import org.apache.commons.compress.harmony.pack200.Codec;
  23. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  24. import org.apache.commons.compress.utils.IOUtils;

  25. /**
  26.  * SegmentHeader is the header band of a {@link Segment}
  27.  */
  28. public class SegmentHeader {

  29.     private static final byte[] EMPTY_BYTE_ARRAY = {};

  30.     /**
  31.      * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they get their inspiration from ...
  32.      */
  33.     private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D };

  34.     private int archiveMajor;

  35.     private int archiveMinor;

  36.     private long archiveModtime;

  37.     private long archiveSize;

  38.     private int attributeDefinitionCount;

  39.     private InputStream bandHeadersInputStream;

  40.     private int bandHeadersSize;

  41.     private int classCount;

  42.     private int cpClassCount;

  43.     private int cpDescriptorCount;

  44.     private int cpDoubleCount;

  45.     private int cpFieldCount;

  46.     private int cpFloatCount;

  47.     private int cpIMethodCount;

  48.     private int cpIntCount;

  49.     private int cpLongCount;

  50.     private int cpMethodCount;

  51.     private int cpSignatureCount;

  52.     private int cpStringCount;

  53.     private int cpUTF8Count;

  54.     private int defaultClassMajorVersion;

  55.     private int defaultClassMinorVersion;

  56.     private int innerClassCount;

  57.     private int numberOfFiles;

  58.     private int segmentsRemaining;

  59.     private SegmentOptions options;

  60.     private final Segment segment;

  61.     private int archiveSizeOffset;

  62.     public SegmentHeader(final Segment segment) {
  63.         this.segment = segment;
  64.     }

  65.     /**
  66.      * Decode a scalar from the band file. A scalar is like a band, but does not perform any band code switching.
  67.      *
  68.      * @param name  the name of the scalar (primarily for logging/debugging purposes)
  69.      * @param in    the input stream to read from
  70.      * @param codec the codec for this scalar
  71.      * @return the decoded value
  72.      * @throws IOException      if there is a problem reading from the underlying input stream
  73.      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
  74.      */
  75.     private int decodeScalar(final String name, final InputStream in, final BHSDCodec codec) throws IOException, Pack200Exception {
  76.         final int ret = codec.decode(in);
  77.         segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " as " + ret);
  78.         return ret;
  79.     }

  80.     /**
  81.      * Decode a number of scalars from the band file. A scalar is like a band, but does not perform any band code switching.
  82.      *
  83.      * @param name  the name of the scalar (primarily for logging/debugging purposes)
  84.      * @param in    the input stream to read from
  85.      * @param codec the codec for this scalar
  86.      * @return an array of decoded {@code long[]} values
  87.      * @throws IOException      if there is a problem reading from the underlying input stream
  88.      * @throws Pack200Exception if there is a problem decoding the value or that the value is invalid
  89.      */
  90.     private int[] decodeScalar(final String name, final InputStream in, final BHSDCodec codec, final int n) throws IOException, Pack200Exception {
  91.         segment.log(Segment.LOG_LEVEL_VERBOSE, "Parsed #" + name + " (" + n + ")");
  92.         return codec.decodeInts(n, in);
  93.     }

  94.     public long getArchiveModtime() {
  95.         return archiveModtime;
  96.     }

  97.     public long getArchiveSize() {
  98.         return archiveSize;
  99.     }

  100.     public int getArchiveSizeOffset() {
  101.         return archiveSizeOffset;
  102.     }

  103.     public int getAttributeDefinitionCount() {
  104.         return attributeDefinitionCount;
  105.     }

  106.     /**
  107.      * 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
  108.      * taking place.
  109.      *
  110.      * 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
  111.      * information needs to be decoded from the stream itself.
  112.      *
  113.      * @return the band headers input stream
  114.      */
  115.     public InputStream getBandHeadersInputStream() {
  116.         if (bandHeadersInputStream == null) {
  117.             bandHeadersInputStream = new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
  118.         }
  119.         return bandHeadersInputStream;

  120.     }

  121.     public int getBandHeadersSize() {
  122.         return bandHeadersSize;
  123.     }

  124.     public int getClassCount() {
  125.         return classCount;
  126.     }

  127.     public int getCpClassCount() {
  128.         return cpClassCount;
  129.     }

  130.     public int getCpDescriptorCount() {
  131.         return cpDescriptorCount;
  132.     }

  133.     public int getCpDoubleCount() {
  134.         return cpDoubleCount;
  135.     }

  136.     public int getCpFieldCount() {
  137.         return cpFieldCount;
  138.     }

  139.     public int getCpFloatCount() {
  140.         return cpFloatCount;
  141.     }

  142.     public int getCpIMethodCount() {
  143.         return cpIMethodCount;
  144.     }

  145.     public int getCpIntCount() {
  146.         return cpIntCount;
  147.     }

  148.     public int getCpLongCount() {
  149.         return cpLongCount;
  150.     }

  151.     public int getCpMethodCount() {
  152.         return cpMethodCount;
  153.     }

  154.     public int getCpSignatureCount() {
  155.         return cpSignatureCount;
  156.     }

  157.     public int getCpStringCount() {
  158.         return cpStringCount;
  159.     }

  160.     public int getCpUTF8Count() {
  161.         return cpUTF8Count;
  162.     }

  163.     public int getDefaultClassMajorVersion() {
  164.         return defaultClassMajorVersion;
  165.     }

  166.     public int getDefaultClassMinorVersion() {
  167.         return defaultClassMinorVersion;
  168.     }

  169.     public int getInnerClassCount() {
  170.         return innerClassCount;
  171.     }

  172.     public int getNumberOfFiles() {
  173.         return numberOfFiles;
  174.     }

  175.     public SegmentOptions getOptions() {
  176.         return options;
  177.     }

  178.     public int getSegmentsRemaining() {
  179.         return segmentsRemaining;
  180.     }

  181.     private void parseArchiveFileCounts(final InputStream in) throws IOException, Pack200Exception {
  182.         if (options.hasArchiveFileCounts()) {
  183.             setArchiveSize((long) decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5));
  184.             archiveSizeOffset = in.available();
  185.             setSegmentsRemaining(decodeScalar("archive_next_count", in, Codec.UNSIGNED5));
  186.             setArchiveModtime(decodeScalar("archive_modtime", in, Codec.UNSIGNED5));
  187.             numberOfFiles = decodeScalar("file_count", in, Codec.UNSIGNED5);
  188.         }
  189.     }

  190.     private void parseArchiveSpecialCounts(final InputStream in) throws IOException, Pack200Exception {
  191.         if (getOptions().hasSpecialFormats()) {
  192.             bandHeadersSize = decodeScalar("band_headers_size", in, Codec.UNSIGNED5);
  193.             setAttributeDefinitionCount(decodeScalar("attr_definition_count", in, Codec.UNSIGNED5));
  194.         }
  195.     }

  196.     private void parseClassCounts(final InputStream in) throws IOException, Pack200Exception {
  197.         innerClassCount = decodeScalar("ic_count", in, Codec.UNSIGNED5);
  198.         defaultClassMinorVersion = decodeScalar("default_class_minver", in, Codec.UNSIGNED5);
  199.         defaultClassMajorVersion = decodeScalar("default_class_majver", in, Codec.UNSIGNED5);
  200.         classCount = decodeScalar("class_count", in, Codec.UNSIGNED5);
  201.     }

  202.     private void parseCpCounts(final InputStream in) throws IOException, Pack200Exception {
  203.         cpUTF8Count = decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5);
  204.         if (getOptions().hasCPNumberCounts()) {
  205.             cpIntCount = decodeScalar("cp_Int_count", in, Codec.UNSIGNED5);
  206.             cpFloatCount = decodeScalar("cp_Float_count", in, Codec.UNSIGNED5);
  207.             cpLongCount = decodeScalar("cp_Long_count", in, Codec.UNSIGNED5);
  208.             cpDoubleCount = decodeScalar("cp_Double_count", in, Codec.UNSIGNED5);
  209.         }
  210.         cpStringCount = decodeScalar("cp_String_count", in, Codec.UNSIGNED5);
  211.         cpClassCount = decodeScalar("cp_Class_count", in, Codec.UNSIGNED5);
  212.         cpSignatureCount = decodeScalar("cp_Signature_count", in, Codec.UNSIGNED5);
  213.         cpDescriptorCount = decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5);
  214.         cpFieldCount = decodeScalar("cp_Field_count", in, Codec.UNSIGNED5);
  215.         cpMethodCount = decodeScalar("cp_Method_count", in, Codec.UNSIGNED5);
  216.         cpIMethodCount = decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5);
  217.     }

  218.     public void read(final InputStream in) throws IOException, Error, Pack200Exception {

  219.         final int[] word = decodeScalar("archive_magic_word", in, Codec.BYTE1, magic.length);
  220.         for (int m = 0; m < magic.length; m++) {
  221.             if (word[m] != magic[m]) {
  222.                 throw new Error("Bad header");
  223.             }
  224.         }
  225.         setArchiveMinorVersion(decodeScalar("archive_minver", in, Codec.UNSIGNED5));
  226.         setArchiveMajorVersion(decodeScalar("archive_majver", in, Codec.UNSIGNED5));
  227.         options = new SegmentOptions(decodeScalar("archive_options", in, Codec.UNSIGNED5));
  228.         parseArchiveFileCounts(in);
  229.         parseArchiveSpecialCounts(in);
  230.         parseCpCounts(in);
  231.         parseClassCounts(in);

  232.         if (getBandHeadersSize() > 0) {
  233.             setBandHeadersData(IOUtils.readRange(in, getBandHeadersSize()));
  234.         }

  235.         archiveSizeOffset -= in.available();
  236.     }

  237.     /**
  238.      * Sets the major version of this archive.
  239.      *
  240.      * @param version the minor version of the archive
  241.      * @throws Pack200Exception if the major version is not 150
  242.      */
  243.     private void setArchiveMajorVersion(final int version) throws Pack200Exception {
  244.         if (version != 150) {
  245.             throw new Pack200Exception("Invalid segment major version: " + version);
  246.         }
  247.         archiveMajor = version;
  248.     }

  249.     /**
  250.      * Sets the minor version of this archive
  251.      *
  252.      * @param version the minor version of the archive
  253.      * @throws Pack200Exception if the minor version is not 7
  254.      */
  255.     private void setArchiveMinorVersion(final int version) throws Pack200Exception {
  256.         if (version != 7) {
  257.             throw new Pack200Exception("Invalid segment minor version");
  258.         }
  259.         archiveMinor = version;
  260.     }

  261.     public void setArchiveModtime(final long archiveModtime) {
  262.         this.archiveModtime = archiveModtime;
  263.     }

  264.     public void setArchiveSize(final long archiveSize) {
  265.         this.archiveSize = archiveSize;
  266.     }

  267.     private void setAttributeDefinitionCount(final long valuie) {
  268.         this.attributeDefinitionCount = (int) valuie;
  269.     }

  270.     private void setBandHeadersData(final byte[] bandHeaders) {
  271.         this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders);
  272.     }

  273.     public void setSegmentsRemaining(final long value) {
  274.         segmentsRemaining = (int) value;
  275.     }

  276.     public void unpack() {

  277.     }
  278. }