FileBands.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *   https://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.harmony.pack200;

  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.util.Arrays;
  23. import java.util.HashSet;
  24. import java.util.List;
  25. import java.util.Set;
  26. import java.util.TimeZone;

  27. import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
  28. import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
  29. import org.apache.commons.compress.utils.ExactMath;
  30. import org.objectweb.asm.ClassReader;

  31. /**
  32.  * 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
  33.  * described in the specification.
  34.  */
  35. public class FileBands extends BandSet {

  36.     private final CPUTF8[] fileName;
  37.     private int[] file_name;
  38.     private final int[] file_modtime;
  39.     private final long[] file_size;
  40.     private final int[] file_options;
  41.     private final byte[][] file_bits;
  42.     private final List<PackingFile> fileList;
  43.     private final PackingOptions options;
  44.     private final CpBands cpBands;

  45.     public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, final SegmentUnit segmentUnit, final int effort) {
  46.         super(effort, segmentHeader);
  47.         fileList = segmentUnit.getFileList();
  48.         this.options = options;
  49.         this.cpBands = cpBands;
  50.         final int size = fileList.size();
  51.         fileName = new CPUTF8[size];
  52.         file_modtime = new int[size];
  53.         file_size = new long[size];
  54.         file_options = new int[size];
  55.         int totalSize = 0;
  56.         file_bits = new byte[size][];
  57.         final int archiveModtime = segmentHeader.getArchive_modtime();

  58.         final Set<String> classNames = new HashSet<>();
  59.         for (final ClassReader reader : segmentUnit.getClassList()) {
  60.             classNames.add(reader.getClassName());
  61.         }
  62.         final CPUTF8 emptyString = cpBands.getCPUtf8("");
  63.         long modtime;
  64.         int latestModtime = Integer.MIN_VALUE;
  65.         final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime());
  66.         for (int i = 0; i < size; i++) {
  67.             final PackingFile packingFile = fileList.get(i);
  68.             final String name = packingFile.getName();
  69.             if (name.endsWith(".class") && !options.isPassFile(name)) {
  70.                 file_options[i] |= 1 << 1;
  71.                 if (classNames.contains(name.substring(0, name.length() - 6))) {
  72.                     fileName[i] = emptyString;
  73.                 } else {
  74.                     fileName[i] = cpBands.getCPUtf8(name);
  75.                 }
  76.             } else {
  77.                 fileName[i] = cpBands.getCPUtf8(name);
  78.             }
  79.             // set deflate_hint for file element
  80.             if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
  81.                 file_options[i] |= 0x1;
  82.             }
  83.             final byte[] bytes = packingFile.getContents();
  84.             file_size[i] = bytes.length;
  85.             totalSize = ExactMath.add(totalSize, file_size[i]);

  86.             // update modification time
  87.             modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
  88.             file_modtime[i] = (int) (modtime - archiveModtime);
  89.             if (isLatest && latestModtime < file_modtime[i]) {
  90.                 latestModtime = file_modtime[i];
  91.             }

  92.             file_bits[i] = packingFile.getContents();
  93.         }

  94.         if (isLatest) {
  95.             Arrays.fill(file_modtime, latestModtime);
  96.         }
  97.     }

  98.     /**
  99.      * 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
  100.      * while classes were being read.
  101.      */
  102.     public void finaliseBands() {
  103.         file_name = new int[fileName.length];
  104.         for (int i = 0; i < file_name.length; i++) {
  105.             if (fileName[i].equals(cpBands.getCPUtf8(""))) {
  106.                 final PackingFile packingFile = fileList.get(i);
  107.                 final String name = packingFile.getName();
  108.                 if (options.isPassFile(name)) {
  109.                     fileName[i] = cpBands.getCPUtf8(name);
  110.                     file_options[i] &= 1 << 1 ^ 0xFFFFFFFF;
  111.                 }
  112.             }
  113.             file_name[i] = fileName[i].getIndex();
  114.         }
  115.     }

  116.     private int[] flatten(final byte[][] bytes) {
  117.         int total = 0;
  118.         for (final byte[] element : bytes) {
  119.             total += element.length;
  120.         }
  121.         final int[] band = new int[total];
  122.         int index = 0;
  123.         for (final byte[] element : bytes) {
  124.             for (final byte element2 : element) {
  125.                 band[index++] = element2 & 0xFF;
  126.             }
  127.         }
  128.         return band;
  129.     }

  130.     @Override
  131.     public void pack(final OutputStream out) throws IOException, Pack200Exception {
  132.         PackingUtils.log("Writing file bands...");
  133.         byte[] encodedBand = encodeBandInt("file_name", file_name, Codec.UNSIGNED5);
  134.         out.write(encodedBand);
  135.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_name[" + file_name.length + "]");

  136.         encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
  137.         out.write(encodedBand);
  138.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]");

  139.         if (segmentHeader.have_file_modtime()) {
  140.             encodedBand = encodeBandInt("file_modtime", file_modtime, Codec.DELTA5);
  141.             out.write(encodedBand);
  142.             PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_modtime[" + file_modtime.length + "]");
  143.         }
  144.         if (segmentHeader.have_file_options()) {
  145.             encodedBand = encodeBandInt("file_options", file_options, Codec.UNSIGNED5);
  146.             out.write(encodedBand);
  147.             PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_options[" + file_options.length + "]");
  148.         }

  149.         encodedBand = encodeBandInt("file_bits", flatten(file_bits), Codec.BYTE1);
  150.         out.write(encodedBand);
  151.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_bits[" + file_bits.length + "]");
  152.     }

  153. }