    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;
    021import java.util.HashSet;
    022import java.util.Iterator;
    023import java.util.List;
    024import java.util.Set;
    025import java.util.TimeZone;
    026
    027import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
    028import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
    029import org.objectweb.asm.ClassReader;
    030
    031/**
    032 * Bands containing information about files in the pack200 archive and the file contents for non-class-files.
    033 * Corresponds to the <code>file_bands</code> set of bands described in the specification.
    034 */
    035public class FileBands extends BandSet {
    036
    037    private final CPUTF8[] fileName;
    038    private int[] file_name;
    039    private final int[] file_modtime;
    040    private final long[] file_size;
    041    private final int[] file_options;
    042    private final byte[][] file_bits;
    043    private final List fileList;
    044    private final PackingOptions options;
    045    private final CpBands cpBands;
    046
    047    public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options,
    048        final SegmentUnit segmentUnit, final int effort) {
    049        super(effort, segmentHeader);
    050        fileList = segmentUnit.getFileList();
    051        this.options = options;
    052        this.cpBands = cpBands;
    053        final int size = fileList.size();
    054        fileName = new CPUTF8[size];
    055        file_modtime = new int[size];
    056        file_size = new long[size];
    057        file_options = new int[size];
    058        int totalSize = 0;
    059        file_bits = new byte[size][];
    060        final int archiveModtime = segmentHeader.getArchive_modtime();
    061
    062        final Set classNames = new HashSet();
    063        for (final Iterator iterator = segmentUnit.getClassList().iterator(); iterator.hasNext();) {
    064            final ClassReader reader = (ClassReader) iterator.next();
    065            classNames.add(reader.getClassName());
    066        }
    067        final CPUTF8 emptyString = cpBands.getCPUtf8("");
    068        long modtime;
    069        int latestModtime = Integer.MIN_VALUE;
    070        final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime());
    071        for (int i = 0; i < size; i++) {
    072            final PackingFile packingFile = (PackingFile) fileList.get(i);
    073            final String name = packingFile.getName();
    074            if (name.endsWith(".class") && !options.isPassFile(name)) {
    075                file_options[i] |= (1 << 1);
    076                if (classNames.contains(name.substring(0, name.length() - 6))) {
    077                    fileName[i] = emptyString;
    078                } else {
    079                    fileName[i] = cpBands.getCPUtf8(name);
    080                }
    081            } else {
    082                fileName[i] = cpBands.getCPUtf8(name);
    083            }
    084            // set deflate_hint for file element
    085            if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
    086                file_options[i] |= 0x1;
    087            }
    088            final byte[] bytes = packingFile.getContents();
    089            file_size[i] = bytes.length;
    090            totalSize += file_size[i];
    091
    092            // update modification time
    093            modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
    094            file_modtime[i] = (int) (modtime - archiveModtime);
    095            if (isLatest && latestModtime < file_modtime[i]) {
    096                latestModtime = file_modtime[i];
    097            }
    098
    099            file_bits[i] = packingFile.getContents();
    100        }
    101
    102        if (isLatest) {
    103            for (int index = 0; index < size; index++) {
    104                file_modtime[index] = latestModtime;
    105            }
    106        }
    107    }
    108
    109    /**
    110     * All input classes for the segment have now been read in, so this method is called so that this class can
    111     * calculate/complete anything it could not do while classes were being read.
    112     */
    113    public void finaliseBands() {
    114        file_name = new int[fileName.length];
    115        for (int i = 0; i < file_name.length; i++) {
    116            if (fileName[i].equals(cpBands.getCPUtf8(""))) {
    117                final PackingFile packingFile = (PackingFile) fileList.get(i);
    118                final String name = packingFile.getName();
    119                if (options.isPassFile(name)) {
    120                    fileName[i] = cpBands.getCPUtf8(name);
    121                    file_options[i] &= (1 << 1) ^ 0xFFFFFFFF;
    122                }
    123            }
    124            file_name[i] = fileName[i].getIndex();
    125        }
    126    }
    127
    128    @Override
    129    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    130        PackingUtils.log("Writing file bands...");
    131        byte[] encodedBand = encodeBandInt("file_name", file_name, Codec.UNSIGNED5);
    132        out.write(encodedBand);
    133        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_name[" + file_name.length + "]");
    134
    135        encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5,
    136            segmentHeader.have_file_size_hi());
    137        out.write(encodedBand);
    138        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]");
    139
    140        if (segmentHeader.have_file_modtime()) {
    141            encodedBand = encodeBandInt("file_modtime", file_modtime, Codec.DELTA5);
    142            out.write(encodedBand);
    143            PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_modtime[" + file_modtime.length + "]");
    144        }
    145        if (segmentHeader.have_file_options()) {
    146            encodedBand = encodeBandInt("file_options", file_options, Codec.UNSIGNED5);
    147            out.write(encodedBand);
    148            PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_options[" + file_options.length + "]");
    149        }
    150
    151        encodedBand = encodeBandInt("file_bits", flatten(file_bits), Codec.BYTE1);
    152        out.write(encodedBand);
    153        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_bits[" + file_bits.length + "]");
    154    }
    155
    156    private int[] flatten(final byte[][] bytes) {
    157        int total = 0;
    158        for (int i = 0; i < bytes.length; i++) {
    159            total += bytes[i].length;
    160        }
    161        final int[] band = new int[total];
    162        int index = 0;
    163        for (int i = 0; i < bytes.length; i++) {
    164            for (int j = 0; j < bytes[i].length; j++) {
    165                band[index++] = bytes[i][j] & 0xFF;
    166            }
    167        }
    168        return band;
    169    }
    170
    171}