diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java')
-rw-r--r-- | framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java | 1674 |
1 files changed, 0 insertions, 1674 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java deleted file mode 100644 index 261c717e..00000000 --- a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/zip/ZipOutputStream.java +++ /dev/null @@ -1,1674 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.apache.tools.zip; - -import static org.apache.tools.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION; -import static org.apache.tools.zip.ZipConstants.DWORD; -import static org.apache.tools.zip.ZipConstants.INITIAL_VERSION; -import static org.apache.tools.zip.ZipConstants.SHORT; -import static org.apache.tools.zip.ZipConstants.WORD; -import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC; -import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC_SHORT; -import static org.apache.tools.zip.ZipConstants.ZIP64_MIN_VERSION; -import static org.apache.tools.zip.ZipLong.putLong; -import static org.apache.tools.zip.ZipShort.putShort; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.zip.CRC32; -import java.util.zip.Deflater; -import java.util.zip.ZipException; - -/** - * Reimplementation of {@link java.util.zip.ZipOutputStream - * java.util.zip.ZipOutputStream} that does handle the extended - * functionality of this package, especially internal/external file - * attributes and extra fields with different layouts for local file - * data and central directory entries. - * - * <p>This class will try to use {@link java.io.RandomAccessFile - * RandomAccessFile} when you know that the output is going to go to a - * file.</p> - * - * <p>If RandomAccessFile cannot be used, this implementation will use - * a Data Descriptor to store size and CRC information for {@link - * #DEFLATED DEFLATED} entries, this means, you don't need to - * calculate them yourself. Unfortunately this is not possible for - * the {@link #STORED STORED} method, here setting the CRC and - * uncompressed size information is required before {@link - * #putNextEntry putNextEntry} can be called.</p> - * - * <p>As of Apache Ant 1.9.0 it transparently supports Zip64 - * extensions and thus individual entries and archives larger than 4 - * GB or with more than 65536 entries in most cases but explicit - * control is provided via {@link #setUseZip64}. If the stream can not - * user RandomAccessFile and you try to write a ZipEntry of - * unknown size then Zip64 extensions will be disabled by default.</p> - */ -public class ZipOutputStream extends FilterOutputStream { - - private static final int BUFFER_SIZE = 512; - private static final int LFH_SIG_OFFSET = 0; - private static final int LFH_VERSION_NEEDED_OFFSET = 4; - private static final int LFH_GPB_OFFSET = 6; - private static final int LFH_METHOD_OFFSET = 8; - private static final int LFH_TIME_OFFSET = 10; - private static final int LFH_CRC_OFFSET = 14; - private static final int LFH_COMPRESSED_SIZE_OFFSET = 18; - private static final int LFH_ORIGINAL_SIZE_OFFSET = 22; - private static final int LFH_FILENAME_LENGTH_OFFSET = 26; - private static final int LFH_EXTRA_LENGTH_OFFSET = 28; - private static final int LFH_FILENAME_OFFSET = 30; - private static final int CFH_SIG_OFFSET = 0; - private static final int CFH_VERSION_MADE_BY_OFFSET = 4; - private static final int CFH_VERSION_NEEDED_OFFSET = 6; - private static final int CFH_GPB_OFFSET = 8; - private static final int CFH_METHOD_OFFSET = 10; - private static final int CFH_TIME_OFFSET = 12; - private static final int CFH_CRC_OFFSET = 16; - private static final int CFH_COMPRESSED_SIZE_OFFSET = 20; - private static final int CFH_ORIGINAL_SIZE_OFFSET = 24; - private static final int CFH_FILENAME_LENGTH_OFFSET = 28; - private static final int CFH_EXTRA_LENGTH_OFFSET = 30; - private static final int CFH_COMMENT_LENGTH_OFFSET = 32; - private static final int CFH_DISK_NUMBER_OFFSET = 34; - private static final int CFH_INTERNAL_ATTRIBUTES_OFFSET = 36; - private static final int CFH_EXTERNAL_ATTRIBUTES_OFFSET = 38; - private static final int CFH_LFH_OFFSET = 42; - private static final int CFH_FILENAME_OFFSET = 46; - - /** - * indicates if this archive is finished. - */ - private boolean finished = false; - - /* - * Apparently Deflater.setInput gets slowed down a lot on Sun JVMs - * when it gets handed a really big buffer. See - * https://issues.apache.org/bugzilla/show_bug.cgi?id=45396 - * - * Using a buffer size of 8 kB proved to be a good compromise - */ - private static final int DEFLATER_BLOCK_SIZE = 8192; - - /** - * Compression method for deflated entries. - * - * @since 1.1 - */ - public static final int DEFLATED = java.util.zip.ZipEntry.DEFLATED; - - /** - * Default compression level for deflated entries. - * - * @since Ant 1.7 - */ - public static final int DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION; - - /** - * Compression method for stored entries. - * - * @since 1.1 - */ - public static final int STORED = java.util.zip.ZipEntry.STORED; - - /** - * default encoding for file names and comment. - */ - static final String DEFAULT_ENCODING = null; - - /** - * General purpose flag, which indicates that filenames are - * written in utf-8. - * @deprecated use {@link GeneralPurposeBit#UFT8_NAMES_FLAG} instead - */ - @Deprecated - public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG; - - private static final byte[] EMPTY = new byte[0]; - - /** - * Current entry. - * - * @since 1.1 - */ - private CurrentEntry entry; - - /** - * The file comment. - * - * @since 1.1 - */ - private String comment = ""; - - /** - * Compression level for next entry. - * - * @since 1.1 - */ - private int level = DEFAULT_COMPRESSION; - - /** - * Has the compression level changed when compared to the last - * entry? - * - * @since 1.5 - */ - private boolean hasCompressionLevelChanged = false; - - /** - * Default compression method for next entry. - * - * @since 1.1 - */ - private int method = java.util.zip.ZipEntry.DEFLATED; - - /** - * List of ZipEntries written so far. - * - * @since 1.1 - */ - private final List<ZipEntry> entries = new LinkedList<ZipEntry>(); - - /** - * CRC instance to avoid parsing DEFLATED data twice. - * - * @since 1.1 - */ - private final CRC32 crc = new CRC32(); - - /** - * Count the bytes written to out. - * - * @since 1.1 - */ - private long written = 0; - - /** - * Start of central directory. - * - * @since 1.1 - */ - private long cdOffset = 0; - - /** - * Length of central directory. - * - * @since 1.1 - */ - private long cdLength = 0; - - /** - * Helper, a 0 as ZipShort. - * - * @since 1.1 - */ - private static final byte[] ZERO = {0, 0}; - - /** - * Helper, a 0 as ZipLong. - * - * @since 1.1 - */ - private static final byte[] LZERO = {0, 0, 0, 0}; - - private static final byte[] ONE = ZipLong.getBytes(1L); - - /** - * Holds the offsets of the LFH starts for each entry. - * - * @since 1.1 - */ - private final Map<ZipEntry, Long> offsets = new HashMap<ZipEntry, Long>(); - - /** - * The encoding to use for filenames and the file comment. - * - * <p>For a list of possible values see <a - * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>. - * Defaults to the platform's default character encoding.</p> - * - * @since 1.3 - */ - private String encoding = null; - - /** - * The zip encoding to use for filenames and the file comment. - * - * This field is of internal use and will be set in {@link - * #setEncoding(String)}. - */ - private ZipEncoding zipEncoding = - ZipEncodingHelper.getZipEncoding(DEFAULT_ENCODING); - - // CheckStyle:VisibilityModifier OFF - bc - - /** - * This Deflater object is used for output. - * - */ - protected final Deflater def = new Deflater(level, true); - - /** - * This buffer serves as a Deflater. - * - * <p>This attribute is only protected to provide a level of API - * backwards compatibility. This class used to extend {@link - * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to - * Revision 1.13.</p> - * - * @since 1.14 - */ - protected byte[] buf = new byte[BUFFER_SIZE]; - - // CheckStyle:VisibilityModifier ON - - /** - * Optional random access output. - * - * @since 1.14 - */ - private final RandomAccessFile raf; - - /** - * whether to use the general purpose bit flag when writing UTF-8 - * filenames or not. - */ - private boolean useUTF8Flag = true; - - /** - * Whether to encode non-encodable file names as UTF-8. - */ - private boolean fallbackToUTF8 = false; - - /** - * whether to create UnicodePathExtraField-s for each entry. - */ - private UnicodeExtraFieldPolicy createUnicodeExtraFields = UnicodeExtraFieldPolicy.NEVER; - - /** - * Whether anything inside this archive has used a ZIP64 feature. - */ - private boolean hasUsedZip64 = false; - - private Zip64Mode zip64Mode = Zip64Mode.AsNeeded; - - private final Calendar calendarInstance = Calendar.getInstance(); - - /** - * Creates a new ZIP OutputStream filtering the underlying stream. - * @param out the outputstream to zip - * @since 1.1 - */ - public ZipOutputStream(OutputStream out) { - super(out); - this.raf = null; - } - - /** - * Creates a new ZIP OutputStream writing to a File. Will use - * random access if possible. - * @param file the file to zip to - * @since 1.14 - * @throws IOException on error - */ - public ZipOutputStream(File file) throws IOException { - super(null); - RandomAccessFile _raf = null; - try { - _raf = new RandomAccessFile(file, "rw"); - _raf.setLength(0); - } catch (IOException e) { - if (_raf != null) { - try { - _raf.close(); - } catch (IOException inner) { // NOPMD - // ignore - } - _raf = null; - } - out = new FileOutputStream(file); - } - raf = _raf; - } - - /** - * This method indicates whether this archive is writing to a - * seekable stream (i.e., to a random access file). - * - * <p>For seekable streams, you don't need to calculate the CRC or - * uncompressed size for {@link #STORED} entries before - * invoking {@link #putNextEntry}. - * @return true if seekable - * @since 1.17 - */ - public boolean isSeekable() { - return raf != null; - } - - /** - * The encoding to use for filenames and the file comment. - * - * <p>For a list of possible values see <a - * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>. - * Defaults to the platform's default character encoding.</p> - * @param encoding the encoding value - * @since 1.3 - */ - public void setEncoding(final String encoding) { - this.encoding = encoding; - this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding); - if (useUTF8Flag && !ZipEncodingHelper.isUTF8(encoding)) { - useUTF8Flag = false; - } - } - - /** - * The encoding to use for filenames and the file comment. - * - * @return null if using the platform's default character encoding. - * - * @since 1.3 - */ - public String getEncoding() { - return encoding; - } - - /** - * Whether to set the language encoding flag if the file name - * encoding is UTF-8. - * - * <p>Defaults to true.</p> - */ - public void setUseLanguageEncodingFlag(boolean b) { - useUTF8Flag = b && ZipEncodingHelper.isUTF8(encoding); - } - - /** - * Whether to create Unicode Extra Fields. - * - * <p>Defaults to NEVER.</p> - */ - public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) { - createUnicodeExtraFields = b; - } - - /** - * Whether to fall back to UTF and the language encoding flag if - * the file name cannot be encoded using the specified encoding. - * - * <p>Defaults to false.</p> - */ - public void setFallbackToUTF8(boolean b) { - fallbackToUTF8 = b; - } - - /** - * Whether Zip64 extensions will be used. - * - * <p>When setting the mode to {@link Zip64Mode#Never Never}, - * {@link #putNextEntry}, {@link #closeEntry}, {@link - * #finish} or {@link #close} may throw a {@link - * Zip64RequiredException} if the entry's size or the total size - * of the archive exceeds 4GB or there are more than 65536 entries - * inside the archive. Any archive created in this mode will be - * readable by implementations that don't support Zip64.</p> - * - * <p>When setting the mode to {@link Zip64Mode#Always Always}, - * Zip64 extensions will be used for all entries. Any archive - * created in this mode may be unreadable by implementations that - * don't support Zip64 even if all its contents would be.</p> - * - * <p>When setting the mode to {@link Zip64Mode#AsNeeded - * AsNeeded}, Zip64 extensions will transparently be used for - * those entries that require them. This mode can only be used if - * the uncompressed size of the {@link ZipEntry} is known - * when calling {@link #putNextEntry} or the archive is written - * to a seekable output (i.e. you have used the {@link - * #ZipOutputStream(java.io.File) File-arg constructor}) - - * this mode is not valid when the output stream is not seekable - * and the uncompressed size is unknown when {@link - * #putNextEntry} is called.</p> - * - * <p>If no entry inside the resulting archive requires Zip64 - * extensions then {@link Zip64Mode#Never Never} will create the - * smallest archive. {@link Zip64Mode#AsNeeded AsNeeded} will - * create a slightly bigger archive if the uncompressed size of - * any entry has initially been unknown and create an archive - * identical to {@link Zip64Mode#Never Never} otherwise. {@link - * Zip64Mode#Always Always} will create an archive that is at - * least 24 bytes per entry bigger than the one {@link - * Zip64Mode#Never Never} would create.</p> - * - * <p>Defaults to {@link Zip64Mode#AsNeeded AsNeeded} unless - * {@link #putNextEntry} is called with an entry of unknown - * size and data is written to a non-seekable stream - in this - * case the default is {@link Zip64Mode#Never Never}.</p> - * - * @since 1.3 - */ - public void setUseZip64(Zip64Mode mode) { - zip64Mode = mode; - } - - /** - * {@inheritDoc} - * @throws Zip64RequiredException if the archive's size exceeds 4 - * GByte or there are more than 65535 entries inside the archive - * and {@link #setUseZip64} is {@link Zip64Mode#Never}. - */ - public void finish() throws IOException { - if (finished) { - throw new IOException("This archive has already been finished"); - } - - if (entry != null) { - closeEntry(); - } - - cdOffset = written; - writeCentralDirectoryInChunks(); - cdLength = written - cdOffset; - writeZip64CentralDirectory(); - writeCentralDirectoryEnd(); - offsets.clear(); - entries.clear(); - def.end(); - finished = true; - } - - private void writeCentralDirectoryInChunks() throws IOException { - final int NUM_PER_WRITE = 1000; - final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(70 * NUM_PER_WRITE); - int count = 0; - for (ZipEntry ze : entries) { - byteArrayOutputStream.write(createCentralFileHeader(ze)); - if (++count > NUM_PER_WRITE){ - writeCounted(byteArrayOutputStream.toByteArray()); - byteArrayOutputStream.reset(); - count = 0; - } - } - writeCounted(byteArrayOutputStream.toByteArray()); - } - - /** - * Writes all necessary data for this entry. - * - * @since 1.1 - * @throws IOException on error - * @throws Zip64RequiredException if the entry's uncompressed or - * compressed size exceeds 4 GByte and {@link #setUseZip64} - * is {@link Zip64Mode#Never}. - */ - public void closeEntry() throws IOException { - preClose(); - - flushDeflater(); - - final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry); - long bytesWritten = written - entry.dataStart; - long realCrc = crc.getValue(); - crc.reset(); - - final boolean actuallyNeedsZip64 = - handleSizesAndCrc(bytesWritten, realCrc, effectiveMode); - - closeEntry(actuallyNeedsZip64); - } - - private void closeEntry(boolean actuallyNeedsZip64) throws IOException { - if (raf != null) { - rewriteSizesAndCrc(actuallyNeedsZip64); - } - - writeDataDescriptor(entry.entry); - entry = null; - } - - private void preClose() throws IOException { - if (finished) { - throw new IOException("Stream has already been finished"); - } - - if (entry == null) { - throw new IOException("No current entry to close"); - } - - if (!entry.hasWritten) { - write(EMPTY, 0, 0); - } - } - - /** - * Ensures all bytes sent to the deflater are written to the stream. - */ - private void flushDeflater() throws IOException { - if (entry.entry.getMethod() == DEFLATED) { - def.finish(); - while (!def.finished()) { - deflate(); - } - } - } - - /** - * Ensures the current entry's size and CRC information is set to - * the values just written, verifies it isn't too big in the - * Zip64Mode.Never case and returns whether the entry would - * require a Zip64 extra field. - */ - private boolean handleSizesAndCrc(long bytesWritten, long crc, - Zip64Mode effectiveMode) - throws ZipException { - if (entry.entry.getMethod() == DEFLATED) { - /* It turns out def.getBytesRead() returns wrong values if - * the size exceeds 4 GB on Java < Java7 - entry.entry.setSize(def.getBytesRead()); - */ - entry.entry.setSize(entry.bytesRead); - entry.entry.setCompressedSize(bytesWritten); - entry.entry.setCrc(crc); - - def.reset(); - } else if (raf == null) { - if (entry.entry.getCrc() != crc) { - throw new ZipException("bad CRC checksum for entry " - + entry.entry.getName() + ": " - + Long.toHexString(entry.entry.getCrc()) - + " instead of " - + Long.toHexString(crc)); - } - - if (entry.entry.getSize() != bytesWritten) { - throw new ZipException("bad size for entry " - + entry.entry.getName() + ": " - + entry.entry.getSize() - + " instead of " - + bytesWritten); - } - } else { /* method is STORED and we used RandomAccessFile */ - entry.entry.setSize(bytesWritten); - entry.entry.setCompressedSize(bytesWritten); - entry.entry.setCrc(crc); - } - - return checkIfNeedsZip64(effectiveMode); - } - - /** - * Ensures the current entry's size and CRC information is set to - * the values just written, verifies it isn't too big in the - * Zip64Mode.Never case and returns whether the entry would - * require a Zip64 extra field. - */ - private boolean checkIfNeedsZip64(Zip64Mode effectiveMode) - throws ZipException { - final boolean actuallyNeedsZip64 = isZip64Required(entry.entry, - effectiveMode); - if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never) { - throw new Zip64RequiredException(Zip64RequiredException - .getEntryTooBigMessage(entry.entry)); - } - return actuallyNeedsZip64; - } - - private boolean isZip64Required(ZipEntry entry1, Zip64Mode requestedMode) { - return requestedMode == Zip64Mode.Always || isTooLageForZip32(entry1); - } - - private boolean isTooLageForZip32(ZipEntry zipArchiveEntry){ - return zipArchiveEntry.getSize() >= ZIP64_MAGIC - || zipArchiveEntry.getCompressedSize() >= ZIP64_MAGIC; - } - - /** - * When using random access output, write the local file header - * and potentiall the ZIP64 extra containing the correct CRC and - * compressed/uncompressed sizes. - */ - private void rewriteSizesAndCrc(boolean actuallyNeedsZip64) - throws IOException { - long save = raf.getFilePointer(); - - raf.seek(entry.localDataStart); - writeOut(ZipLong.getBytes(entry.entry.getCrc())); - if (!hasZip64Extra(entry.entry) || !actuallyNeedsZip64) { - writeOut(ZipLong.getBytes(entry.entry.getCompressedSize())); - writeOut(ZipLong.getBytes(entry.entry.getSize())); - } else { - writeOut(ZipLong.ZIP64_MAGIC.getBytes()); - writeOut(ZipLong.ZIP64_MAGIC.getBytes()); - } - - if (hasZip64Extra(entry.entry)) { - // seek to ZIP64 extra, skip header and size information - raf.seek(entry.localDataStart + 3 * WORD + 2 * SHORT - + getName(entry.entry).limit() + 2 * SHORT); - // inside the ZIP64 extra uncompressed size comes - // first, unlike the LFH, CD or data descriptor - writeOut(ZipEightByteInteger.getBytes(entry.entry.getSize())); - writeOut(ZipEightByteInteger.getBytes(entry.entry.getCompressedSize())); - - if (!actuallyNeedsZip64) { - // do some cleanup: - // * rewrite version needed to extract - raf.seek(entry.localDataStart - 5 * SHORT); - writeOut(ZipShort.getBytes(INITIAL_VERSION)); - - // * remove ZIP64 extra so it doesn't get written - // to the central directory - entry.entry.removeExtraField(Zip64ExtendedInformationExtraField - .HEADER_ID); - entry.entry.setExtra(); - - // * reset hasUsedZip64 if it has been set because - // of this entry - if (entry.causedUseOfZip64) { - hasUsedZip64 = false; - } - } - } - raf.seek(save); - } - - /** - * {@inheritDoc} - * @throws Zip64RequiredException if the entry's uncompressed or - * compressed size is known to exceed 4 GByte and {@link #setUseZip64} - * is {@link Zip64Mode#Never}. - */ - public void putNextEntry(ZipEntry archiveEntry) throws IOException { - if (finished) { - throw new IOException("Stream has already been finished"); - } - - if (entry != null) { - closeEntry(); - } - - entry = new CurrentEntry(archiveEntry); - entries.add(entry.entry); - - setDefaults(entry.entry); - - final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry); - validateSizeInformation(effectiveMode); - - if (shouldAddZip64Extra(entry.entry, effectiveMode)) { - - Zip64ExtendedInformationExtraField z64 = getZip64Extra(entry.entry); - - // just a placeholder, real data will be in data - // descriptor or inserted later via RandomAccessFile - ZipEightByteInteger size = ZipEightByteInteger.ZERO; - ZipEightByteInteger compressedSize = ZipEightByteInteger.ZERO; - if (entry.entry.getMethod() == STORED - && entry.entry.getSize() != -1) { - // actually, we already know the sizes - size = new ZipEightByteInteger(entry.entry.getSize()); - compressedSize = size; - } - z64.setSize(size); - z64.setCompressedSize(compressedSize); - entry.entry.setExtra(); - } - - if (entry.entry.getMethod() == DEFLATED && hasCompressionLevelChanged) { - def.setLevel(level); - hasCompressionLevelChanged = false; - } - writeLocalFileHeader(entry.entry); - } - - /** - * Provides default values for compression method and last - * modification time. - */ - private void setDefaults(ZipEntry entry) { - if (entry.getMethod() == -1) { // not specified - entry.setMethod(method); - } - - if (entry.getTime() == -1) { // not specified - entry.setTime(System.currentTimeMillis()); - } - } - - /** - * Throws an exception if the size is unknown for a stored entry - * that is written to a non-seekable output or the entry is too - * big to be written without Zip64 extra but the mode has been set - * to Never. - */ - private void validateSizeInformation(Zip64Mode effectiveMode) - throws ZipException { - // Size/CRC not required if RandomAccessFile is used - if (entry.entry.getMethod() == STORED && raf == null) { - if (entry.entry.getSize() == -1) { - throw new ZipException("uncompressed size is required for" - + " STORED method when not writing to a" - + " file"); - } - if (entry.entry.getCrc() == -1) { - throw new ZipException("crc checksum is required for STORED" - + " method when not writing to a file"); - } - entry.entry.setCompressedSize(entry.entry.getSize()); - } - - if ((entry.entry.getSize() >= ZIP64_MAGIC - || entry.entry.getCompressedSize() >= ZIP64_MAGIC) - && effectiveMode == Zip64Mode.Never) { - throw new Zip64RequiredException(Zip64RequiredException - .getEntryTooBigMessage(entry.entry)); - } - } - - /** - * Whether to addd a Zip64 extended information extra field to the - * local file header. - * - * <p>Returns true if</p> - * - * <ul> - * <li>mode is Always</li> - * <li>or we already know it is going to be needed</li> - * <li>or the size is unknown and we can ensure it won't hurt - * other implementations if we add it (i.e. we can erase its - * usage</li> - * </ul> - */ - private boolean shouldAddZip64Extra(ZipEntry entry, Zip64Mode mode) { - return mode == Zip64Mode.Always - || entry.getSize() >= ZIP64_MAGIC - || entry.getCompressedSize() >= ZIP64_MAGIC - || (entry.getSize() == -1 - && raf != null && mode != Zip64Mode.Never); - } - - /** - * Set the file comment. - * @param comment the comment - */ - public void setComment(String comment) { - this.comment = comment; - } - - /** - * Sets the compression level for subsequent entries. - * - * <p>Default is Deflater.DEFAULT_COMPRESSION.</p> - * @param level the compression level. - * @throws IllegalArgumentException if an invalid compression - * level is specified. - * @since 1.1 - */ - public void setLevel(int level) { - if (level < Deflater.DEFAULT_COMPRESSION - || level > Deflater.BEST_COMPRESSION) { - throw new IllegalArgumentException("Invalid compression level: " - + level); - } - hasCompressionLevelChanged = (this.level != level); - this.level = level; - } - - /** - * Sets the default compression method for subsequent entries. - * - * <p>Default is DEFLATED.</p> - * @param method an <code>int</code> from java.util.zip.ZipEntry - * @since 1.1 - */ - public void setMethod(int method) { - this.method = method; - } - - /** - * Whether this stream is able to write the given entry. - * - * <p>May return false if it is set up to use encryption or a - * compression method that hasn't been implemented yet.</p> - */ - public boolean canWriteEntryData(ZipEntry ae) { - return ZipUtil.canHandleEntryData(ae); - } - - /** - * Writes bytes to ZIP entry. - * @param b the byte array to write - * @param offset the start position to write from - * @param length the number of bytes to write - * @throws IOException on error - */ - @Override - public void write(byte[] b, int offset, int length) throws IOException { - if (entry == null) { - throw new IllegalStateException("No current entry"); - } - ZipUtil.checkRequestedFeatures(entry.entry); - entry.hasWritten = true; - if (entry.entry.getMethod() == DEFLATED) { - writeDeflated(b, offset, length); - } else { - writeCounted(b, offset, length); - } - crc.update(b, offset, length); - } - - /** - * Write bytes to output or random access file. - * @param data the byte array to write - * @throws IOException on error - */ - private void writeCounted(byte[] data) throws IOException { - writeCounted(data, 0, data.length); - } - - private void writeCounted(byte[] data, int offset, int length) throws IOException { - writeOut(data, offset, length); - written += length; - } - - /** - * write implementation for DEFLATED entries. - */ - private void writeDeflated(byte[]b, int offset, int length) - throws IOException { - if (length > 0 && !def.finished()) { - entry.bytesRead += length; - if (length <= DEFLATER_BLOCK_SIZE) { - def.setInput(b, offset, length); - deflateUntilInputIsNeeded(); - } else { - final int fullblocks = length / DEFLATER_BLOCK_SIZE; - for (int i = 0; i < fullblocks; i++) { - def.setInput(b, offset + i * DEFLATER_BLOCK_SIZE, - DEFLATER_BLOCK_SIZE); - deflateUntilInputIsNeeded(); - } - final int done = fullblocks * DEFLATER_BLOCK_SIZE; - if (done < length) { - def.setInput(b, offset + done, length - done); - deflateUntilInputIsNeeded(); - } - } - } - } - - /** - * Closes this output stream and releases any system resources - * associated with the stream. - * - * @exception IOException if an I/O error occurs. - * @throws Zip64RequiredException if the archive's size exceeds 4 - * GByte or there are more than 65535 entries inside the archive - * and {@link #setUseZip64} is {@link Zip64Mode#Never}. - */ - @Override - public void close() throws IOException { - if (!finished) { - finish(); - } - destroy(); - } - - /** - * Flushes this output stream and forces any buffered output bytes - * to be written out to the stream. - * - * @exception IOException if an I/O error occurs. - */ - @Override - public void flush() throws IOException { - if (out != null) { - out.flush(); - } - } - - /* - * Various ZIP constants - */ - /** - * local file header signature - * - * @since 1.1 - */ - protected static final byte[] LFH_SIG = ZipLong.LFH_SIG.getBytes(); - /** - * data descriptor signature - * - * @since 1.1 - */ - protected static final byte[] DD_SIG = ZipLong.DD_SIG.getBytes(); - /** - * central file header signature - * - * @since 1.1 - */ - protected static final byte[] CFH_SIG = ZipLong.CFH_SIG.getBytes(); - /** - * end of central dir signature - * - * @since 1.1 - */ - protected static final byte[] EOCD_SIG = ZipLong.getBytes(0X06054B50L); - /** - * ZIP64 end of central dir signature - */ - static final byte[] ZIP64_EOCD_SIG = ZipLong.getBytes(0X06064B50L); - /** - * ZIP64 end of central dir locator signature - */ - static final byte[] ZIP64_EOCD_LOC_SIG = ZipLong.getBytes(0X07064B50L); - - /** - * Writes next block of compressed data to the output stream. - * @throws IOException on error - * - * @since 1.14 - */ - protected final void deflate() throws IOException { - int len = def.deflate(buf, 0, buf.length); - if (len > 0) { - writeCounted(buf, 0, len); - } - } - - /** - * Writes the local file header entry - * @param ze the entry to write - * @throws IOException on error - * - * @since 1.1 - */ - protected void writeLocalFileHeader(ZipEntry ze) throws IOException { - - boolean encodable = zipEncoding.canEncode(ze.getName()); - ByteBuffer name = getName(ze); - - if (createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER) { - addUnicodeExtraFields(ze, encodable, name); - } - - final byte[] localHeader = createLocalFileHeader(ze, name, encodable); - final long localHeaderStart = written; - offsets.put(ze, localHeaderStart); - entry.localDataStart = localHeaderStart + LFH_CRC_OFFSET; // At crc offset - writeCounted(localHeader); - entry.dataStart = written; - } - - private byte[] createLocalFileHeader(ZipEntry ze, ByteBuffer name, boolean encodable) { - byte[] extra = ze.getLocalFileDataExtra(); - final int nameLen = name.limit() - name.position(); - int len= LFH_FILENAME_OFFSET + nameLen + extra.length; - byte[] buf = new byte[len]; - - System.arraycopy(LFH_SIG, 0, buf, LFH_SIG_OFFSET, WORD); - - //store method in local variable to prevent multiple method calls - final int zipMethod = ze.getMethod(); - - putShort(versionNeededToExtract(zipMethod, hasZip64Extra(ze)), - buf, LFH_VERSION_NEEDED_OFFSET); - - GeneralPurposeBit generalPurposeBit = - getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8); - generalPurposeBit.encode(buf, LFH_GPB_OFFSET); - - // compression method - putShort(zipMethod, buf, LFH_METHOD_OFFSET); - - ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, LFH_TIME_OFFSET); - - // CRC - if (zipMethod == DEFLATED || raf != null) { - System.arraycopy(LZERO, 0, buf, LFH_CRC_OFFSET, WORD); - } else { - putLong(ze.getCrc(), buf, LFH_CRC_OFFSET); - } - - // compressed length - // uncompressed length - if (hasZip64Extra(entry.entry)){ - // point to ZIP64 extended information extra field for - // sizes, may get rewritten once sizes are known if - // stream is seekable - ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET); - ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET); - } else if (zipMethod == DEFLATED || raf != null) { - System.arraycopy(LZERO, 0, buf, LFH_COMPRESSED_SIZE_OFFSET, WORD); - System.arraycopy(LZERO, 0, buf, LFH_ORIGINAL_SIZE_OFFSET, WORD); - } else { // Stored - putLong(ze.getSize(), buf, LFH_COMPRESSED_SIZE_OFFSET); - putLong(ze.getSize(), buf, LFH_ORIGINAL_SIZE_OFFSET); - } - // file name length - putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET); - - // extra field length - putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET); - - // file name - System.arraycopy(name.array(), name.arrayOffset(), buf, - LFH_FILENAME_OFFSET, nameLen); - - System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length); - return buf; - } - - /** - * Adds UnicodeExtra fields for name and file comment if mode is - * ALWAYS or the data cannot be encoded using the configured - * encoding. - */ - private void addUnicodeExtraFields(ZipEntry ze, boolean encodable, - ByteBuffer name) - throws IOException { - if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS - || !encodable) { - ze.addExtraField(new UnicodePathExtraField(ze.getName(), - name.array(), - name.arrayOffset(), - name.limit() - - name.position())); - } - - String comm = ze.getComment(); - if (comm != null && !"".equals(comm)) { - - boolean commentEncodable = zipEncoding.canEncode(comm); - - if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS - || !commentEncodable) { - ByteBuffer commentB = getEntryEncoding(ze).encode(comm); - ze.addExtraField(new UnicodeCommentExtraField(comm, - commentB.array(), - commentB.arrayOffset(), - commentB.limit() - - commentB.position()) - ); - } - } - } - - /** - * Writes the data descriptor entry. - * @param ze the entry to write - * @throws IOException on error - * - * @since 1.1 - */ - protected void writeDataDescriptor(ZipEntry ze) throws IOException { - if (ze.getMethod() != DEFLATED || raf != null) { - return; - } - writeCounted(DD_SIG); - writeCounted(ZipLong.getBytes(ze.getCrc())); - if (!hasZip64Extra(ze)) { - writeCounted(ZipLong.getBytes(ze.getCompressedSize())); - writeCounted(ZipLong.getBytes(ze.getSize())); - } else { - writeCounted(ZipEightByteInteger.getBytes(ze.getCompressedSize())); - writeCounted(ZipEightByteInteger.getBytes(ze.getSize())); - } - } - - /** - * Writes the central file header entry. - * @param ze the entry to write - * @throws IOException on error - * @throws Zip64RequiredException if the archive's size exceeds 4 - * GByte and {@link Zip64Mode #setUseZip64} is {@link - * Zip64Mode#Never}. - */ - protected void writeCentralFileHeader(ZipEntry ze) throws IOException { - byte[] centralFileHeader = createCentralFileHeader(ze); - writeCounted(centralFileHeader); - } - - private byte[] createCentralFileHeader(ZipEntry ze) throws IOException { - final long lfhOffset = offsets.get(ze); - final boolean needsZip64Extra = hasZip64Extra(ze) - || ze.getCompressedSize() >= ZIP64_MAGIC - || ze.getSize() >= ZIP64_MAGIC - || lfhOffset >= ZIP64_MAGIC; - - if (needsZip64Extra && zip64Mode == Zip64Mode.Never) { - // must be the offset that is too big, otherwise an - // exception would have been throw in putArchiveEntry or - // closeArchiveEntry - throw new Zip64RequiredException(Zip64RequiredException - .ARCHIVE_TOO_BIG_MESSAGE); - } - - - handleZip64Extra(ze, lfhOffset, needsZip64Extra); - - return createCentralFileHeader(ze, getName(ze), lfhOffset, needsZip64Extra); - } - - /** - * Writes the central file header entry. - * @param ze the entry to write - * @param name The encoded name - * @param lfhOffset Local file header offset for this file - * @throws IOException on error - */ - private byte[] createCentralFileHeader(ZipEntry ze, ByteBuffer name, long lfhOffset, - boolean needsZip64Extra) throws IOException { - byte[] extra = ze.getCentralDirectoryExtra(); - - // file comment length - String comm = ze.getComment(); - if (comm == null) { - comm = ""; - } - - ByteBuffer commentB = getEntryEncoding(ze).encode(comm); - final int nameLen = name.limit() - name.position(); - final int commentLen = commentB.limit() - commentB.position(); - int len= CFH_FILENAME_OFFSET + nameLen + extra.length + commentLen; - byte[] buf = new byte[len]; - - System.arraycopy(CFH_SIG, 0, buf, CFH_SIG_OFFSET, WORD); - - // version made by - // CheckStyle:MagicNumber OFF - putShort((ze.getPlatform() << 8) | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION), - buf, CFH_VERSION_MADE_BY_OFFSET); - - final int zipMethod = ze.getMethod(); - final boolean encodable = zipEncoding.canEncode(ze.getName()); - putShort(versionNeededToExtract(zipMethod, needsZip64Extra), buf, CFH_VERSION_NEEDED_OFFSET); - getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8).encode(buf, CFH_GPB_OFFSET); - - // compression method - putShort(zipMethod, buf, CFH_METHOD_OFFSET); - - - // last mod. time and date - ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, CFH_TIME_OFFSET); - - // CRC - // compressed length - // uncompressed length - putLong(ze.getCrc(), buf, CFH_CRC_OFFSET); - if (ze.getCompressedSize() >= ZIP64_MAGIC - || ze.getSize() >= ZIP64_MAGIC) { - ZipLong.ZIP64_MAGIC.putLong(buf, CFH_COMPRESSED_SIZE_OFFSET); - ZipLong.ZIP64_MAGIC.putLong(buf, CFH_ORIGINAL_SIZE_OFFSET); - } else { - putLong(ze.getCompressedSize(), buf, CFH_COMPRESSED_SIZE_OFFSET); - putLong(ze.getSize(), buf, CFH_ORIGINAL_SIZE_OFFSET); - } - - putShort(nameLen, buf, CFH_FILENAME_LENGTH_OFFSET); - - // extra field length - putShort(extra.length, buf, CFH_EXTRA_LENGTH_OFFSET); - - putShort(commentLen, buf, CFH_COMMENT_LENGTH_OFFSET); - - // disk number start - System.arraycopy(ZERO, 0, buf, CFH_DISK_NUMBER_OFFSET, SHORT); - - // internal file attributes - putShort(ze.getInternalAttributes(), buf, CFH_INTERNAL_ATTRIBUTES_OFFSET); - - // external file attributes - putLong(ze.getExternalAttributes(), buf, CFH_EXTERNAL_ATTRIBUTES_OFFSET); - - // relative offset of LFH - putLong(Math.min(lfhOffset, ZIP64_MAGIC), buf, CFH_LFH_OFFSET); - - // file name - System.arraycopy(name.array(), name.arrayOffset(), buf, CFH_FILENAME_OFFSET, nameLen); - - int extraStart = CFH_FILENAME_OFFSET + nameLen; - System.arraycopy(extra, 0, buf, extraStart, extra.length); - - int commentStart = extraStart + commentLen; - - // file comment - System.arraycopy(commentB.array(), commentB.arrayOffset(), buf, commentStart, commentLen); - return buf; - } - - /** - * If the entry needs Zip64 extra information inside the central - * directory then configure its data. - */ - private void handleZip64Extra(ZipEntry ze, long lfhOffset, - boolean needsZip64Extra) { - if (needsZip64Extra) { - Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze); - if (ze.getCompressedSize() >= ZIP64_MAGIC - || ze.getSize() >= ZIP64_MAGIC) { - z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize())); - z64.setSize(new ZipEightByteInteger(ze.getSize())); - } else { - // reset value that may have been set for LFH - z64.setCompressedSize(null); - z64.setSize(null); - } - if (lfhOffset >= ZIP64_MAGIC) { - z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset)); - } - ze.setExtra(); - } - } - - /** - * Writes the "End of central dir record". - * @throws IOException on error - * @throws Zip64RequiredException if the archive's size exceeds 4 - * GByte or there are more than 65535 entries inside the archive - * and {@link Zip64Mode #setUseZip64} is {@link Zip64Mode#Never}. - */ - protected void writeCentralDirectoryEnd() throws IOException { - writeCounted(EOCD_SIG); - - // disk numbers - writeCounted(ZERO); - writeCounted(ZERO); - - // number of entries - int numberOfEntries = entries.size(); - if (numberOfEntries > ZIP64_MAGIC_SHORT - && zip64Mode == Zip64Mode.Never) { - throw new Zip64RequiredException(Zip64RequiredException - .TOO_MANY_ENTRIES_MESSAGE); - } - if (cdOffset > ZIP64_MAGIC && zip64Mode == Zip64Mode.Never) { - throw new Zip64RequiredException(Zip64RequiredException - .ARCHIVE_TOO_BIG_MESSAGE); - } - - byte[] num = ZipShort.getBytes(Math.min(numberOfEntries, - ZIP64_MAGIC_SHORT)); - writeCounted(num); - writeCounted(num); - - // length and location of CD - writeCounted(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC))); - writeCounted(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC))); - - // ZIP file comment - ByteBuffer data = this.zipEncoding.encode(comment); - int dataLen = data.limit() - data.position(); - writeCounted(ZipShort.getBytes(dataLen)); - writeCounted(data.array(), data.arrayOffset(), dataLen); - } - - /** - * Convert a Date object to a DOS date/time field. - * @param time the <code>Date</code> to convert - * @return the date as a <code>ZipLong</code> - * @since 1.1 - * @deprecated use ZipUtil#toDosTime - */ - @Deprecated - protected static ZipLong toDosTime(Date time) { - return ZipUtil.toDosTime(time); - } - - /** - * Convert a Date object to a DOS date/time field. - * - * <p>Stolen from InfoZip's <code>fileio.c</code></p> - * @param t number of milliseconds since the epoch - * @return the date as a byte array - * @since 1.26 - * @deprecated use ZipUtil#toDosTime - */ - @Deprecated - protected static byte[] toDosTime(long t) { - return ZipUtil.toDosTime(t); - } - - /** - * Retrieve the bytes for the given String in the encoding set for - * this Stream. - * @param name the string to get bytes from - * @return the bytes as a byte array - * @throws ZipException on error - * - * @since 1.3 - */ - protected byte[] getBytes(String name) throws ZipException { - try { - ByteBuffer b = - ZipEncodingHelper.getZipEncoding(encoding).encode(name); - byte[] result = new byte[b.limit()]; - System.arraycopy(b.array(), b.arrayOffset(), result, 0, - result.length); - return result; - } catch (IOException ex) { - throw new ZipException("Failed to encode name: " + ex.getMessage()); - } - } - - /** - * Writes the "ZIP64 End of central dir record" and - * "ZIP64 End of central dir locator". - * @throws IOException on error - */ - protected void writeZip64CentralDirectory() throws IOException { - if (zip64Mode == Zip64Mode.Never) { - return; - } - - if (!hasUsedZip64 - && (cdOffset >= ZIP64_MAGIC || cdLength >= ZIP64_MAGIC - || entries.size() >= ZIP64_MAGIC_SHORT)) { - // actually "will use" - hasUsedZip64 = true; - } - - if (!hasUsedZip64) { - return; - } - - long offset = written; - - writeOut(ZIP64_EOCD_SIG); - // size, we don't have any variable length as we don't support - // the extensible data sector, yet - writeOut(ZipEightByteInteger - .getBytes(SHORT /* version made by */ - + SHORT /* version needed to extract */ - + WORD /* disk number */ - + WORD /* disk with central directory */ - + DWORD /* number of entries in CD on this disk */ - + DWORD /* total number of entries */ - + DWORD /* size of CD */ - + DWORD /* offset of CD */ - )); - - // version made by and version needed to extract - writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION)); - writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION)); - - // disk numbers - four bytes this time - writeOut(LZERO); - writeOut(LZERO); - - // number of entries - byte[] num = ZipEightByteInteger.getBytes(entries.size()); - writeOut(num); - writeOut(num); - - // length and location of CD - writeOut(ZipEightByteInteger.getBytes(cdLength)); - writeOut(ZipEightByteInteger.getBytes(cdOffset)); - - // no "zip64 extensible data sector" for now - - // and now the "ZIP64 end of central directory locator" - writeOut(ZIP64_EOCD_LOC_SIG); - - // disk number holding the ZIP64 EOCD record - writeOut(LZERO); - // relative offset of ZIP64 EOCD record - writeOut(ZipEightByteInteger.getBytes(offset)); - // total number of disks - writeOut(ONE); - } - - /** - * Write bytes to output or random access file. - * @param data the byte array to write - * @throws IOException on error - * - * @since 1.14 - */ - protected final void writeOut(byte[] data) throws IOException { - writeOut(data, 0, data.length); - } - - /** - * Write bytes to output or random access file. - * @param data the byte array to write - * @param offset the start position to write from - * @param length the number of bytes to write - * @throws IOException on error - * - * @since 1.14 - */ - protected final void writeOut(byte[] data, int offset, int length) - throws IOException { - if (raf != null) { - raf.write(data, offset, length); - } else { - out.write(data, offset, length); - } - } - - /** - * Assumes a negative integer really is a positive integer that - * has wrapped around and re-creates the original value. - * @param i the value to treat as unsigned int. - * @return the unsigned int as a long. - * @since 1.34 - * @deprecated use ZipUtil#adjustToLong - */ - @Deprecated - protected static long adjustToLong(int i) { - return ZipUtil.adjustToLong(i); - } - - private void deflateUntilInputIsNeeded() throws IOException { - while (!def.needsInput()) { - deflate(); - } - } - - private GeneralPurposeBit getGeneralPurposeBits(final int zipMethod, final boolean utfFallback) { - GeneralPurposeBit b = new GeneralPurposeBit(); - b.useUTF8ForNames(useUTF8Flag || utfFallback); - if (isDeflatedToOutputStream(zipMethod)) { - b.useDataDescriptor(true); - } - return b; - } - - private int versionNeededToExtract(final int zipMethod, final boolean zip64) { - if (zip64) { - return ZIP64_MIN_VERSION; - } - // requires version 2 as we are going to store length info - // in the data descriptor - return (isDeflatedToOutputStream(zipMethod)) ? - DATA_DESCRIPTOR_MIN_VERSION : - INITIAL_VERSION; - } - - private boolean isDeflatedToOutputStream(int zipMethod) { - return zipMethod == DEFLATED && raf == null; - } - - /** - * Get the existing ZIP64 extended information extra field or - * create a new one and add it to the entry. - */ - private Zip64ExtendedInformationExtraField getZip64Extra(ZipEntry ze) { - if (entry != null) { - entry.causedUseOfZip64 = !hasUsedZip64; - } - hasUsedZip64 = true; - Zip64ExtendedInformationExtraField z64 = - (Zip64ExtendedInformationExtraField) - ze.getExtraField(Zip64ExtendedInformationExtraField - .HEADER_ID); - if (z64 == null) { - /* - System.err.println("Adding z64 for " + ze.getName() - + ", method: " + ze.getMethod() - + " (" + (ze.getMethod() == STORED) + ")" - + ", raf: " + (raf != null)); - */ - z64 = new Zip64ExtendedInformationExtraField(); - } - - // even if the field is there already, make sure it is the first one - ze.addAsFirstExtraField(z64); - - return z64; - } - - /** - * Is there a ZIP64 extended information extra field for the - * entry? - */ - private boolean hasZip64Extra(ZipEntry ze) { - return ze.getExtraField(Zip64ExtendedInformationExtraField - .HEADER_ID) - != null; - } - - /** - * If the mode is AsNeeded and the entry is a compressed entry of - * unknown size that gets written to a non-seekable stream the - * change the default to Never. - */ - private Zip64Mode getEffectiveZip64Mode(ZipEntry ze) { - if (zip64Mode != Zip64Mode.AsNeeded - || raf != null - || ze.getMethod() != DEFLATED - || ze.getSize() != -1) { - return zip64Mode; - } - return Zip64Mode.Never; - } - - private ZipEncoding getEntryEncoding(ZipEntry ze) { - boolean encodable = zipEncoding.canEncode(ze.getName()); - return !encodable && fallbackToUTF8 - ? ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding; - } - - private ByteBuffer getName(ZipEntry ze) throws IOException { - return getEntryEncoding(ze).encode(ze.getName()); - } - - /** - * Closes the underlying stream/file without finishing the - * archive, the result will likely be a corrupt archive. - * - * <p>This method only exists to support tests that generate - * corrupt archives so they can clean up any temporary files.</p> - */ - void destroy() throws IOException { - if (raf != null) { - raf.close(); - } - if (out != null) { - out.close(); - } - } - - /** - * enum that represents the possible policies for creating Unicode - * extra fields. - */ - public static final class UnicodeExtraFieldPolicy { - /** - * Always create Unicode extra fields. - */ - public static final UnicodeExtraFieldPolicy ALWAYS = - new UnicodeExtraFieldPolicy("always"); - /** - * Never create Unicode extra fields. - */ - public static final UnicodeExtraFieldPolicy NEVER = - new UnicodeExtraFieldPolicy("never"); - /** - * Create Unicode extra fields for filenames that cannot be - * encoded using the specified encoding. - */ - public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE = - new UnicodeExtraFieldPolicy("not encodeable"); - - private final String name; - private UnicodeExtraFieldPolicy(String n) { - name = n; - } - @Override - public String toString() { - return name; - } - } - - /** - * Structure collecting information for the entry that is - * currently being written. - */ - private static final class CurrentEntry { - private CurrentEntry(ZipEntry entry) { - this.entry = entry; - } - /** - * Current ZIP entry. - */ - private final ZipEntry entry; - /** - * Offset for CRC entry in the local file header data for the - * current entry starts here. - */ - private long localDataStart = 0; - /** - * Data for local header data - */ - private long dataStart = 0; - /** - * Number of bytes read for the current entry (can't rely on - * Deflater#getBytesRead) when using DEFLATED. - */ - private long bytesRead = 0; - /** - * Whether current entry was the first one using ZIP64 features. - */ - private boolean causedUseOfZip64 = false; - /** - * Whether write() has been called at all. - * - * <p>In order to create a valid archive {@link - * #closeEntry closeEntry} will write an empty - * array to get the CRC right if nothing has been written to - * the stream at all.</p> - */ - private boolean hasWritten; - } - -} |