FileBands.java

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

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

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

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

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

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

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

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

  90.             file_bits[i] = packingFile.getContents();
  91.         }

  92.         if (isLatest) {
  93.             Arrays.fill(file_modtime, latestModtime);
  94.         }
  95.     }

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

  114.     private int[] flatten(final byte[][] bytes) {
  115.         int total = 0;
  116.         for (final byte[] element : bytes) {
  117.             total += element.length;
  118.         }
  119.         final int[] band = new int[total];
  120.         int index = 0;
  121.         for (final byte[] element : bytes) {
  122.             for (final byte element2 : element) {
  123.                 band[index++] = element2 & 0xFF;
  124.             }
  125.         }
  126.         return band;
  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.         encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
  135.         out.write(encodedBand);
  136.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]");

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

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

  151. }