Home | History | Annotate | Download | only in zip
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.util.zip;
     28 
     29 import java.io.FilterOutputStream;
     30 import java.io.OutputStream;
     31 import java.io.InputStream;
     32 import java.io.IOException;
     33 
     34 /**
     35  * This class implements an output stream filter for compressing data in
     36  * the "deflate" compression format. It is also used as the basis for other
     37  * types of compression filters, such as GZIPOutputStream.
     38  *
     39  * @see         Deflater
     40  * @author      David Connelly
     41  */
     42 public
     43 class DeflaterOutputStream extends FilterOutputStream {
     44     /**
     45      * Compressor for this stream.
     46      */
     47     protected Deflater def;
     48 
     49     /**
     50      * Output buffer for writing compressed data.
     51      */
     52     protected byte[] buf;
     53 
     54     /**
     55      * Indicates that the stream has been closed.
     56      */
     57 
     58     private boolean closed = false;
     59 
     60     private final boolean syncFlush;
     61 
     62     /**
     63      * Creates a new output stream with the specified compressor,
     64      * buffer size and flush mode.
     65 
     66      * @param out the output stream
     67      * @param def the compressor ("deflater")
     68      * @param size the output buffer size
     69      * @param syncFlush
     70      *        if {@code true} the {@link #flush()} method of this
     71      *        instance flushes the compressor with flush mode
     72      *        {@link Deflater#SYNC_FLUSH} before flushing the output
     73      *        stream, otherwise only flushes the output stream
     74      *
     75      * @throws IllegalArgumentException if {@code size <= 0}
     76      *
     77      * @since 1.7
     78      */
     79     public DeflaterOutputStream(OutputStream out,
     80                                 Deflater def,
     81                                 int size,
     82                                 boolean syncFlush) {
     83         super(out);
     84         if (out == null || def == null) {
     85             throw new NullPointerException();
     86         } else if (size <= 0) {
     87             throw new IllegalArgumentException("buffer size <= 0");
     88         }
     89         this.def = def;
     90         this.buf = new byte[size];
     91         this.syncFlush = syncFlush;
     92     }
     93 
     94 
     95     /**
     96      * Creates a new output stream with the specified compressor and
     97      * buffer size.
     98      *
     99      * <p>The new output stream instance is created as if by invoking
    100      * the 4-argument constructor DeflaterOutputStream(out, def, size, false).
    101      *
    102      * @param out the output stream
    103      * @param def the compressor ("deflater")
    104      * @param size the output buffer size
    105      * @exception IllegalArgumentException if {@code size <= 0}
    106      */
    107     public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
    108         this(out, def, size, false);
    109     }
    110 
    111     /**
    112      * Creates a new output stream with the specified compressor, flush
    113      * mode and a default buffer size.
    114      *
    115      * @param out the output stream
    116      * @param def the compressor ("deflater")
    117      * @param syncFlush
    118      *        if {@code true} the {@link #flush()} method of this
    119      *        instance flushes the compressor with flush mode
    120      *        {@link Deflater#SYNC_FLUSH} before flushing the output
    121      *        stream, otherwise only flushes the output stream
    122      *
    123      * @since 1.7
    124      */
    125     public DeflaterOutputStream(OutputStream out,
    126                                 Deflater def,
    127                                 boolean syncFlush) {
    128         this(out, def, 512, syncFlush);
    129     }
    130 
    131 
    132     /**
    133      * Creates a new output stream with the specified compressor and
    134      * a default buffer size.
    135      *
    136      * <p>The new output stream instance is created as if by invoking
    137      * the 3-argument constructor DeflaterOutputStream(out, def, false).
    138      *
    139      * @param out the output stream
    140      * @param def the compressor ("deflater")
    141      */
    142     public DeflaterOutputStream(OutputStream out, Deflater def) {
    143         this(out, def, 512, false);
    144     }
    145 
    146     boolean usesDefaultDeflater = false;
    147 
    148 
    149     /**
    150      * Creates a new output stream with a default compressor, a default
    151      * buffer size and the specified flush mode.
    152      *
    153      * @param out the output stream
    154      * @param syncFlush
    155      *        if {@code true} the {@link #flush()} method of this
    156      *        instance flushes the compressor with flush mode
    157      *        {@link Deflater#SYNC_FLUSH} before flushing the output
    158      *        stream, otherwise only flushes the output stream
    159      *
    160      * @since 1.7
    161      */
    162     public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
    163         this(out, new Deflater(), 512, syncFlush);
    164         usesDefaultDeflater = true;
    165     }
    166 
    167     /**
    168      * Creates a new output stream with a default compressor and buffer size.
    169      *
    170      * <p>The new output stream instance is created as if by invoking
    171      * the 2-argument constructor DeflaterOutputStream(out, false).
    172      *
    173      * @param out the output stream
    174      */
    175     public DeflaterOutputStream(OutputStream out) {
    176         this(out, false);
    177         usesDefaultDeflater = true;
    178     }
    179 
    180     /**
    181      * Writes a byte to the compressed output stream. This method will
    182      * block until the byte can be written.
    183      * @param b the byte to be written
    184      * @exception IOException if an I/O error has occurred
    185      */
    186     public void write(int b) throws IOException {
    187         byte[] buf = new byte[1];
    188         buf[0] = (byte)(b & 0xff);
    189         write(buf, 0, 1);
    190     }
    191 
    192     /**
    193      * Writes an array of bytes to the compressed output stream. This
    194      * method will block until all the bytes are written.
    195      * @param b the data to be written
    196      * @param off the start offset of the data
    197      * @param len the length of the data
    198      * @exception IOException if an I/O error has occurred
    199      */
    200     public void write(byte[] b, int off, int len) throws IOException {
    201         if (def.finished()) {
    202             throw new IOException("write beyond end of stream");
    203         }
    204         if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
    205             throw new IndexOutOfBoundsException();
    206         } else if (len == 0) {
    207             return;
    208         }
    209         if (!def.finished()) {
    210             def.setInput(b, off, len);
    211             while (!def.needsInput()) {
    212                 deflate();
    213             }
    214         }
    215     }
    216 
    217     /**
    218      * Finishes writing compressed data to the output stream without closing
    219      * the underlying stream. Use this method when applying multiple filters
    220      * in succession to the same output stream.
    221      * @exception IOException if an I/O error has occurred
    222      */
    223     public void finish() throws IOException {
    224         if (!def.finished()) {
    225             def.finish();
    226             while (!def.finished()) {
    227                 deflate();
    228             }
    229         }
    230     }
    231 
    232     /**
    233      * Writes remaining compressed data to the output stream and closes the
    234      * underlying stream.
    235      * @exception IOException if an I/O error has occurred
    236      */
    237     public void close() throws IOException {
    238         if (!closed) {
    239             finish();
    240             if (usesDefaultDeflater)
    241                 def.end();
    242             out.close();
    243             closed = true;
    244         }
    245     }
    246 
    247     /**
    248      * Writes next block of compressed data to the output stream.
    249      * @throws IOException if an I/O error has occurred
    250      */
    251     protected void deflate() throws IOException {
    252         // Android-changed: output all available compressed data (b/4005091)
    253         int len = 0;
    254         while ((len = def.deflate(buf, 0, buf.length)) > 0) {
    255           out.write(buf, 0, len);
    256         }
    257     }
    258 
    259     /**
    260      * Flushes the compressed output stream.
    261      *
    262      * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean)
    263      * syncFlush} is {@code true} when this compressed output stream is
    264      * constructed, this method first flushes the underlying {@code compressor}
    265      * with the flush mode {@link Deflater#SYNC_FLUSH} to force
    266      * all pending data to be flushed out to the output stream and then
    267      * flushes the output stream. Otherwise this method only flushes the
    268      * output stream without flushing the {@code compressor}.
    269      *
    270      * @throws IOException if an I/O error has occurred
    271      *
    272      * @since 1.7
    273      */
    274     public void flush() throws IOException {
    275         if (syncFlush && !def.finished()) {
    276             int len = 0;
    277             while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0)
    278             {
    279                 out.write(buf, 0, len);
    280                 if (len < buf.length)
    281                     break;
    282             }
    283         }
    284         out.flush();
    285     }
    286 }
    287