    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.harmony.pack200;
    020
    021import java.io.IOException;
    022import java.io.OutputStream;
    023import java.util.Arrays;
    024import java.util.HashSet;
    025import java.util.List;
    026import java.util.Set;
    027import java.util.TimeZone;
    028
    029import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
    030import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
    031import org.apache.commons.compress.utils.ExactMath;
    032import org.objectweb.asm.ClassReader;
    033
    034/**
    035 * Bands containing information about files in the pack200 archive and the file contents for non-class-files. Corresponds to the {@code file_bands} set of bands
    036 * described in the specification.
    037 */
    038public class FileBands extends BandSet {
    039
    040    private final CPUTF8[] fileName;
    041    private int[] file_name;
    042    private final int[] file_modtime;
    043    private final long[] file_size;
    044    private final int[] file_options;
    045    private final byte[][] file_bits;
    046    private final List<PackingFile> fileList;
    047    private final PackingOptions options;
    048    private final CpBands cpBands;
    049
    050    public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, final SegmentUnit segmentUnit, final int effort) {
    051        super(effort, segmentHeader);
    052        fileList = segmentUnit.getFileList();
    053        this.options = options;
    054        this.cpBands = cpBands;
    055        final int size = fileList.size();
    056        fileName = new CPUTF8[size];
    057        file_modtime = new int[size];
    058        file_size = new long[size];
    059        file_options = new int[size];
    060        int totalSize = 0;
    061        file_bits = new byte[size][];
    062        final int archiveModtime = segmentHeader.getArchive_modtime();
    063
    064        final Set<String> classNames = new HashSet<>();
    065        for (final ClassReader reader : segmentUnit.getClassList()) {
    066            classNames.add(reader.getClassName());
    067        }
    068        final CPUTF8 emptyString = cpBands.getCPUtf8("");
    069        long modtime;
    070        int latestModtime = Integer.MIN_VALUE;
    071        final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime());
    072        for (int i = 0; i < size; i++) {
    073            final PackingFile packingFile = fileList.get(i);
    074            final String name = packingFile.getName();
    075            if (name.endsWith(".class") && !options.isPassFile(name)) {
    076                file_options[i] |= 1 << 1;
    077                if (classNames.contains(name.substring(0, name.length() - 6))) {
    078                    fileName[i] = emptyString;
    079                } else {
    080                    fileName[i] = cpBands.getCPUtf8(name);
    081                }
    082            } else {
    083                fileName[i] = cpBands.getCPUtf8(name);
    084            }
    085            // set deflate_hint for file element
    086            if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
    087                file_options[i] |= 0x1;
    088            }
    089            final byte[] bytes = packingFile.getContents();
    090            file_size[i] = bytes.length;
    091            totalSize = ExactMath.add(totalSize, file_size[i]);
    092
    093            // update modification time
    094            modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
    095            file_modtime[i] = (int) (modtime - archiveModtime);
    096            if (isLatest && latestModtime < file_modtime[i]) {
    097                latestModtime = file_modtime[i];
    098            }
    099
    100            file_bits[i] = packingFile.getContents();
    101        }
    102
    103        if (isLatest) {
    104            Arrays.fill(file_modtime, latestModtime);
    105        }
    106    }
    107
    108    /**
    109     * All input classes for the segment have now been read in, so this method is called so that this class can calculate/complete anything it could not do
    110     * while classes were being read.
    111     */
    112    public void finaliseBands() {
    113        file_name = new int[fileName.length];
    114        for (int i = 0; i < file_name.length; i++) {
    115            if (fileName[i].equals(cpBands.getCPUtf8(""))) {
    116                final PackingFile packingFile = fileList.get(i);
    117                final String name = packingFile.getName();
    118                if (options.isPassFile(name)) {
    119                    fileName[i] = cpBands.getCPUtf8(name);
    120                    file_options[i] &= 1 << 1 ^ 0xFFFFFFFF;
    121                }
    122            }
    123            file_name[i] = fileName[i].getIndex();
    124        }
    125    }
    126
    127    private int[] flatten(final byte[][] bytes) {
    128        int total = 0;
    129        for (final byte[] element : bytes) {
    130            total += element.length;
    131        }
    132        final int[] band = new int[total];
    133        int index = 0;
    134        for (final byte[] element : bytes) {
    135            for (final byte element2 : element) {
    136                band[index++] = element2 & 0xFF;
    137            }
    138        }
    139        return band;
    140    }
    141
    142    @Override
    143    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    144        PackingUtils.log("Writing file bands...");
    145        byte[] encodedBand = encodeBandInt("file_name", file_name, Codec.UNSIGNED5);
    146        out.write(encodedBand);
    147        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_name[" + file_name.length + "]");
    148
    149        encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
    150        out.write(encodedBand);
    151        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]");
    152
    153        if (segmentHeader.have_file_modtime()) {
    154            encodedBand = encodeBandInt("file_modtime", file_modtime, Codec.DELTA5);
    155            out.write(encodedBand);
    156            PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_modtime[" + file_modtime.length + "]");
    157        }
    158        if (segmentHeader.have_file_options()) {
    159            encodedBand = encodeBandInt("file_options", file_options, Codec.UNSIGNED5);
    160            out.write(encodedBand);
    161            PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_options[" + file_options.length + "]");
    162        }
    163
    164        encodedBand = encodeBandInt("file_bits", flatten(file_bits), Codec.BYTE1);
    165        out.write(encodedBand);
    166        PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_bits[" + file_bits.length + "]");
    167    }
    168
    169}