Merge branch 'addzip' of git://github.com/CUBoulderBoy/avian into CUBoulderBoy-addzip

This commit is contained in:
Joshua Warner 2013-11-04 17:38:27 -07:00
commit da69b735f4
7 changed files with 720 additions and 10 deletions

View File

@ -12,4 +12,8 @@ package java.util.jar;
import java.util.zip.ZipEntry;
public abstract class JarEntry extends ZipEntry { }
public abstract class JarEntry extends ZipEntry {
public JarEntry(){
super(null);
}
}

View File

@ -11,16 +11,16 @@
package java.util.zip;
import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
public class DeflaterOutputStream extends OutputStream {
private final OutputStream out;
private final Deflater deflater;
private final byte[] buffer;
public class DeflaterOutputStream extends FilterOutputStream {
protected final Deflater deflater;
protected final byte[] buffer;
public DeflaterOutputStream(OutputStream out, Deflater deflater, int bufferSize)
{
this.out = out;
super(out);
this.deflater = deflater;
this.buffer = new byte[bufferSize];
}

View File

@ -10,12 +10,205 @@
package java.util.zip;
public abstract class ZipEntry {
public abstract String getName();
public abstract int getCompressedSize();
public abstract int getSize();
/**
* Class ZipEntry:
*
* Class to store and retrieve information for entries in a zip file
* Contains variables for all standard zip format field as well as
* setter and accessor methods
*
* "name" is used to store the string name of the entrys
* "reqVersion" stores a byte encoded minimum required version to open the zip
* "compressionMethod" stores the method used to compress the zip
* "modTimeDate" stores an MSDOS time field "millisTime" stores time in long format
* "crc" stores crc data for the zip entry
* "compSize" and "uncompSize" store compressed and uncompressed sizes
* "offset" stores data regarding the offset from the start of the zip file
*
* @author Christopher Jordan
* @author David Chau
* @author Aaron Davis
* @author Riley Moses
*/
import java.util.Calendar;
import java.util.Date;
public class ZipEntry {
String name;
short reqVersion = -1;
short compressionMethod = -1;
int modTimeDate = -1;
long millisTime = -1;
int crc = -1;
int compSize = 0;
int uncompSize = 0;
int offset = -1;
public ZipEntry(String name) {
this.name = name;
setTime(System.currentTimeMillis());
}
//Method to return name of the file
public String getName() {
return name;
}
//Method to check if file is a directory
public boolean isDirectory() {
return getName().endsWith("/");
}
/**
* Method setRequiredVersion:
*
* Method to set the minimum version required to open the zip file
* Valid values for the compression method are the numbers 1.0 to 10.0
*
* @author Christopher Jordan
*/
private boolean setRequiredVersion(float versionFloat){
//Check for valid version numbers
if (versionFloat < 1 || versionFloat > 100){
return false;
}
//Convert to short value for storage
versionFloat = versionFloat * 10;
short versionShort = (short)versionFloat;
//Set value of version
reqVersion = versionShort;
return true;
}
//Method to set the compression method for the file
//Valid methods are "stored" = 0 or "deflated" = 8
public void setMethod(short compMethod){
if (compMethod == 0 || compMethod == 8){
this.compressionMethod = compMethod;
}
}
public int getMethod(){
return this.compressionMethod;
}
//Methods to set and get the crc for the entry
public void setCrc(int crc){
if (crc < 0 || crc > 0xffffffff){
return;
}
else
this.crc = crc;
}
public int getCrc(){
return this.crc;
}
//Methods to set and get the time and date
public void setTime(long currentTime){
modTimeDate = computeDOSDateTime(currentTime);
millisTime = currentTime;
}
public long getTime(){
return millisTime;
}
public int getJavaTime(){
return modTimeDate;
}
/**
* Method computeDOSDateTime():
*
* Takes the time from a long and converts to a Calendar object
*
* Time is stored in the MSDOS format 5 bits for hour, 6 bits for min, 5 bits for seconds
* Hours are in military time and must be adjusted to time zone
* Seconds are divided by two before storing and must be multiplied by two to retrieve
*
* Date is stored in the MSDOS format 7 bit for year, 4 bit for month, 5 bits for day of month
* Year is the number of years since 1980 per, the month is stored starting at 0
* for January. Day of month is stored with nature numbering
*
* Bit masks and shifting are used to build the time and date bytes
*
* @author Christopher Jordan
* @author Aaron Davis
**/
private static int computeDOSDateTime(long currentTime){
final int DAY_OF_MONTH = 5;
final int HOUR_OF_DAY = 11;
final int MINUTE = 12;
final int MONTH = 2;
final int SECOND = 13;
final int YEAR = 1;
//Create calendar object, then set time to value passed in
Calendar modCalendar = Calendar.getInstance();
modCalendar.setTime(new Date(currentTime));
//Hour
int timeBits = modCalendar.get(HOUR_OF_DAY);
timeBits = timeBits - 6;
timeBits = timeBits << 6;
//Minutes
int minBits = 0x3f & (modCalendar.get(MINUTE));;
timeBits = timeBits ^ minBits;
timeBits = timeBits << 5;
//Seconds
int secBits = 0x1f & (modCalendar.get(SECOND));
secBits = secBits >> 1; //Divide by 2
timeBits = timeBits ^ secBits;
//Year
int dateBits = (modCalendar.get(YEAR) -1980);
dateBits = dateBits << 4;
//Month
int month = 0xf & ((modCalendar.get(MONTH)) + 1);
dateBits = dateBits ^ month;
dateBits = dateBits << 5;
//Day of month
int dayBits = 0x1f & modCalendar.get(DAY_OF_MONTH);
dateBits = dateBits ^ dayBits;
//Store Date
int storeDate = ((dateBits << 16) ^ (timeBits));
return storeDate;
}
//Methods to set and get the uncompressed size of the entry
public void setSize(int size){
if (size < 0){
return;
}
else
uncompSize = size;
}
public int getSize() {
return uncompSize;
}
//Methods to set and get the compressed size of the entry
public void setCompressedSize(int size){
if (size < 0){
return;
}
else
compSize = size;
}
public int getCompressedSize() {
return compSize;
}
}

View File

@ -285,6 +285,7 @@ public class ZipFile {
public final int pointer;
public MyZipEntry(Window window, int pointer) {
super(null);
this.window = window;
this.pointer = pointer;
}

View File

@ -0,0 +1,246 @@
/* Copyright (c) 2008-2013, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.util.zip;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Deflater;
/**
* An as simple as possible implementation of ZipOutputStream
* Compression method defaults to DEFLATE
* All hardcoded defaults match the defaults for openJDK,
* including PKZip version, bit flags set, compression level, etc
*
* @author David Chau
* @author Aaron Davis
* @author Christopher Jordan
* @author Riley Moses
*
*/
public class ZipOutputStream extends DeflaterOutputStream {
private static final int SIGNATURE = 0x04034b50;
private static final short VERSION = 0x0014;
private static final short BITFLAG = 0x0008;
private static final short METHOD = 0x0008;
private static final int CENTRAL_FILE_HEADER = 0x02014b50;
private static final int DATA_DESCRIPTER_HEADER = 0x08074b50;
private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50;
private static final int DEFAULT_LEVEL = 6;
private static final int INPUT_BUFFER_SIZE = 1024;
private List<ZipEntry> entries;
private CRC32 crc = new CRC32();
private ZipEntry currentEntry; // holder for current entry
private int bytesWritten; // a counter for total bytes written
private int sizeOfCentralDirectory; // a counter for central dir size
// these are used for the function write(int b) to provide a speed increase
private byte[] inputBuffer = new byte[INPUT_BUFFER_SIZE];
private int bufferIndex;
public ZipOutputStream(OutputStream outStream) {
super(outStream, new Deflater(DEFAULT_LEVEL, true));
bytesWritten = 0;
sizeOfCentralDirectory = 0;
entries = new ArrayList<ZipEntry>();
}
public void putNextEntry(ZipEntry e) throws IOException {
e.offset = bytesWritten;
currentEntry = e;
entries.add(e);
writeLocalHeader(e);
}
public void closeEntry() throws IOException {
// write remainder of buffer if partially full
if (bufferIndex != 0) {
write(inputBuffer, 0, bufferIndex);
bufferIndex = 0;
}
finish();
currentEntry.crc = (int) crc.getValue();
crc.reset();
writeDataDescriptor(currentEntry);
}
@Override
public void write(byte[] b, int offset, int length) throws IOException {
if (offset < 0 || length < 0 || b.length - (offset + length) < 0)
throw new IndexOutOfBoundsException();
currentEntry.uncompSize += length;
crc.update(b, offset, length);
currentEntry.crc = (int) crc.getValue();
deflater.setInput(b, offset, length);
while (deflater.getRemaining() > 0)
deflate();
}
@Override
public void write(int b) throws IOException {
inputBuffer[bufferIndex] = (byte)(b & 0xff);
bufferIndex += 1;
if (bufferIndex == 1024) {
write(inputBuffer, 0, bufferIndex);
bufferIndex = 0;
}
}
private void deflate() throws IOException {
int len = deflater.deflate(buffer, 0, buffer.length);
currentEntry.compSize += len;
bytesWritten += len;
if (len > 0)
out.write(buffer, 0, len);
}
private void writeLocalHeader(ZipEntry e) throws IOException {
byte[] tmpBuffer = new byte[30];
addFourBytes(SIGNATURE, 0, tmpBuffer); // local header signature
addTwoBytes(VERSION, 4, tmpBuffer); // version used
addTwoBytes(BITFLAG, 6, tmpBuffer); // flags
addTwoBytes(METHOD, 8, tmpBuffer); // compression method
addFourBytes(e.modTimeDate, 10, tmpBuffer); // last mod date and time
addFourBytes(0, 14, tmpBuffer); // CRC is 0 for local header
// with default flag settings (bit 3 set) the compressed and uncompressed size
// is written here as 0 and written correctly in the data descripter
addFourBytes(0, 18, tmpBuffer); // compressed size
addFourBytes(0, 22, tmpBuffer); // uncompressed size
addTwoBytes(e.name.length(), 26, tmpBuffer); // length of file name
// extra field length, in this implementation extra field in not used
addTwoBytes(0, 28, tmpBuffer);
out.write(tmpBuffer, 0, 30);
// write file name, return the number of bytes written
int len = writeUTF8(e.getName());
bytesWritten += 30 + len;
}
private void writeDataDescriptor(ZipEntry currentEntry) throws IOException {
byte[] tmpBuffer = new byte[16];
addFourBytes(DATA_DESCRIPTER_HEADER, 0, tmpBuffer); // data descripter header
addFourBytes(currentEntry.crc, 4, tmpBuffer); // crc value
addFourBytes(currentEntry.compSize, 8, tmpBuffer); // compressed size
addFourBytes(currentEntry.uncompSize, 12, tmpBuffer);// uncompressed size
out.write(tmpBuffer, 0, 16);
bytesWritten += 16;
}
private void writeCentralDirectoryHeader(ZipEntry e) throws IOException {
byte[] tmpBuffer = new byte[46];
addFourBytes(CENTRAL_FILE_HEADER, 0, tmpBuffer); // central directory header signature
addTwoBytes(VERSION, 4, tmpBuffer); // version made by
addTwoBytes(VERSION, 6, tmpBuffer); // version needed
addTwoBytes(BITFLAG, 8, tmpBuffer); // flags
addTwoBytes(METHOD, 10, tmpBuffer); // compression method
addFourBytes(e.modTimeDate, 12, tmpBuffer); // last mod date and time
addFourBytes(e.crc, 16, tmpBuffer); // crc
addFourBytes(e.compSize, 20, tmpBuffer); // compressed size
addFourBytes(e.uncompSize, 24, tmpBuffer); // uncompressed size
addTwoBytes(e.getName().length(), 28, tmpBuffer); // file name length
// the following 5 fields are all 0 for a simple default compression
addTwoBytes(0, 30, tmpBuffer); // extra field length (not used)
addTwoBytes(0, 32, tmpBuffer); // comment length (not used)
addTwoBytes(0, 34, tmpBuffer); // disk number start
addTwoBytes(0, 36, tmpBuffer); // internal file attribute
addFourBytes(0, 38, tmpBuffer); // external file attribute
addFourBytes((int) e.offset, 42, tmpBuffer); // relative offset of local header
out.write(tmpBuffer, 0, 46);
int len = writeUTF8(e.getName());
bytesWritten += 46 + len;
sizeOfCentralDirectory += 46 + len;
}
private void writeEndofCentralDirectory(int offset) throws IOException {
byte[] tmpBuffer = new byte[22];
short numEntries = (short) entries.size();
addFourBytes(END_OF_CENTRAL_DIRECTORY_SIG, 0, tmpBuffer); // end of central directory signature
addTwoBytes(0, 4, tmpBuffer); // disk number
addTwoBytes(0, 6, tmpBuffer); // disk number where central dir starts
addTwoBytes(numEntries, 8, tmpBuffer); // number of entries on this disk
addTwoBytes(numEntries, 10, tmpBuffer); // number of entries in central dir
addFourBytes(sizeOfCentralDirectory, 12, tmpBuffer); // length of central directory
addFourBytes(offset, 16, tmpBuffer); // offset of central directory
addTwoBytes(0, 20, tmpBuffer); // length of added comments (not used)
out.write(tmpBuffer, 0, 22);
bytesWritten += 22;
}
@Override
public void close() throws IOException {
int centralDirOffset = bytesWritten;
for (ZipEntry e : entries)
writeCentralDirectoryHeader(e);
writeEndofCentralDirectory(centralDirOffset);
deflater.dispose();
out.close();
}
@Override
public void flush() throws IOException {
out.write(inputBuffer, 0, inputBuffer.length);
inputBuffer = new byte[INPUT_BUFFER_SIZE];
}
private void finish() throws IOException {
deflater.finish();
while (!deflater.finished()) {
deflate();
}
deflater.reset();
}
private void addTwoBytes(int bytes, int offset, byte[] buffer) throws IOException {
buffer[offset] = (byte) (bytes & 0xff);
buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff);
}
private void addFourBytes(int bytes, int offset, byte[] buffer) throws IOException {
buffer[offset] = (byte) (bytes & 0xff);
buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff);
buffer[offset + 2] = (byte) ((bytes >> 16) & 0xff);
buffer[offset + 3] = (byte) ((bytes >> 24) & 0xff);
}
private int writeUTF8(String text) throws IOException {
byte[] bytes = text.getBytes("UTF-8");
out.write(bytes, 0, bytes.length);
return bytes.length;
}
}

56
test/ZipEntryTest.java Normal file
View File

@ -0,0 +1,56 @@
/* Copyright (c) 2013, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
import java.util.zip.ZipEntry;
/**
* class ZipEntryTest:
*
* class to test the ZipEntry class in java.util.zip
*
* @author Christopher Jordan
*/
public class ZipEntryTest {
static long timeInMillis = 1373309644787L;
static int dateInBytes = 1122526914;
public static void main(String args[]){
ZipEntry testEntry = new ZipEntry("testfile");
verifyDefaultValues(testEntry);
verifyTimeDate(testEntry, timeInMillis);
checkSetsAndGets(testEntry);
}
private static void verifyDefaultValues(ZipEntry testEntry){
if (testEntry.getTime() == -1)
throw new RuntimeException("The time isn't being set by the constructor");
verifyName(testEntry);
}
private static void verifyName(ZipEntry testEntry){
if (testEntry.getName() == "testfile")
return;
else
throw new RuntimeException("Name isn't being stored properly");
}
private static void verifyTimeDate(ZipEntry testEntry, long timeMillis){
testEntry.setTime(timeMillis);
if (testEntry.getJavaTime() != dateInBytes)
throw new RuntimeException("Date isn't being parsed properly");
if (testEntry.getTime() != timeInMillis)
throw new RuntimeException("Time isn't being stored accurately");
}
private static void checkSetsAndGets(ZipEntry testEntry){
return;
}
}

View File

@ -0,0 +1,210 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.*;
public class ZipOutputStreamTest
{
private static final String TEST1 = "test1.txt";
private static final String TEST2 = "test2.txt";
private static final String TEST3 = "test3.txt";
private static final String TEST4 = "test4.txt";
private static final String TEST1_CONTENTS = "\"this is a test\"";
private static final String TEST2_CONTENTS = "this is a\nmulti-line test";
private static final String TEST3_CONTENTS = "74 68 69 73 20 69 73 20 61 20 74 65 73 74";
private static final String TEST4_CONTENTS = "01110100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01110100 01100101 01110011 01110100";
private static final String ONE_PARAM_ZIP_PREFIX = "zos1param";
private static final String THREE_PARAM_ZIP_PREFIX = "zos3param";
private static final String ZIP_SUFFIX = ".zip";
private static final Map<String, String> FILES_CONTENTS;
static
{
Map<String, String> m = new HashMap<String, String>();
m.put(TEST1, TEST1_CONTENTS);
m.put(TEST2, TEST2_CONTENTS);
m.put(TEST3, TEST3_CONTENTS);
m.put(TEST4, TEST4_CONTENTS);
FILES_CONTENTS = Collections.unmodifiableMap(m);
}
private static final boolean USE_ONE_PARAM_WRITE = true;
private static final boolean USE_THREE_PARAM_WRITE = false;
private static byte[] buffer = new byte[1024];
public static void main(String[] args)
{
List<File> zipFiles = new ArrayList<File>(2);
// Test 1-param write function
File f1 = createZip(USE_ONE_PARAM_WRITE);
zipFiles.add(f1);
verifyContents(f1.getAbsolutePath());
// Test 3-param write function
File f2 = createZip(USE_THREE_PARAM_WRITE);
zipFiles.add(f2);
verifyContents(f2.getAbsolutePath());
// Remove the created zip files
cleanUp(zipFiles);
}
private static File createZip(boolean useOneParam)
{
FileOutputStream outputStream = null;
ZipOutputStream zipContents = null;
try
{
// Create a temporary zip file for this test
String prefix = useOneParam ? ONE_PARAM_ZIP_PREFIX : THREE_PARAM_ZIP_PREFIX;
File outputZip = File.createTempFile(prefix, ZIP_SUFFIX);
System.out.println("Created " + outputZip.getAbsolutePath());
// Prepare the streams
outputStream = new FileOutputStream(outputZip);
zipContents = new ZipOutputStream(outputStream);
// Zip the file contents (convert directly from string to bytes)
long startTime = System.currentTimeMillis();
for (Map.Entry<String, String> f : FILES_CONTENTS.entrySet())
{
String name = f.getKey();
String contents = f.getValue();
System.out.println("Zipping " + name + "...");
ZipEntry entry = new ZipEntry(name);
zipContents.putNextEntry(entry);
byte[] bytesToWrite = contents.getBytes();
if (useOneParam)
{
// Use the 1-parameter write method; takes a single byte
for (int i = 0; i < bytesToWrite.length; i++)
{
zipContents.write(bytesToWrite[i]);
}
}
else
{
// Use 3-parameter write method; takes a buffer, offset, and length
zipContents.write(bytesToWrite, 0 , bytesToWrite.length);
}
// Done with this file
zipContents.closeEntry();
System.out.println("Done");
}
// All files have been written
long endTime = System.currentTimeMillis();
System.out.println("Finished " + outputZip.getName() + " in " + ((endTime - startTime) / 1000.0) + " seconds");
return outputZip;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally
{
try
{
if (zipContents != null)
zipContents.close();
if (outputStream != null)
outputStream.close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
private static void verifyContents(String zipName)
{
System.out.println("Verify " + zipName);
ZipFile zf = null;
BufferedReader reader = null;
int numFilesInZip = 0;
try
{
String line;
String contents;
// Get the contents of each file in the zip
zf = new ZipFile(zipName);
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements();)
{
ZipEntry entry = e.nextElement();
reader = new BufferedReader(new InputStreamReader(zf.getInputStream(entry)));
contents = "";
numFilesInZip += 1;
while ((line = reader.readLine()) != null)
{
if (contents.length() > 0)
{
contents += "\n";
}
contents += line;
}
reader.close();
// Assert that this file's contents are correct
assert(contents.equals(FILES_CONTENTS.get(entry.getName())));
}
zf.close();
// Assert that the zip contained the correct number of files
assert(numFilesInZip == FILES_CONTENTS.size());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally
{
try
{
if (zf != null)
zf.close();
if (reader != null)
reader.close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
private static void cleanUp(List<File> zipFiles)
{
try
{
for (File f : zipFiles)
{
if (f.exists())
{
f.delete();
}
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}