1   /*
2    * %W% %E%
3    *
4    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6    */
7   
8   package java.util.zip;
9   
10  import java.io.FilterOutputStream;
11  import java.io.OutputStream;
12  import java.io.InputStream;
13  import java.io.IOException;
14  
15  /**
16   * This class implements an output stream filter for compressing data in
17   * the "deflate" compression format. It is also used as the basis for other
18   * types of compression filters, such as GZIPOutputStream.
19   *
20   * @see     Deflater
21   * @version     %I%, %G%
22   * @author  David Connelly
23   */
24  public
25  class DeflaterOutputStream extends FilterOutputStream {
26      /**
27       * Compressor for this stream.
28       */
29      protected Deflater def;
30  
31      /**
32       * Output buffer for writing compressed data.
33       */
34      protected byte[] buf;
35     
36      /**
37       * Indicates that the stream has been closed.
38       */
39  
40      private boolean closed = false;
41  
42      /**
43       * Creates a new output stream with the specified compressor and
44       * buffer size.
45       * @param out the output stream
46       * @param def the compressor ("deflater")
47       * @param size the output buffer size
48       * @exception IllegalArgumentException if size is <= 0
49       */
50      public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
51          super(out);
52          if (out == null || def == null) {
53              throw new NullPointerException();
54          } else if (size <= 0) {
55              throw new IllegalArgumentException("buffer size <= 0");
56          }
57          this.def = def;
58          buf = new byte[size];
59      }
60  
61      /**
62       * Creates a new output stream with the specified compressor and
63       * a default buffer size.
64       * @param out the output stream
65       * @param def the compressor ("deflater")
66       */
67      public DeflaterOutputStream(OutputStream out, Deflater def) {
68      this(out, def, 512);
69      }
70  
71      boolean usesDefaultDeflater = false;
72  
73      /**
74       * Creates a new output stream with a default compressor and buffer size.
75       * @param out the output stream
76       */
77      public DeflaterOutputStream(OutputStream out) {
78      this(out, new Deflater());
79          usesDefaultDeflater = true;
80      }
81  
82      /**
83       * Writes a byte to the compressed output stream. This method will
84       * block until the byte can be written.
85       * @param b the byte to be written
86       * @exception IOException if an I/O error has occurred
87       */
88      public void write(int b) throws IOException {
89          byte[] buf = new byte[1];
90      buf[0] = (byte)(b & 0xff);
91      write(buf, 0, 1);
92      }
93  
94      /**
95       * Writes an array of bytes to the compressed output stream. This
96       * method will block until all the bytes are written.
97       * @param b the data to be written
98       * @param off the start offset of the data
99       * @param len the length of the data
100      * @exception IOException if an I/O error has occurred
101      */
102     public void write(byte[] b, int off, int len) throws IOException {
103     if (def.finished()) {
104         throw new IOException("write beyond end of stream");
105     }
106         if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
107         throw new IndexOutOfBoundsException();
108     } else if (len == 0) {
109         return;
110     }
111     if (!def.finished()) {
112             // Deflate no more than stride bytes at a time.  This avoids
113             // excess copying in deflateBytes (see Deflater.c)
114             int stride = buf.length;
115             for (int i = 0; i < len; i+= stride) {
116                 def.setInput(b, off + i, Math.min(stride, len - i));
117                 while (!def.needsInput()) {
118                     deflate();
119                 }
120             }
121     }
122     }
123 
124     /**
125      * Finishes writing compressed data to the output stream without closing
126      * the underlying stream. Use this method when applying multiple filters
127      * in succession to the same output stream.
128      * @exception IOException if an I/O error has occurred
129      */
130     public void finish() throws IOException {
131     if (!def.finished()) {
132         def.finish();
133         while (!def.finished()) {
134         deflate();
135         }
136     }
137     }
138 
139     /**
140      * Writes remaining compressed data to the output stream and closes the
141      * underlying stream.
142      * @exception IOException if an I/O error has occurred
143      */
144     public void close() throws IOException {
145         if (!closed) {
146             finish();
147             if (usesDefaultDeflater)
148                 def.end();
149             out.close();
150             closed = true;
151         }
152     }
153 
154     /**
155      * Writes next block of compressed data to the output stream.
156      * @throws IOException if an I/O error has occurred
157      */
158     protected void deflate() throws IOException {
159     int len = def.deflate(buf, 0, buf.length);
160     if (len > 0) {
161         out.write(buf, 0, len);
162     }
163     }
164 }
165