/*
* 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 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.Date;
import java.util.HashMap;
import java.util.Iterator;
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>
*
*/
public class ZipOutputStream extends FilterOutputStream {
private static final int BYTE_MASK = 0xFF;
private static final int SHORT = 2;
private static final int WORD = 4;
private static final int BUFFER_SIZE = 512;
/*
* 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.
*/
public static final int UFT8_NAMES_FLAG = 1 << 11;
/**
* General purpose flag, which indicates that filenames are
* written in utf-8.
* @deprecated use {@link #UFT8_NAMES_FLAG} instead
*/
public static final int EFS_FLAG = UFT8_NAMES_FLAG;
/**
* Current entry.
*
* @since 1.1
*/
private ZipEntry 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 entries = new LinkedList();
/**
* 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;
/**
* Data for local header data
*
* @since 1.1
*/
private long dataStart = 0;
/**
* Offset for CRC entry in the local file header data for the
* current entry starts here.
*
* @since 1.15
*/
private long localDataStart = 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};
/**
* Holds the offsets of the LFH starts for each entry.
*
* @since 1.1
*/
private final Map offsets = new HashMap();
/**
* 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.
*
* <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 Deflater def = new Deflater(level, true);
/**
* This buffer servers 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 RandomAccessFile raf = null;
/**
* 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.