content: /*
   * Copyright (C) 2014 The Android Open Source Project
   * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package java.util.zip;
  
  import java.io.FilterOutputStream;
  import java.io.OutputStream;
  import java.io.InputStream;
  import java.io.IOException;
  
  /**
   * This class implements an output stream filter for compressing data in
   * the "deflate" compression format. It is also used as the basis for other
   * types of compression filters, such as GZIPOutputStream.
   *
   * @see         Deflater
   * @author      David Connelly
   */
  public
  class DeflaterOutputStream extends FilterOutputStream {
      /**
       * Compressor for this stream.
       */
      protected Deflater def;
  
      /**
       * Output buffer for writing compressed data.
       */
      protected byte[] buf;
  
      /**
       * Indicates that the stream has been closed.
       */
  
      private boolean closed = false;
  
      private final boolean syncFlush;
  
      /**
       * Creates a new output stream with the specified compressor,
       * buffer size and flush mode.
  
       * @param out the output stream
       * @param def the compressor ("deflater")
       * @param size the output buffer size
       * @param syncFlush
       *        if {@code true} the {@link #flush()} method of this
       *        instance flushes the compressor with flush mode
       *        {@link Deflater#SYNC_FLUSH} before flushing the output
       *        stream, otherwise only flushes the output stream
       *
       * @throws IllegalArgumentException if {@code size <= 0}
       *
       * @since 1.7
       */
      public DeflaterOutputStream(OutputStream out,
                                  Deflater def,
                                  int size,
                                  boolean syncFlush) {
          super(out);
          if (out == null || def == null) {
              throw new NullPointerException();
          } else if (size <= 0) {
              throw new IllegalArgumentException("buffer size <= 0");
          }
          this.def = def;
          this.buf = new byte[size];
          this.syncFlush = syncFlush;
      }
  
  
      /**
       * Creates a new output stream with the specified compressor and
       * buffer size.
       *
       * <p>The new output stream instance is created as if by invoking
       * the 4-argument constructor DeflaterOutputStream(out, def, size, false).
       *
       * @param out the output stream
       * @param def the compressor ("deflater")
       * @param size the output buffer size
       * @exception IllegalArgumentException if {@code size <= 0}
       */
      public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
          this(out, def, size, false);
      }
  
      /**
       * Creates a new output stream with the specified compressor, flush
       * mode and a default buffer size.
       *
       * @param out the output stream
       * @param def the compressor ("deflater")
       * @param syncFlush
       *        if {@code true} the {@link #flush()} method of this
       *        instance flushes the compressor with flush mode
       *        {@link Deflater#SYNC_FLUSH} before flushing the output
       *        stream, otherwise only flushes the output stream
       *
       * @since 1.7
       */
      public DeflaterOutputStream(OutputStream out,
                                  Deflater def,
                                  boolean syncFlush) {
          this(out, def, 512, syncFlush);
      }
  
  
      /**
       * Creates a new output stream with the specified compressor and
       * a default buffer size.
       *
       * <p>The new output stream instance is created as if by invoking
       * the 3-argument constructor DeflaterOutputStream(out, def, false).
       *
       * @param out the output stream
       * @param def the compressor ("deflater")
       */
      public DeflaterOutputStream(OutputStream out, Deflater def) {
          this(out, def, 512, false);
      }
  
      boolean usesDefaultDeflater = false;
  
  
      /**
       * Creates a new output stream with a default compressor, a default
       * buffer size and the specified flush mode.
       *
       * @param out the output stream
       * @param syncFlush
       *        if {@code true} the {@link #flush()} method of this
       *        instance flushes the compressor with flush mode
       *        {@link Deflater#SYNC_FLUSH} before flushing the output
       *        stream, otherwise only flushes the output stream
       *
       * @since 1.7
       */
      public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
          this(out, new Deflater(), 512, syncFlush);
          usesDefaultDeflater = true;
      }
  
      /**
       * Creates a new output stream with a default compressor and buffer size.
       *
       * <p>The new output stream instance is created as if by invoking
       * the 2-argument constructor DeflaterOutputStream(out, false).
       *
       * @param out the output stream
       */
      public DeflaterOutputStream(OutputStream out) {
          this(out, false);
          usesDefaultDeflater = true;
      }
  
      /**
       * Writes a byte to the compressed output stream. This method will
       * block until the byte can be written.
       * @param b the byte to be written
       * @exception IOException if an I/O error has occurred
       */
      public void write(int b) throws IOException {
          byte[] buf = new byte[1];
          buf[0] = (byte)(b & 0xff);
          write(buf, 0, 1);
      }
  
      /**
       * Writes an array of bytes to the compressed output stream. This
       * method will block until all the bytes are written.
       * @param b the data to be written
       * @param off the start offset of the data
       * @param len the length of the data
       * @exception IOException if an I/O error has occurred
       */
      public void write(byte[] b, int off, int len) throws IOException {
          if (def.finished()) {
              throw new IOException("write beyond end of stream");
          }
          if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
              throw new IndexOutOfBoundsException();
          } else if (len == 0) {
              return;
          }
          if (!def.finished()) {
              def.setInput(b, off, len);
              while (!def.needsInput()) {
                  deflate();
              }
          }
      }
  
      /**
       * Finishes writing compressed data to the output stream without closing
       * the underlying stream. Use this method when applying multiple filters
       * in succession to the same output stream.
       * @exception IOException if an I/O error has occurred
       */
      public void finish() throws IOException {
          if (!def.finished()) {
              def.finish();
              while (!def.finished()) {
                  deflate();
              }
          }
      }
  
      /**
       * Writes remaining compressed data to the output stream and closes the
       * underlying stream.
       * @exception IOException if an I/O error has occurred
       */
      public void close() throws IOException {
          if (!closed) {
              finish();
              if (usesDefaultDeflater)
                  def.end();
              out.close();
              closed = true;
          }
      }
  
      /**
       * Writes next block of compressed data to the output stream.
       * @throws IOException if an I/O error has occurred
       */
      protected void deflate() throws IOException {
          // Android-changed: Output all available compressed data (b/4005091).
          // See http://b/111496419 for more details.
          // int len = def.deflate(buf, 0, buf.length);
          // if (len > 0) {
          //     out.write(buf, 0, len);
          // }
          int len = 0;
          while ((len = def.deflate(buf, 0, buf.length)) > 0) {
            out.write(buf, 0, len);
          }
      }
  
      /**
       * Flushes the compressed output stream.
       *
       * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean)
       * syncFlush} is {@code true} when this compressed output stream is
       * constructed, this method first flushes the underlying {@code compressor}
       * with the flush mode {@link Deflater#SYNC_FLUSH} to force
       * all pending data to be flushed out to the output stream and then
       * flushes the output stream. Otherwise this method only flushes the
       * output stream without flushing the {@code compressor}.
       *
       * @throws IOException if an I/O error has occurred
       *
       * @since 1.7
       */
      public void flush() throws IOException {
          if (syncFlush && !def.finished()) {
              int len = 0;
              while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0)
              {
                  out.write(buf, 0, len);
                  if (len < buf.length)
                      break;
              }
          }
          out.flush();
      }
  }
  