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