001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.zip;
019:
020: import java.io.EOFException;
021: import java.io.FilterInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024:
025: import org.apache.harmony.archive.internal.nls.Messages;
026:
027: /**
028: * InflaterOutputStream read data which has been compressed using the DEFLATE
029: * compression method.
030: *
031: * @see Inflater
032: * @see DeflaterOutputStream
033: */
034: public class InflaterInputStream extends FilterInputStream {
035:
036: protected Inflater inf;
037:
038: protected byte[] buf;
039:
040: protected int len;
041:
042: boolean closed;
043:
044: boolean eof;
045:
046: static final int BUF_SIZE = 512;
047:
048: /**
049: * Constructs a new InflaterOutputStream on is
050: *
051: * @param is
052: * The InputStream to read data from
053: */
054: public InflaterInputStream(InputStream is) {
055: this (is, new Inflater(), BUF_SIZE);
056: }
057:
058: /**
059: * Constructs a new InflaterOutputStream on is, using the Inflater provided
060: * in inf.
061: *
062: * @param is
063: * The InputStream to read data from
064: * @param inf
065: * The Inflater to use for decompression
066: */
067: public InflaterInputStream(InputStream is, Inflater inf) {
068: this (is, inf, BUF_SIZE);
069: }
070:
071: /**
072: * Constructs a new InflaterOutputStream on is, using the Inflater provided
073: * in inf. The size of the inflation buffer is determined by bsize.
074: *
075: * @param is
076: * The InputStream to read data from
077: * @param inf
078: * The Inflater to use for decompression
079: * @param bsize
080: * size of the inflation buffer
081: */
082: public InflaterInputStream(InputStream is, Inflater inf, int bsize) {
083: super (is);
084: if (is == null || inf == null) {
085: throw new NullPointerException();
086: }
087: if (bsize <= 0) {
088: throw new IllegalArgumentException();
089: }
090: this .inf = inf;
091: buf = new byte[bsize];
092: }
093:
094: /**
095: * Reads a single byte of decompressed data.
096: *
097: * @return byte read
098: * @throws IOException
099: * If an error occurs reading
100: */
101: @Override
102: public int read() throws IOException {
103: byte[] b = new byte[1];
104: if (read(b, 0, 1) == -1) {
105: return -1;
106: }
107: return b[0] & 0xff;
108: }
109:
110: /**
111: * Reads up to nbytes of decompressed data and stores it in buf starting at
112: * off.
113: *
114: * @param buffer
115: * Buffer to store into
116: * @param off
117: * offset in buffer to store at
118: * @param nbytes
119: * number of bytes to store
120: * @return Number of uncompressed bytes read
121: * @throws IOException
122: * If an error occurs reading
123: */
124: @Override
125: public int read(byte[] buffer, int off, int nbytes)
126: throws IOException {
127: /* archive.1E=Stream is closed */
128: if (closed) {
129: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
130: }
131:
132: if (null == buffer) {
133: throw new NullPointerException();
134: }
135:
136: if (off < 0 || nbytes < 0 || off + nbytes > buffer.length) {
137: throw new IndexOutOfBoundsException();
138: }
139:
140: if (nbytes == 0) {
141: return 0;
142: }
143:
144: if (inf.finished()) {
145: eof = true;
146: return -1;
147: }
148:
149: // avoid int overflow, check null buffer
150: if (off <= buffer.length && nbytes >= 0 && off >= 0
151: && buffer.length - off >= nbytes) {
152: do {
153: if (inf.needsInput()) {
154: fill();
155: }
156: int result;
157: try {
158: result = inf.inflate(buffer, off, nbytes);
159: } catch (DataFormatException e) {
160: if (len == -1) {
161: throw new EOFException();
162: }
163: throw (IOException) (new IOException().initCause(e));
164: }
165: if (result > 0) {
166: return result;
167: } else if (inf.finished()) {
168: eof = true;
169: return -1;
170: } else if (inf.needsDictionary()) {
171: return -1;
172: } else if (len == -1) {
173: throw new EOFException();
174: // If result == 0, fill() and try again
175: }
176: } while (true);
177: }
178: throw new ArrayIndexOutOfBoundsException();
179: }
180:
181: protected void fill() throws IOException {
182: if (closed) {
183: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
184: }
185: if ((len = in.read(buf)) > 0) {
186: inf.setInput(buf, 0, len);
187: }
188: }
189:
190: /**
191: * Skips up to nbytes of uncompressed data.
192: *
193: * @param nbytes
194: * Number of bytes to skip
195: * @return Number of uncompressed bytes skipped
196: * @throws IOException
197: * If an error occurs skipping
198: */
199: @Override
200: public long skip(long nbytes) throws IOException {
201: if (nbytes >= 0) {
202: long count = 0, rem = 0;
203: while (count < nbytes) {
204: int x = read(
205: buf,
206: 0,
207: (rem = nbytes - count) > buf.length ? buf.length
208: : (int) rem);
209: if (x == -1) {
210: eof = true;
211: return count;
212: }
213: count += x;
214: }
215: return count;
216: }
217: throw new IllegalArgumentException();
218: }
219:
220: /**
221: * Returns 0 if this stream has been closed, 1 otherwise.
222: *
223: * @throws IOException
224: * If an error occurs
225: */
226: @Override
227: public int available() throws IOException {
228: if (closed) {
229: // archive.1E=Stream is closed
230: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
231: }
232: if (eof) {
233: return 0;
234: }
235: return 1;
236: }
237:
238: /**
239: * Closes the stream
240: *
241: * @throws IOException
242: * If an error occurs closing the stream
243: */
244: @Override
245: public void close() throws IOException {
246: if (!closed) {
247: inf.end();
248: closed = true;
249: eof = true;
250: super .close();
251: }
252: }
253:
254: /**
255: * Marks the current position in the stream.
256: *
257: * This implementation overrides the supertype implementation to do nothing
258: * at all.
259: *
260: * @param readlimit
261: * of no use
262: */
263: @Override
264: @SuppressWarnings("unused")
265: public void mark(int readlimit) {
266: // do nothing
267: }
268:
269: /**
270: * Reset the position of the stream to the last mark position.
271: *
272: * This implementation overrides the supertype implementation and always
273: * throws an {@link IOException IOException} when called.
274: *
275: * @throws IOException
276: * if the method is called
277: */
278: @Override
279: public void reset() throws IOException {
280: throw new IOException();
281: }
282:
283: /**
284: * Answers whether the receiver implements mark semantics. This type does
285: * not support mark, so always responds <code>false</code>.
286: *
287: * @return false
288: */
289: @Override
290: public boolean markSupported() {
291: return false;
292: }
293:
294: }
|