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.HashSet; 022import java.util.Iterator; 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.objectweb.asm.ClassReader; 030 031/** 032 * Bands containing information about files in the pack200 archive and the file contents for non-class-files. 033 * Corresponds to the <code>file_bands</code> set of bands described in the specification. 034 */ 035public class FileBands extends BandSet { 036 037 private final CPUTF8[] fileName; 038 private int[] file_name; 039 private final int[] file_modtime; 040 private final long[] file_size; 041 private final int[] file_options; 042 private final byte[][] file_bits; 043 private final List fileList; 044 private final PackingOptions options; 045 private final CpBands cpBands; 046 047 public FileBands(final CpBands cpBands, final SegmentHeader segmentHeader, final PackingOptions options, 048 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 classNames = new HashSet(); 063 for (final Iterator iterator = segmentUnit.getClassList().iterator(); iterator.hasNext();) { 064 final ClassReader reader = (ClassReader) iterator.next(); 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 = (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 += 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 for (int index = 0; index < size; index++) { 104 file_modtime[index] = latestModtime; 105 } 106 } 107 } 108 109 /** 110 * All input classes for the segment have now been read in, so this method is called so that this class can 111 * calculate/complete anything it could not do while classes were being read. 112 */ 113 public void finaliseBands() { 114 file_name = new int[fileName.length]; 115 for (int i = 0; i < file_name.length; i++) { 116 if (fileName[i].equals(cpBands.getCPUtf8(""))) { 117 final PackingFile packingFile = (PackingFile) fileList.get(i); 118 final String name = packingFile.getName(); 119 if (options.isPassFile(name)) { 120 fileName[i] = cpBands.getCPUtf8(name); 121 file_options[i] &= (1 << 1) ^ 0xFFFFFFFF; 122 } 123 } 124 file_name[i] = fileName[i].getIndex(); 125 } 126 } 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 135 encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5, Codec.UNSIGNED5, 136 segmentHeader.have_file_size_hi()); 137 out.write(encodedBand); 138 PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_size[" + file_size.length + "]"); 139 140 if (segmentHeader.have_file_modtime()) { 141 encodedBand = encodeBandInt("file_modtime", file_modtime, Codec.DELTA5); 142 out.write(encodedBand); 143 PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_modtime[" + file_modtime.length + "]"); 144 } 145 if (segmentHeader.have_file_options()) { 146 encodedBand = encodeBandInt("file_options", file_options, Codec.UNSIGNED5); 147 out.write(encodedBand); 148 PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_options[" + file_options.length + "]"); 149 } 150 151 encodedBand = encodeBandInt("file_bits", flatten(file_bits), Codec.BYTE1); 152 out.write(encodedBand); 153 PackingUtils.log("Wrote " + encodedBand.length + " bytes from file_bits[" + file_bits.length + "]"); 154 } 155 156 private int[] flatten(final byte[][] bytes) { 157 int total = 0; 158 for (int i = 0; i < bytes.length; i++) { 159 total += bytes[i].length; 160 } 161 final int[] band = new int[total]; 162 int index = 0; 163 for (int i = 0; i < bytes.length; i++) { 164 for (int j = 0; j < bytes[i].length; j++) { 165 band[index++] = bytes[i][j] & 0xFF; 166 } 167 } 168 return band; 169 } 170 171}