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}