Android Open Source - JiangHomeStyle_Android_Phone Zip Output Stream






From Project

Back to project page JiangHomeStyle_Android_Phone.

License

The source code is released under:

Apache License

If you think the Android project JiangHomeStyle_Android_Phone listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 *  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
 *//ww w  .  j  a  v a 2  s  . c om
 *      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.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.ZipException;

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;

/**
 * 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;

  /**
   * 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 };

  /**
   * 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;

  /**
   * 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;
    for (ZipEntry ze : entries)
    {
      writeCentralFileHeader(ze);
    }
    cdLength = written - cdOffset;
    writeZip64CentralDirectory();
    writeCentralDirectoryEnd();
    offsets.clear();
    entries.clear();
    def.end();
    finished = true;
  }

  /**
   * 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
  {
    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);
    }

    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);

    if (raf != null)
    {
      rewriteSizesAndCrc(actuallyNeedsZip64);
    }

    writeDataDescriptor(entry.entry);
    entry = null;
  }

  /**
   * 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);
    }

    final boolean actuallyNeedsZip64 = effectiveMode == Zip64Mode.Always || entry.entry.getSize() >= ZIP64_MAGIC
        || entry.entry.getCompressedSize() >= ZIP64_MAGIC;
    if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never)
    {
      throw new Zip64RequiredException(Zip64RequiredException.getEntryTooBigMessage(entry.entry));
    }
    return actuallyNeedsZip64;
  }

  /**
   * 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;
      if (entry.entry.getMethod() == STORED && entry.entry.getSize() != -1)
      {
        // actually, we already know the sizes
        size = new ZipEightByteInteger(entry.entry.getSize());
      }
      z64.setSize(size);
      z64.setCompressedSize(size);
      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
  {
    ZipUtil.checkRequestedFeatures(entry.entry);
    entry.hasWritten = true;
    if (entry.entry.getMethod() == DEFLATED)
    {
      writeDeflated(b, offset, length);
    }
    else
    {
      writeOut(b, offset, length);
      written += length;
    }
    crc.update(b, offset, 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)
    {
      writeOut(buf, 0, len);
      written += 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);
    }

    offsets.put(ze, Long.valueOf(written));

    writeOut(LFH_SIG);
    written += WORD;

    // store method in local variable to prevent multiple method calls
    final int zipMethod = ze.getMethod();

    writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8, hasZip64Extra(ze));
    written += WORD;

    // compression method
    writeOut(ZipShort.getBytes(zipMethod));
    written += SHORT;

    // last mod. time and date
    writeOut(ZipUtil.toDosTime(ze.getTime()));
    written += WORD;

    // CRC
    // compressed length
    // uncompressed length
    entry.localDataStart = written;
    if (zipMethod == DEFLATED || raf != null)
    {
      writeOut(LZERO);
      if (hasZip64Extra(entry.entry))
      {
        // point to ZIP64 extended information extra field for
        // sizes, may get rewritten once sizes are known if
        // stream is seekable
        writeOut(ZipLong.ZIP64_MAGIC.getBytes());
        writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      }
      else
      {
        writeOut(LZERO);
        writeOut(LZERO);
      }
    }
    else
    {
      writeOut(ZipLong.getBytes(ze.getCrc()));
      byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
      if (!hasZip64Extra(ze))
      {
        size = ZipLong.getBytes(ze.getSize());
      }
      writeOut(size);
      writeOut(size);
    }
    // CheckStyle:MagicNumber OFF
    written += 12;
    // CheckStyle:MagicNumber ON

    // file name length
    writeOut(ZipShort.getBytes(name.limit()));
    written += SHORT;

    // extra field length
    byte[] extra = ze.getLocalFileDataExtra();
    writeOut(ZipShort.getBytes(extra.length));
    written += SHORT;

    // file name
    writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
    written += name.limit();

    // extra field
    writeOut(extra);
    written += extra.length;

    entry.dataStart = written;
  }

  /**
   * 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;
    }
    writeOut(DD_SIG);
    writeOut(ZipLong.getBytes(ze.getCrc()));
    int sizeFieldSize = WORD;
    if (!hasZip64Extra(ze))
    {
      writeOut(ZipLong.getBytes(ze.getCompressedSize()));
      writeOut(ZipLong.getBytes(ze.getSize()));
    }
    else
    {
      sizeFieldSize = DWORD;
      writeOut(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
      writeOut(ZipEightByteInteger.getBytes(ze.getSize()));
    }
    written += 2 * WORD + 2 * sizeFieldSize;
  }

  /**
   * 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
  {
    writeOut(CFH_SIG);
    written += WORD;

    final long lfhOffset = offsets.get(ze).longValue();
    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 putNextEntry or
      // closeEntry
      throw new Zip64RequiredException(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE);
    }

    handleZip64Extra(ze, lfhOffset, needsZip64Extra);

    // version made by
    // CheckStyle:MagicNumber OFF
    writeOut(ZipShort.getBytes((ze.getPlatform() << 8) | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION)));
    written += SHORT;

    final int zipMethod = ze.getMethod();
    final boolean encodable = zipEncoding.canEncode(ze.getName());
    writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8, needsZip64Extra);
    written += WORD;

    // compression method
    writeOut(ZipShort.getBytes(zipMethod));
    written += SHORT;

    // last mod. time and date
    writeOut(ZipUtil.toDosTime(ze.getTime()));
    written += WORD;

    // CRC
    // compressed length
    // uncompressed length
    writeOut(ZipLong.getBytes(ze.getCrc()));
    if (ze.getCompressedSize() >= ZIP64_MAGIC || ze.getSize() >= ZIP64_MAGIC)
    {
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
    }
    else
    {
      writeOut(ZipLong.getBytes(ze.getCompressedSize()));
      writeOut(ZipLong.getBytes(ze.getSize()));
    }
    // CheckStyle:MagicNumber OFF
    written += 12;
    // CheckStyle:MagicNumber ON

    ByteBuffer name = getName(ze);

    writeOut(ZipShort.getBytes(name.limit()));
    written += SHORT;

    // extra field length
    byte[] extra = ze.getCentralDirectoryExtra();
    writeOut(ZipShort.getBytes(extra.length));
    written += SHORT;

    // file comment length
    String comm = ze.getComment();
    if (comm == null)
    {
      comm = "";
    }

    ByteBuffer commentB = getEntryEncoding(ze).encode(comm);

    writeOut(ZipShort.getBytes(commentB.limit()));
    written += SHORT;

    // disk number start
    writeOut(ZERO);
    written += SHORT;

    // internal file attributes
    writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
    written += SHORT;

    // external file attributes
    writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
    written += WORD;

    // relative offset of LFH
    writeOut(ZipLong.getBytes(Math.min(lfhOffset, ZIP64_MAGIC)));
    written += WORD;

    // file name
    writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
    written += name.limit();

    // extra field
    writeOut(extra);
    written += extra.length;

    // file comment
    writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position());
    written += commentB.limit();
  }

  /**
   * 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 &quot;End of central dir record&quot;.
   * 
   * @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
  {
    writeOut(EOCD_SIG);

    // disk numbers
    writeOut(ZERO);
    writeOut(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));
    writeOut(num);
    writeOut(num);

    // length and location of CD
    writeOut(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
    writeOut(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));

    // ZIP file comment
    ByteBuffer data = this.zipEncoding.encode(comment);
    writeOut(ZipShort.getBytes(data.limit()));
    writeOut(data.array(), data.arrayOffset(), data.limit() - data.position());
  }

  /**
   * 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
   */
  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
   */
  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());
    }
  }

  private static final byte[] ONE = ZipLong.getBytes(1L);

  /**
   * Writes the &quot;ZIP64 End of central dir record&quot; and &quot;ZIP64
   * End of central dir locator&quot;.
   * 
   * @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
   */
  protected static long adjustToLong(int i)
  {
    return ZipUtil.adjustToLong(i);
  }

  private void deflateUntilInputIsNeeded() throws IOException
  {
    while (!def.needsInput())
    {
      deflate();
    }
  }

  private void writeVersionNeededToExtractAndGeneralPurposeBits(final int zipMethod, final boolean utfFallback,
      final boolean zip64) throws IOException
  {

    // CheckStyle:MagicNumber OFF
    int versionNeededToExtract = INITIAL_VERSION;
    GeneralPurposeBit b = new GeneralPurposeBit();
    b.useUTF8ForNames(useUTF8Flag || utfFallback);
    if (zipMethod == DEFLATED && raf == null)
    {
      // requires version 2 as we are going to store length info
      // in the data descriptor
      versionNeededToExtract = DATA_DESCRIPTOR_MIN_VERSION;
      b.useDataDescriptor(true);
    }
    if (zip64)
    {
      versionNeededToExtract = ZIP64_MIN_VERSION;
    }
    // CheckStyle:MagicNumber ON

    // version needed to extract
    writeOut(ZipShort.getBytes(versionNeededToExtract));
    // general purpose bit flag
    writeOut(b.encode());
  }

  /**
   * 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;
  }

}




Java Source Code List

com.cidesign.jianghomestylephone.DetailActivity.java
com.cidesign.jianghomestylephone.JiangActivity.java
com.cidesign.jianghomestylephone.MainActivity.java
com.cidesign.jianghomestylephone.SplashActivity.java
com.cidesign.jianghomestylephone.adapter.CommunityViewpagerAdapter.java
com.cidesign.jianghomestylephone.adapter.HumanityViewpagerAdapter.java
com.cidesign.jianghomestylephone.adapter.LandscapeViewpagerAdapter.java
com.cidesign.jianghomestylephone.adapter.LayoutCaculateAdapter.java
com.cidesign.jianghomestylephone.adapter.StoryViewpagerAdapter.java
com.cidesign.jianghomestylephone.async.AsyncDownTask.java
com.cidesign.jianghomestylephone.async.AsyncInitCommunityData.java
com.cidesign.jianghomestylephone.async.AsyncInitData.java
com.cidesign.jianghomestylephone.async.AsyncInitHomeData.java
com.cidesign.jianghomestylephone.async.AsyncInitHumanityData.java
com.cidesign.jianghomestylephone.async.AsyncInitLandscapeData.java
com.cidesign.jianghomestylephone.async.AsyncInitStoryData.java
com.cidesign.jianghomestylephone.db.DatabaseConfigUtil.java
com.cidesign.jianghomestylephone.db.DatabaseHelper.java
com.cidesign.jianghomestylephone.entity.ArticleEntity.java
com.cidesign.jianghomestylephone.entity.FileListEntity.java
com.cidesign.jianghomestylephone.entity.LayoutEntity.java
com.cidesign.jianghomestylephone.entity.RelativeLayoutRulesEntity.java
com.cidesign.jianghomestylephone.http.ArticalOperation.java
com.cidesign.jianghomestylephone.http.DownLoadThread.java
com.cidesign.jianghomestylephone.service.DownloadService.java
com.cidesign.jianghomestylephone.tools.CategoryDataLoadingLogic.java
com.cidesign.jianghomestylephone.tools.FileOperationTools.java
com.cidesign.jianghomestylephone.tools.JiangCategory.java
com.cidesign.jianghomestylephone.tools.LayoutMarginSetting.java
com.cidesign.jianghomestylephone.tools.LoadingDataFromDB.java
com.cidesign.jianghomestylephone.tools.LoadingImageTools.java
com.cidesign.jianghomestylephone.tools.MD5Tools.java
com.cidesign.jianghomestylephone.tools.StorageUtils.java
com.cidesign.jianghomestylephone.tools.TimeTools.java
com.cidesign.jianghomestylephone.tools.WidgetCache.java
com.cidesign.jianghomestylephone.tools.XmlParseTools.java
com.cidesign.jianghomestylephone.version.NetworkTool.java
com.cidesign.jianghomestylephone.version.VersionConfig.java
com.cidesign.jianghomestylephone.version.VersionUpdate.java
com.cidesign.jianghomestylephone.widget.CommunityRelativeLayout.java
com.cidesign.jianghomestylephone.widget.CustomScrollView.java
com.cidesign.jianghomestylephone.widget.HScrollViewTouchLogic.java
com.cidesign.jianghomestylephone.widget.HumanityRelativeLayout.java
com.cidesign.jianghomestylephone.widget.LandscapeRelativeLayout.java
com.cidesign.jianghomestylephone.widget.PopMenu.java
com.cidesign.jianghomestylephone.widget.StoryRelativeLayout.java
org.apache.tools.zip.AbstractUnicodeExtraField.java
org.apache.tools.zip.AsiExtraField.java
org.apache.tools.zip.CentralDirectoryParsingZipExtraField.java
org.apache.tools.zip.ExtraFieldUtils.java
org.apache.tools.zip.FallbackZipEncoding.java
org.apache.tools.zip.GeneralPurposeBit.java
org.apache.tools.zip.JarMarker.java
org.apache.tools.zip.NioZipEncoding.java
org.apache.tools.zip.Simple8BitZipEncoding.java
org.apache.tools.zip.UnicodeCommentExtraField.java
org.apache.tools.zip.UnicodePathExtraField.java
org.apache.tools.zip.UnixStat.java
org.apache.tools.zip.UnparseableExtraFieldData.java
org.apache.tools.zip.UnrecognizedExtraField.java
org.apache.tools.zip.UnsupportedZipFeatureException.java
org.apache.tools.zip.Zip64ExtendedInformationExtraField.java
org.apache.tools.zip.Zip64Mode.java
org.apache.tools.zip.Zip64RequiredException.java
org.apache.tools.zip.ZipConstants.java
org.apache.tools.zip.ZipEightByteInteger.java
org.apache.tools.zip.ZipEncodingHelper.java
org.apache.tools.zip.ZipEncoding.java
org.apache.tools.zip.ZipEntry.java
org.apache.tools.zip.ZipExtraField.java
org.apache.tools.zip.ZipFile.java
org.apache.tools.zip.ZipLong.java
org.apache.tools.zip.ZipOutputStream.java
org.apache.tools.zip.ZipShort.java
org.apache.tools.zip.ZipUtil.java