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