View Javadoc
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  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.util.Arrays;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
25  import java.util.TimeZone;
26  
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  /**
33   * 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
34   * described in the specification.
35   */
36  public class FileBands extends BandSet {
37  
38      private final CPUTF8[] fileName;
39      private int[] file_name;
40      private final int[] file_modtime;
41      private final long[] file_size;
42      private final int[] file_options;
43      private final byte[][] file_bits;
44      private final List<PackingFile> fileList;
45      private final PackingOptions options;
46      private final CpBands cpBands;
47  
48      public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, final SegmentUnit segmentUnit, final int effort) {
49          super(effort, segmentHeader);
50          fileList = segmentUnit.getFileList();
51          this.options = options;
52          this.cpBands = cpBands;
53          final int size = fileList.size();
54          fileName = new CPUTF8[size];
55          file_modtime = new int[size];
56          file_size = new long[size];
57          file_options = new int[size];
58          int totalSize = 0;
59          file_bits = new byte[size][];
60          final int archiveModtime = segmentHeader.getArchive_modtime();
61  
62          final Set<String> classNames = new HashSet<>();
63          for (final ClassReader reader : segmentUnit.getClassList()) {
64              classNames.add(reader.getClassName());
65          }
66          final CPUTF8 emptyString = cpBands.getCPUtf8("");
67          long modtime;
68          int latestModtime = Integer.MIN_VALUE;
69          final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime());
70          for (int i = 0; i < size; i++) {
71              final PackingFile packingFile = fileList.get(i);
72              final String name = packingFile.getName();
73              if (name.endsWith(".class") && !options.isPassFile(name)) {
74                  file_options[i] |= 1 << 1;
75                  if (classNames.contains(name.substring(0, name.length() - 6))) {
76                      fileName[i] = emptyString;
77                  } else {
78                      fileName[i] = cpBands.getCPUtf8(name);
79                  }
80              } else {
81                  fileName[i] = cpBands.getCPUtf8(name);
82              }
83              // set deflate_hint for file element
84              if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
85                  file_options[i] |= 0x1;
86              }
87              final byte[] bytes = packingFile.getContents();
88              file_size[i] = bytes.length;
89              totalSize = ExactMath.add(totalSize, file_size[i]);
90  
91              // update modification time
92              modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
93              file_modtime[i] = (int) (modtime - archiveModtime);
94              if (isLatest && latestModtime < file_modtime[i]) {
95                  latestModtime = file_modtime[i];
96              }
97  
98              file_bits[i] = packingFile.getContents();
99          }
100 
101         if (isLatest) {
102             Arrays.fill(file_modtime, latestModtime);
103         }
104     }
105 
106     /**
107      * 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
108      * while classes were being read.
109      */
110     public void finaliseBands() {
111         file_name = new int[fileName.length];
112         for (int i = 0; i < file_name.length; i++) {
113             if (fileName[i].equals(cpBands.getCPUtf8(""))) {
114                 final PackingFile packingFile = fileList.get(i);
115                 final String name = packingFile.getName();
116                 if (options.isPassFile(name)) {
117                     fileName[i] = cpBands.getCPUtf8(name);
118                     file_options[i] &= 1 << 1 ^ 0xFFFFFFFF;
119                 }
120             }
121             file_name[i] = fileName[i].getIndex();
122         }
123     }
124 
125     private int[] flatten(final byte[][] bytes) {
126         int total = 0;
127         for (final byte[] element : bytes) {
128             total += element.length;
129         }
130         final int[] band = new int[total];
131         int index = 0;
132         for (final byte[] element : bytes) {
133             for (final byte element2 : element) {
134                 band[index++] = element2 & 0xFF;
135             }
136         }
137         return band;
138     }
139 
140     @Override
141     public void pack(final OutputStream out) throws IOException, Pack200Exception {
142         PackingUtils.log("Writing file bands...");
143         byte[] encodedBand = encodeBandInt("file_name", file_name, Codec.UNSIGNED5);
144         out.write(encodedBand);
145         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_name[" + file_name.length + "]");
146 
147         encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
148         out.write(encodedBand);
149         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]");
150 
151         if (segmentHeader.have_file_modtime()) {
152             encodedBand = encodeBandInt("file_modtime", file_modtime, Codec.DELTA5);
153             out.write(encodedBand);
154             PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_modtime[" + file_modtime.length + "]");
155         }
156         if (segmentHeader.have_file_options()) {
157             encodedBand = encodeBandInt("file_options", file_options, Codec.UNSIGNED5);
158             out.write(encodedBand);
159             PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_options[" + file_options.length + "]");
160         }
161 
162         encodedBand = encodeBandInt("file_bits", flatten(file_bits), Codec.BYTE1);
163         out.write(encodedBand);
164         PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_bits[" + file_bits.length + "]");
165     }
166 
167 }