    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.pack200;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021
    022/**
    023 * SegmentHeader is the header band of a {@link Segment}. Corresponds to <code>segment_header</code> in the pack200
    024 * specification.
    025 */
    026public class SegmentHeader extends BandSet {
    027
    028    /**
    029     * Create a new SegmentHeader
    030     */
    031    public SegmentHeader() {
    032        super(1, null); // Pass 1 for effort because bands in the segment header
    033                        // should always use the default encoding
    034    }
    035
    036    private static final int[] magic = {0xCA, 0xFE, 0xD0, 0x0D};
    037    private static final int archive_minver = 7;
    038    private static final int archive_majver = 150;
    039
    040    private int archive_options;
    041
    042    private int cp_Utf8_count;
    043    private int cp_Int_count;
    044    private int cp_Float_count;
    045    private int cp_Long_count;
    046    private int cp_Double_count;
    047    private int cp_String_count;
    048    private int cp_Class_count;
    049    private int cp_Signature_count;
    050    private int cp_Descr_count;
    051    private int cp_Field_count;
    052    private int cp_Method_count;
    053    private int cp_Imethod_count;
    054
    055    private int attribute_definition_count;
    056    private final IntList band_headers = new IntList();
    057
    058    private boolean have_all_code_flags = true; // true by default
    059
    060    private int archive_size_hi;
    061    private int archive_size_lo;
    062    private int archive_next_count;
    063    private int archive_modtime;
    064    private int file_count;
    065
    066    private boolean deflate_hint;
    067    private final boolean have_file_modtime = true;
    068    private final boolean have_file_options = true;
    069    private boolean have_file_size_hi;
    070    private boolean have_class_flags_hi;
    071    private boolean have_field_flags_hi;
    072    private boolean have_method_flags_hi;
    073    private boolean have_code_flags_hi;
    074
    075    private int ic_count;
    076    private int class_count;
    077    private final Counter majverCounter = new Counter();
    078
    079    /**
    080     * Encode and write the SegmentHeader bands to the OutputStream
    081     */
    082    @Override
    083    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    084        out.write(encodeScalar(magic, Codec.BYTE1));
    085        out.write(encodeScalar(archive_minver, Codec.UNSIGNED5));
    086        out.write(encodeScalar(archive_majver, Codec.UNSIGNED5));
    087        calculateArchiveOptions();
    088        out.write(encodeScalar(archive_options, Codec.UNSIGNED5));
    089        writeArchiveFileCounts(out);
    090        writeArchiveSpecialCounts(out);
    091        writeCpCounts(out);
    092        writeClassCounts(out);
    093        if (band_headers.size() > 0) {
    094            out.write(encodeScalar(band_headers.toArray(), Codec.BYTE1));
    095        }
    096    }
    097
    098    private void calculateArchiveOptions() {
    099        if (attribute_definition_count > 0 || band_headers.size() > 0) {
    100            archive_options |= 1;
    101        }
    102        if (cp_Int_count > 0 || cp_Float_count > 0 || cp_Long_count > 0 || cp_Double_count > 0) {
    103            archive_options |= (1 << 1);
    104        }
    105        if (have_all_code_flags) {
    106            archive_options |= (1 << 2);
    107        }
    108        if (file_count > 0) {
    109            archive_options |= (1 << 4);
    110        }
    111        if (deflate_hint) {
    112            archive_options |= (1 << 5);
    113        }
    114        if (have_file_modtime) {
    115            archive_options |= (1 << 6);
    116        }
    117        if (have_file_options) {
    118            archive_options |= (1 << 7);
    119        }
    120        if (have_file_size_hi) {
    121            archive_options |= (1 << 8);
    122        }
    123        if (have_class_flags_hi) {
    124            archive_options |= (1 << 9);
    125        }
    126        if (have_field_flags_hi) {
    127            archive_options |= (1 << 10);
    128        }
    129        if (have_method_flags_hi) {
    130            archive_options |= (1 << 11);
    131        }
    132        if (have_code_flags_hi) {
    133            archive_options |= (1 << 12);
    134        }
    135    }
    136
    137    public void setCp_Utf8_count(final int count) {
    138        cp_Utf8_count = count;
    139    }
    140
    141    public void setCp_Int_count(final int count) {
    142        cp_Int_count = count;
    143    }
    144
    145    public void setCp_Float_count(final int count) {
    146        cp_Float_count = count;
    147    }
    148
    149    public void setCp_Long_count(final int count) {
    150        cp_Long_count = count;
    151    }
    152
    153    public void setCp_Double_count(final int count) {
    154        cp_Double_count = count;
    155    }
    156
    157    public void setCp_String_count(final int count) {
    158        cp_String_count = count;
    159    }
    160
    161    public void setCp_Class_count(final int count) {
    162        cp_Class_count = count;
    163    }
    164
    165    public void setCp_Signature_count(final int count) {
    166        cp_Signature_count = count;
    167    }
    168
    169    public void setCp_Descr_count(final int count) {
    170        cp_Descr_count = count;
    171    }
    172
    173    public void setCp_Field_count(final int count) {
    174        cp_Field_count = count;
    175    }
    176
    177    public void setCp_Method_count(final int count) {
    178        cp_Method_count = count;
    179    }
    180
    181    public void setCp_Imethod_count(final int count) {
    182        cp_Imethod_count = count;
    183    }
    184
    185    public void setAttribute_definition_count(final int attribute_definition_count) {
    186        this.attribute_definition_count = attribute_definition_count;
    187    }
    188
    189    public void setHave_all_code_flags(final boolean have_all_code_flags) {
    190        this.have_all_code_flags = have_all_code_flags;
    191    }
    192
    193    public int getArchive_modtime() {
    194        return archive_modtime;
    195    }
    196
    197    public void setFile_count(final int file_count) {
    198        this.file_count = file_count;
    199    }
    200
    201    public void setDeflate_hint(final boolean deflate_hint) {
    202        this.deflate_hint = deflate_hint;
    203    }
    204
    205    public void setHave_class_flags_hi(final boolean have_class_flags_hi) {
    206        this.have_class_flags_hi = have_class_flags_hi;
    207    }
    208
    209    public void setHave_field_flags_hi(final boolean have_field_flags_hi) {
    210        this.have_field_flags_hi = have_field_flags_hi;
    211    }
    212
    213    public void setHave_method_flags_hi(final boolean have_method_flags_hi) {
    214        this.have_method_flags_hi = have_method_flags_hi;
    215    }
    216
    217    public void setHave_code_flags_hi(final boolean have_code_flags_hi) {
    218        this.have_code_flags_hi = have_code_flags_hi;
    219    }
    220
    221    public boolean have_class_flags_hi() {
    222        return have_class_flags_hi;
    223    }
    224
    225    public boolean have_field_flags_hi() {
    226        return have_field_flags_hi;
    227    }
    228
    229    public boolean have_method_flags_hi() {
    230        return have_method_flags_hi;
    231    }
    232
    233    public boolean have_code_flags_hi() {
    234        return have_code_flags_hi;
    235    }
    236
    237    public void setIc_count(final int ic_count) {
    238        this.ic_count = ic_count;
    239    }
    240
    241    public void setClass_count(final int class_count) {
    242        this.class_count = class_count;
    243    }
    244
    245    private void writeCpCounts(final OutputStream out) throws IOException, Pack200Exception {
    246        out.write(encodeScalar(cp_Utf8_count, Codec.UNSIGNED5));
    247        if ((archive_options & (1 << 1)) != 0) { // have_cp_numbers
    248            out.write(encodeScalar(cp_Int_count, Codec.UNSIGNED5));
    249            out.write(encodeScalar(cp_Float_count, Codec.UNSIGNED5));
    250            out.write(encodeScalar(cp_Long_count, Codec.UNSIGNED5));
    251            out.write(encodeScalar(cp_Double_count, Codec.UNSIGNED5));
    252        }
    253        out.write(encodeScalar(cp_String_count, Codec.UNSIGNED5));
    254        out.write(encodeScalar(cp_Class_count, Codec.UNSIGNED5));
    255        out.write(encodeScalar(cp_Signature_count, Codec.UNSIGNED5));
    256        out.write(encodeScalar(cp_Descr_count, Codec.UNSIGNED5));
    257        out.write(encodeScalar(cp_Field_count, Codec.UNSIGNED5));
    258        out.write(encodeScalar(cp_Method_count, Codec.UNSIGNED5));
    259        out.write(encodeScalar(cp_Imethod_count, Codec.UNSIGNED5));
    260    }
    261
    262    private void writeClassCounts(final OutputStream out) throws IOException, Pack200Exception {
    263        final int default_class_minver = 0;
    264        final int default_class_majver = majverCounter.getMostCommon();
    265        out.write(encodeScalar(ic_count, Codec.UNSIGNED5));
    266        out.write(encodeScalar(default_class_minver, Codec.UNSIGNED5));
    267        out.write(encodeScalar(default_class_majver, Codec.UNSIGNED5));
    268        out.write(encodeScalar(class_count, Codec.UNSIGNED5));
    269    }
    270
    271    private void writeArchiveSpecialCounts(final OutputStream out) throws IOException, Pack200Exception {
    272        if ((archive_options & 1) > 0) { // have_special_formats
    273            out.write(encodeScalar(band_headers.size(), Codec.UNSIGNED5));
    274            out.write(encodeScalar(attribute_definition_count, Codec.UNSIGNED5));
    275        }
    276    }
    277
    278    private void writeArchiveFileCounts(final OutputStream out) throws IOException, Pack200Exception {
    279        if ((archive_options & (1 << 4)) > 0) { // have_file_headers
    280            out.write(encodeScalar(archive_size_hi, Codec.UNSIGNED5));
    281            out.write(encodeScalar(archive_size_lo, Codec.UNSIGNED5));
    282            out.write(encodeScalar(archive_next_count, Codec.UNSIGNED5));
    283            out.write(encodeScalar(archive_modtime, Codec.UNSIGNED5));
    284            out.write(encodeScalar(file_count, Codec.UNSIGNED5));
    285        }
    286    }
    287
    288    public void addMajorVersion(final int major) {
    289        majverCounter.add(major);
    290    }
    291
    292    /**
    293     * Counter for major/minor class file numbers so we can work out the default
    294     */
    295    private class Counter {
    296
    297        private final int[] objs = new int[8];
    298        private final int[] counts = new int[8];
    299        private int length;
    300
    301        public void add(final int obj) {
    302            boolean found = false;
    303            for (int i = 0; i < length; i++) {
    304                if (objs[i] == obj) {
    305                    counts[i]++;
    306                    found = true;
    307                }
    308            }
    309            if (!found) {
    310                objs[length] = obj;
    311                counts[length] = 1;
    312                length++;
    313                if (length > objs.length - 1) {
    314                    final Object[] newArray = new Object[objs.length + 8];
    315                    System.arraycopy(objs, 0, newArray, 0, length);
    316                }
    317            }
    318        }
    319
    320        public int getMostCommon() {
    321            int returnIndex = 0;
    322            for (int i = 0; i < length; i++) {
    323                if (counts[i] > counts[returnIndex]) {
    324                    returnIndex = i;
    325                }
    326            }
    327            return objs[returnIndex];
    328        }
    329    }
    330
    331    public int getDefaultMajorVersion() {
    332        return majverCounter.getMostCommon();
    333    }
    334
    335    public boolean have_file_size_hi() {
    336        return have_file_size_hi;
    337    }
    338
    339    public boolean have_file_modtime() {
    340        return have_file_modtime;
    341    }
    342
    343    public boolean have_file_options() {
    344        return have_file_options;
    345    }
    346
    347    public boolean have_all_code_flags() {
    348        return have_all_code_flags;
    349    }
    350
    351    public void appendBandCodingSpecifier(final int specifier) {
    352        band_headers.add(specifier);
    353    }
    354
    355}