001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.pack200; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.util.Arrays; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Set; 025import java.util.TimeZone; 026 027import org.apache.commons.compress.harmony.pack200.Archive.PackingFile; 028import org.apache.commons.compress.harmony.pack200.Archive.SegmentUnit; 029import org.apache.commons.compress.utils.ExactMath; 030import org.objectweb.asm.ClassReader; 031 032/** 033 * 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 034 * described in the specification. 035 */ 036public class FileBands extends BandSet { 037 038 private final CPUTF8[] fileName; 039 private int[] file_name; 040 private final int[] file_modtime; 041 private final long[] file_size; 042 private final int[] file_options; 043 private final byte[][] file_bits; 044 private final List<PackingFile> fileList; 045 private final PackingOptions options; 046 private final CpBands cpBands; 047 048 public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, final SegmentUnit segmentUnit, final int effort) { 049 super(effort, segmentHeader); 050 fileList = segmentUnit.getFileList(); 051 this.options = options; 052 this.cpBands = cpBands; 053 final int size = fileList.size(); 054 fileName = new CPUTF8[size]; 055 file_modtime = new int[size]; 056 file_size = new long[size]; 057 file_options = new int[size]; 058 int totalSize = 0; 059 file_bits = new byte[size][]; 060 final int archiveModtime = segmentHeader.getArchive_modtime(); 061 062 final Set<String> classNames = new HashSet<>(); 063 for (final ClassReader reader : segmentUnit.getClassList()) { 064 classNames.add(reader.getClassName()); 065 } 066 final CPUTF8 emptyString = cpBands.getCPUtf8(""); 067 long modtime; 068 int latestModtime = Integer.MIN_VALUE; 069 final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime()); 070 for (int i = 0; i < size; i++) { 071 final PackingFile packingFile = fileList.get(i); 072 final String name = packingFile.getName(); 073 if (name.endsWith(".class") && !options.isPassFile(name)) { 074 file_options[i] |= 1 << 1; 075 if (classNames.contains(name.substring(0, name.length() - 6))) { 076 fileName[i] = emptyString; 077 } else { 078 fileName[i] = cpBands.getCPUtf8(name); 079 } 080 } else { 081 fileName[i] = cpBands.getCPUtf8(name); 082 } 083 // set deflate_hint for file element 084 if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) { 085 file_options[i] |= 0x1; 086 } 087 final byte[] bytes = packingFile.getContents(); 088 file_size[i] = bytes.length; 089 totalSize = ExactMath.add(totalSize, file_size[i]); 090 091 // update modification time 092 modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L; 093 file_modtime[i] = (int) (modtime - archiveModtime); 094 if (isLatest && latestModtime < file_modtime[i]) { 095 latestModtime = file_modtime[i]; 096 } 097 098 file_bits[i] = packingFile.getContents(); 099 } 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}