View Javadoc
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  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.util.Arrays;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.TimeZone;
28  
29  import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;
30  import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit;
31  import org.apache.commons.compress.utils.ExactMath;
32  import org.objectweb.asm.ClassReader;
33  
34  /**
35   * 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
36   * described in the specification.
37   */
38  public class FileBands extends BandSet {
39  
40      private final CPUTF8[] fileName;
41      private int[] file_name;
42      private final int[] file_modtime;
43      private final long[] file_size;
44      private final int[] file_options;
45      private final byte[][] file_bits;
46      private final List<PackingFile> fileList;
47      private final PackingOptions options;
48      private final CpBands cpBands;
49  
50      public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, final SegmentUnit segmentUnit, final int effort) {
51          super(effort, segmentHeader);
52          fileList = segmentUnit.getFileList();
53          this.options = options;
54          this.cpBands = cpBands;
55          final int size = fileList.size();
56          fileName = new CPUTF8[size];
57          file_modtime = new int[size];
58          file_size = new long[size];
59          file_options = new int[size];
60          int totalSize = 0;
61          file_bits = new byte[size][];
62          final int archiveModtime = segmentHeader.getArchive_modtime();
63  
64          final Set<String> classNames = new HashSet<>();
65          for (final ClassReader reader : segmentUnit.getClassList()) {
66              classNames.add(reader.getClassName());
67          }
68          final CPUTF8 emptyString = cpBands.getCPUtf8("");
69          long modtime;
70          int latestModtime = Integer.MIN_VALUE;
71          final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime());
72          for (int i = 0; i < size; i++) {
73              final PackingFile packingFile = fileList.get(i);
74              final String name = packingFile.getName();
75              if (name.endsWith(".class") && !options.isPassFile(name)) {
76                  file_options[i] |= 1 << 1;
77                  if (classNames.contains(name.substring(0, name.length() - 6))) {
78                      fileName[i] = emptyString;
79                  } else {
80                      fileName[i] = cpBands.getCPUtf8(name);
81                  }
82              } else {
83                  fileName[i] = cpBands.getCPUtf8(name);
84              }
85              // set deflate_hint for file element
86              if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
87                  file_options[i] |= 0x1;
88              }
89              final byte[] bytes = packingFile.getContents();
90              file_size[i] = bytes.length;
91              totalSize = ExactMath.add(totalSize, file_size[i]);
92  
93              // update modification time
94              modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
95              file_modtime[i] = (int) (modtime - archiveModtime);
96              if (isLatest && latestModtime < file_modtime[i]) {
97                  latestModtime = file_modtime[i];
98              }
99  
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 }