diff --git a/classpath/java/util/zip/ZipEntry.java b/classpath/java/util/zip/ZipEntry.java index f52da8f4ca..b396127187 100644 --- a/classpath/java/util/zip/ZipEntry.java +++ b/classpath/java/util/zip/ZipEntry.java @@ -1,21 +1,160 @@ -/* 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; -public abstract class ZipEntry { - public abstract String getName(); - public abstract int getCompressedSize(); - public abstract int getSize(); +import java.util.Calendar; +import java.util.Date; +/** + * 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 + * + * @author ReadyTalk Summer 2013 Intern Team + **/ +public class ZipEntry { + String name; + //Minimum version needed to extract the file(s) from a compressed state + short reqVersion; + + //Method used to compress file + short compressionMethod; + + //Format of date and time are both 2 byte fields + short modTime; + short modDate; + + //CRC-32 + int crc; + + //Sizes of file + int compSize; + int uncompSize; + + int offset; + + public ZipEntry(String name) { + this.name = name; + setTimeDate(); + compSize = 0; + uncompSize = 0; + crc = 0; + offset = 0; + } + + //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 to return the compressed size of the file + public int getCompressedSize() { + return compSize; + } + + //Method to return the uncompressed size of the file + public int getSize() { + return uncompSize; + } + + /** + * Method setTime Date(): + * Creates a calendar object to retrieve date and time information + * + * 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 cjordan + **/ + public void setTimeDate(){ + 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; + + Calendar modCalendar = Calendar.getInstance(); + + //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; + timeBits = timeBits ^ secBits; + + //Store Time + modTime = (short)timeBits; + + //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 + modDate = (short)dateBits; + + } + + //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 + public 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 values for the compression method are the numbers 0 to 19 and 99 + public boolean setCompressionMethod(short compMethod){ + if (compMethod == 99){ + compressionMethod = compMethod; + return true; + } + else if (compMethod < 0 || compMethod > 19){ + return false; + } + else{ + compressionMethod = compMethod; + return true; + } + } + +} \ No newline at end of file diff --git a/classpath/java/util/zip/ZipOutputStream.java b/classpath/java/util/zip/ZipOutputStream.java new file mode 100644 index 0000000000..aa00705375 --- /dev/null +++ b/classpath/java/util/zip/ZipOutputStream.java @@ -0,0 +1,200 @@ +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; +import java.io.BufferedOutputStream; + + +/** + * 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 ReadyTalk Summer 2013 Intern Team + */ +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 final OutputStream out; + private DeflaterOutputStream deflaterStream; + private Deflater deflater; + private List entries; + private CRC32 crc = new CRC32(); + private ZipEntry currentEntry; + + private int bytesWritten; + private int sizeOfCentralDirectory; + private byte[] buffer; + + public ZipOutputStream(OutputStream outStream, int bufferSize) { + super(outStream); + out = outStream; + bytesWritten = 0; + sizeOfCentralDirectory = 0; + entries = new ArrayList(); + buffer = new byte[bufferSize]; + deflater = new Deflater(DEFAULT_LEVEL, true); + } + + public ZipOutputStream(OutputStream outStream) { + this(outStream, 4 * 1024); + } + + public void putNextEntry(ZipEntry e) throws IOException { + e.offset = bytesWritten; + currentEntry = e; + entries.add(e); + writeLocalHeader(e); + } + + public void closeEntry() throws IOException { + deflater.finish(); + while (!deflater.finished()) { + deflate(); + } + deflater.dispose(); + deflater.reset(); + + currentEntry.crc = (int) crc.getValue(); + crc.reset(); + writeDataDescriptor(currentEntry); + } + + private void writeLocalHeader(ZipEntry e) throws IOException { + writeFourBytes(SIGNATURE); // local header signature + writeTwoBytes(VERSION); // version used + writeTwoBytes(BITFLAG); // flags + writeTwoBytes(METHOD); // compression method + writeTwoBytes(e.modTime); // last modified time + writeTwoBytes(e.modDate); // last modified date + writeFourBytes(0); // CRC is 0 for local header + + // with default flag settings, the compressed and uncompressed size + // is written here as 0 and written correctly in the data descripter + writeFourBytes(0); // compressed size + writeFourBytes(0); // uncompressed size + writeTwoBytes(e.name.length()); // length of file name + + // extra field length, in this implementation extra field in not used + writeTwoBytes(0); + + // write file name, return the number of bytes written + int len = writeVariableByteLength(e.getName()); + + bytesWritten += 30 + len; + } + + private void writeDataDescriptor(ZipEntry currentEntry) throws IOException { + writeFourBytes(DATA_DESCRIPTER_HEADER); // data descripter header + writeFourBytes(currentEntry.crc); // crc value + writeFourBytes(currentEntry.compSize); // compressed size + writeFourBytes(currentEntry.uncompSize); // uncompressed size + bytesWritten += 16; + } + + public void write(byte[] b, int offset, int length) throws IOException { + currentEntry.uncompSize += length; + crc.update(b, offset, length); + currentEntry.crc = (int) crc.getValue(); + + deflater.setInput(b, offset, length); + while (deflater.getRemaining() > 0) + deflate(); + } + + public void write(int b) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte)(b & 0xff); + write(buf, 0, 1); + } + + 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 writeCentralDirectoryHeader(ZipEntry e) throws IOException { + writeFourBytes(CENTRAL_FILE_HEADER); // central directory header signature + writeTwoBytes(VERSION); // version made by + writeTwoBytes(VERSION); // version needed + writeTwoBytes(BITFLAG); // flags + writeTwoBytes(METHOD); // compression method + + writeTwoBytes(e.modTime); // last mod time + writeTwoBytes(e.modDate); // last mod date + writeFourBytes(e.crc); // crc + writeFourBytes(e.compSize); // compressed size + writeFourBytes(e.uncompSize); // uncompressed size + + writeTwoBytes(e.getName().length()); // file name length + + // the following 5 fields are all 0 for a simple default compression + writeTwoBytes(0); // extra field length (not used) + writeTwoBytes(0); // comment length (not used) + writeTwoBytes(0); // disk number start + writeTwoBytes(0); // internal file attribute + writeFourBytes(0); // external file attribute + + writeFourBytes((int) e.offset); // relative offset of local header + + int len = writeVariableByteLength(e.getName()); + + bytesWritten += 46 + len; + sizeOfCentralDirectory += 46 + len; + } + + private void writeEndofCentralDirectory(int offset) throws IOException { + short numEntries = (short) entries.size(); + writeFourBytes(END_OF_CENTRAL_DIRECTORY_SIG); // end of central directory signature + writeTwoBytes(0); // disk number + writeTwoBytes(0); // disk number where central dir starts + writeTwoBytes(numEntries); // number of entries on this disk + writeTwoBytes(numEntries); // number of entries in central dir + writeFourBytes(sizeOfCentralDirectory); // length of central directory + writeFourBytes(offset); // offset of central directory + writeTwoBytes(0); // length of added comments (not used) + bytesWritten += 22; + } + + public void close() throws IOException { + int centralDirOffset = bytesWritten; + for (ZipEntry e : entries) + writeCentralDirectoryHeader(e); + writeEndofCentralDirectory(centralDirOffset); + } + + private void writeTwoBytes(int bytes) throws IOException { + out.write(bytes & 0xff); + out.write((bytes >> 8) & 0xff); + } + + private void writeFourBytes(int bytes) throws IOException { + out.write(bytes & 0xff); + out.write((bytes >> 8) & 0xff); + out.write((bytes >> 16) & 0xff); + out.write((bytes >> 24) & 0xff); + } + + private int writeVariableByteLength(String text) throws IOException { + byte[] bytes = text.getBytes("UTF-8"); + out.write(bytes, 0, bytes.length); + return bytes.length; + } +} \ No newline at end of file