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. 034 * Corresponds to the {@code file_bands} set of bands 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, 049 final SegmentUnit segmentUnit, final int effort) { 050 super(effort, segmentHeader); 051 fileList = segmentUnit.getFileList(); 052 this.options = options; 053 this.cpBands = cpBands; 054 final int size = fileList.size(); 055 fileName = new CPUTF8[size]; 056 file_modtime = new int[size]; 057 file_size = new long[size]; 058 file_options = new int[size]; 059 int totalSize = 0; 060 file_bits = new byte[size][]; 061 final int archiveModtime = segmentHeader.getArchive_modtime(); 062 063 final Set<String> classNames = new HashSet<>(); 064 for (final ClassReader reader : segmentUnit.getClassList()) { 065 classNames.add(reader.getClassName()); 066 } 067 final CPUTF8 emptyString = cpBands.getCPUtf8(""); 068 long modtime; 069 int latestModtime = Integer.MIN_VALUE; 070 final boolean isLatest = !PackingOptions.KEEP.equals(options.getModificationTime()); 071 for (int i = 0; i < size; i++) { 072 final PackingFile packingFile = fileList.get(i); 073 final String name = packingFile.getName(); 074 if (name.endsWith(".class") && !options.isPassFile(name)) { 075 file_options[i] |= (1 << 1); 076 if (classNames.contains(name.substring(0, name.length() - 6))) { 077 fileName[i] = emptyString; 078 } else { 079 fileName[i] = cpBands.getCPUtf8(name); 080 } 081 } else { 082 fileName[i] = cpBands.getCPUtf8(name); 083 } 084 // set deflate_hint for file element 085 if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) { 086 file_options[i] |= 0x1; 087 } 088 final byte[] bytes = packingFile.getContents(); 089 file_size[i] = bytes.length; 090 totalSize = ExactMath.add(totalSize, file_size[i]); 091 092 // update modification time 093 modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L; 094 file_modtime[i] = (int) (modtime - archiveModtime); 095 if (isLatest && latestModtime < file_modtime[i]) { 096 latestModtime = file_modtime[i]; 097 } 098 099 file_bits[i] = packingFile.getContents(); 100 } 101 102 if (isLatest) { 103 Arrays.fill(file_modtime, latestModtime); 104 } 105 } 106 107 /** 108 * All input classes for the segment have now been read in, so this method is called so that this class can 109 * calculate/complete anything it could not do while classes were being read. 110 */ 111 public void finaliseBands() { 112 file_name = new int[fileName.length]; 113 for (int i = 0; i < file_name.length; i++) { 114 if (fileName[i].equals(cpBands.getCPUtf8(""))) { 115 final PackingFile packingFile = fileList.get(i); 116 final String name = packingFile.getName(); 117 if (options.isPassFile(name)) { 118 fileName[i] = cpBands.getCPUtf8(name); 119 file_options[i] &= (1 << 1) ^ 0xFFFFFFFF; 120 } 121 } 122 file_name[i] = fileName[i].getIndex(); 123 } 124 } 125 126 private int[] flatten(final byte[][] bytes) { 127 int total = 0; 128 for (final byte[] element : bytes) { 129 total += element.length; 130 } 131 final int[] band = new int[total]; 132 int index = 0; 133 for (final byte[] element : bytes) { 134 for (final byte element2 : element) { 135 band[index++] = element2 & 0xFF; 136 } 137 } 138 return band; 139 } 140 141 @Override 142 public void pack(final OutputStream out) throws IOException, Pack200Exception { 143 PackingUtils.log("Writing file bands..."); 144 byte[] encodedBand = encodeBandInt("file_name", file_name, Codec.UNSIGNED5); 145 out.write(encodedBand); 146 PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_name[" + file_name.length + "]"); 147 148 encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, 149 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}