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}