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}