Add Java 6's new java.util.zip API.

This passes all but four of the harmony tests. The others fail because our
Deflater works differently to theirs; we already fail the corresponding
Deflater test.

The only jtreg test for ZipError neither passes nor fails: it appears that
this can only be thrown on Windows.

Bug: 2497395
Change-Id: I79687495da42507dda692c890ec939bdbc9be2b3
diff --git a/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java
new file mode 100644
index 0000000..13d65e4
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/DeflaterInputStream.java
@@ -0,0 +1,243 @@
+/* 
+ * 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
+ * 
+ *     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 java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An {@code InputStream} filter to compress data. Callers read
+ * compressed data in the "deflate" format from the uncompressed
+ * underlying stream.
+ * @since 1.6
+ * @hide
+ */
+public class DeflaterInputStream extends FilterInputStream {
+    private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+    protected final Deflater deflater;
+    protected final byte[] buf;
+
+    private boolean closed = false;
+    private boolean available = true;
+
+    /**
+     * Constructs a {@code DeflaterInputStream} with a new {@code Deflater} and an
+     * implementation-defined default internal buffer size. {@code in} is a source of
+     * uncompressed data, and this stream will be a source of compressed data.
+     * 
+     * @param in the source {@code InputStream}
+     */
+    public DeflaterInputStream(InputStream in) {
+        this(in, new Deflater(), DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and an
+     * implementation-defined default internal buffer size. {@code in} is a source of
+     * uncompressed data, and this stream will be a source of compressed data.
+     * 
+     * @param in the source {@code InputStream}
+     * @param deflater the {@code Deflater} to be used for compression
+     */
+    public DeflaterInputStream(InputStream in, Deflater deflater) {
+        this(in, deflater, DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Constructs a {@code DeflaterInputStream} with the given {@code Deflater} and
+     * given internal buffer size. {@code in} is a source of
+     * uncompressed data, and this stream will be a source of compressed data.
+     * 
+     * @param in the source {@code InputStream}
+     * @param deflater the {@code Deflater} to be used for compression
+     * @param bufferSize the length in bytes of the internal buffer
+     */
+    public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) {
+        super(in);
+        if (in == null || deflater == null) {
+            throw new NullPointerException();
+        }
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException();
+        }
+        this.deflater = deflater;
+        this.buf = new byte[bufferSize];
+    }
+
+    /**
+     * Closes the underlying input stream and discards any remaining uncompressed
+     * data.
+     */
+    @Override
+    public void close() throws IOException {
+        closed = true;
+        deflater.end();
+        in.close();
+    }
+
+    /**
+     * Reads a byte from the compressed input stream. The result will be a byte of compressed
+     * data corresponding to an uncompressed byte or bytes read from the underlying stream.
+     * 
+     * @return the byte or -1 if the end of the stream has been reached.
+     */
+    @Override
+    public int read() throws IOException {
+        byte[] result = new byte[1];
+        if (read(result, 0, 1) == -1) {
+            return -1;
+        }
+        return result[0] & 0xff;
+    }
+
+    /**
+     * Reads compressed data into a byte buffer. The result will be bytes of compressed
+     * data corresponding to an uncompressed byte or bytes read from the underlying stream.
+     * 
+     * @param b
+     *            the byte buffer that compressed data will be read into.
+     * @param off
+     *            the offset in the byte buffer where compressed data will start
+     *            to be read into.
+     * @param len
+     *            the length of the compressed data that is expected to read.
+     * @return the number of bytes read or -1 if the end of the compressed input
+     *         stream has been reached.
+     */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        checkClosed();
+        if (b == null) {
+            throw new NullPointerException();
+        }
+        if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return 0;
+        }
+
+        if (!available) {
+            return -1;
+        }
+
+        int count = 0;
+        while (count < len && !deflater.finished()) {
+            if (deflater.needsInput()) {
+                // read data from input stream
+                int byteCount = in.read(buf);
+                if (byteCount == -1) {
+                    deflater.finish();
+                } else {
+                    deflater.setInput(buf, 0, byteCount);
+                }
+            }
+            int byteCount = deflater.deflate(buf, 0, Math.min(buf.length, len - count));
+            if (byteCount == -1) {
+                break;
+            }
+            System.arraycopy(buf, 0, b, off + count, byteCount);
+            count += byteCount;
+        }
+        if (count == 0) {
+            count = -1;
+            available = false;
+        }
+        return count;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>Note: if {@code n > Integer.MAX_VALUE}, this stream will only attempt to
+     * skip {@code Integer.MAX_VALUE} bytes.
+     */
+    @Override
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException();
+        }
+        if (n > Integer.MAX_VALUE) {
+            n = Integer.MAX_VALUE;
+        }
+        checkClosed();
+
+        int remaining = (int) n;
+        byte[] tmp = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
+        while (remaining > 0) {
+            int count = read(tmp, 0, Math.min(remaining, tmp.length));
+            if (count == -1) {
+                break;
+            }
+            remaining -= count;
+        }
+        return n - remaining;
+    }
+
+    /**
+     * Returns 0 when when this stream has exhausted its input; and 1 otherwise.
+     * A result of 1 does not guarantee that further bytes can be returned,
+     * with or without blocking.
+     * 
+     * <p>Although consistent with the RI, this behavior is inconsistent with
+     * {@link InputStream#available()}, and violates the <a
+     * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov
+     * Substitution Principle</a>. This method should not be used.
+     * 
+     * @return 0 if no further bytes are available. Otherwise returns 1,
+     *         which suggests (but does not guarantee) that additional bytes are
+     *         available.
+     * @throws IOException if this stream is closed or an error occurs
+     */
+    @Override
+    public int available() throws IOException {
+        checkClosed();
+        return available ? 1 : 0;
+    }
+
+    /**
+     * Returns false because {@code DeflaterInputStream} does not support
+     * {@code mark}/{@code reset}.
+     */
+    @Override
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * This operation is not supported and does nothing.
+     */
+    @Override
+    public void mark(int limit) {
+    }
+
+    /**
+     * This operation is not supported and throws {@code IOException}.
+     */
+    @Override
+    public void reset() throws IOException {
+        throw new IOException();
+    }
+
+    private void checkClosed() throws IOException {
+        if (closed) {
+            throw new IOException("Stream is closed");
+        }
+    }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
index dd0da9b..d7f412e 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
@@ -159,7 +159,7 @@
     @Override
     public int read(byte[] buffer, int off, int nbytes) throws IOException {
         if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
+            throw new IOException("Stream is closed");
         }
         if (eos) {
             return -1;
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
index 8cd8cf2..fcaf8c15 100644
--- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
@@ -151,12 +151,8 @@
      */
     @Override
     public int read(byte[] buffer, int off, int nbytes) throws IOException {
-        /* archive.1E=Stream is closed */
-        if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
-
-        if (null == buffer) {
+        checkClosed();
+        if (buffer == null) {
             throw new NullPointerException();
         }
 
@@ -216,9 +212,7 @@
      *             if an {@code IOException} occurs.
      */
     protected void fill() throws IOException {
-        if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         // BEGIN android-only
         if (nativeEndBufSize > 0) {
             ZipFile.RAFStream is = (ZipFile.RAFStream)in;
@@ -283,10 +277,7 @@
      */
     @Override
     public int available() throws IOException {
-        if (closed) {
-            // archive.1E=Stream is closed
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         if (eof) {
             return 0;
         }
@@ -345,4 +336,9 @@
         return false;
     }
 
+    private void checkClosed() throws IOException {
+        if (closed) {
+            throw new IOException("Stream is closed");
+        }
+    }
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java
new file mode 100644
index 0000000..1329393
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterOutputStream.java
@@ -0,0 +1,171 @@
+/* 
+ * 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
+ * 
+ *     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 java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An {@code OutputStream} filter to decompress data. Callers write
+ * compressed data in the "deflate" format, and uncompressed data is
+ * written to the underlying stream.
+ * @since 1.6
+ * @hide
+ */
+public class InflaterOutputStream extends FilterOutputStream {
+    private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+    protected final Inflater inflater;
+    protected final byte[] buf;
+
+    private boolean closed = false;
+
+    /**
+     * Constructs an {@code InflaterOutputStream} with a new {@code Inflater} and an
+     * implementation-defined default internal buffer size. {@code out} is a destination
+     * for uncompressed data, and compressed data will be written to this stream.
+     * 
+     * @param out the destination {@code OutputStream}
+     */
+    public InflaterOutputStream(OutputStream out) {
+        this(out, new Inflater());
+    }
+
+    /**
+     * Constructs an {@code InflaterOutputStream} with the given {@code Inflater} and an
+     * implementation-defined default internal buffer size. {@code out} is a destination
+     * for uncompressed data, and compressed data will be written to this stream.
+     * 
+     * @param out the destination {@code OutputStream}
+     * @param inflater the {@code Inflater} to be used for decompression
+     */
+    public InflaterOutputStream(OutputStream out, Inflater inflater) {
+        this(out, inflater, DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
+     * Constructs an {@code InflaterOutputStream} with the given {@code Inflater} and
+     * given internal buffer size. {@code out} is a destination
+     * for uncompressed data, and compressed data will be written to this stream.
+     * 
+     * @param out the destination {@code OutputStream}
+     * @param inflater the {@code Inflater} to be used for decompression
+     * @param bufferSize the length in bytes of the internal buffer
+     */
+    public InflaterOutputStream(OutputStream out, Inflater inflater, int bufferSize) {
+        super(out);
+        if (out == null || inflater == null) {
+            throw new NullPointerException();
+        }
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException();
+        }
+        this.inflater = inflater;
+        this.buf = new byte[bufferSize];
+    }
+
+    /**
+     * Writes remaining data into the output stream and closes the underlying
+     * output stream.
+     */
+    @Override
+    public void close() throws IOException {
+        if (!closed) {
+            finish();
+            inflater.end();
+            out.close();
+            closed = true;
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        finish();
+        out.flush();
+    }
+
+    /**
+     * Finishes writing current uncompressed data into the InflaterOutputStream
+     * without closing it.
+     * 
+     * @throws IOException if an I/O error occurs, or the stream has been closed
+     */
+    public void finish() throws IOException {
+        checkClosed();
+        write();
+    }
+
+    /**
+     * Writes a byte to the decompressing output stream. {@code b} should be a byte of
+     * compressed input. The corresponding uncompressed data will be written to the underlying
+     * stream.
+     * 
+     * @param b the byte
+     * @throws IOException if an I/O error occurs, or the stream has been closed
+     * @throws ZipException if a zip exception occurs.
+     */
+    @Override
+    public void write(int b) throws IOException, ZipException {
+        write(new byte[] { (byte) b }, 0, 1);
+    }
+
+    /**
+     * Writes bytes to the decompressing output stream. {@code b} should be an array of
+     * compressed input. The corresponding uncompressed data will be written to the underlying
+     * stream.
+     * 
+     * @param b the byte array
+     * @param off the start offset in the byte array
+     * @param len the number of the bytes to take from the byte array
+     * @throws IOException if an I/O error occurs, or the stream has been closed
+     * @throws ZipException if a zip exception occurs.
+     * @throws NullPointerException if {@code b == null}.
+     * @throws IndexOutOfBoundsException if {@code off < 0 || len < 0 || off + len > b.length}
+     */
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException, ZipException {
+        checkClosed();
+        if (b == null) {
+            throw new NullPointerException();
+        }
+        if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        inflater.setInput(b, off, len);
+        write();
+    }
+
+    private void write() throws IOException, ZipException {
+        try {
+            int inflated;
+            while ((inflated = inflater.inflate(buf)) > 0) {
+                out.write(buf, 0, inflated);
+            }
+        } catch (DataFormatException e) {
+            throw new ZipException();
+        }
+    }
+
+    private void checkClosed() throws IOException {
+        if (closed) {
+            throw new IOException();
+        }
+    }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipError.java b/libcore/archive/src/main/java/java/util/zip/ZipError.java
new file mode 100644
index 0000000..a244d80
--- /dev/null
+++ b/libcore/archive/src/main/java/java/util/zip/ZipError.java
@@ -0,0 +1,34 @@
+/* 
+ * 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
+ * 
+ *     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 java.util.zip;
+
+/**
+ * Thrown when an unrecoverable ZIP error has occurred.
+ * @since 1.6
+ * @hide
+ */
+public class ZipError extends InternalError {
+    private static final long serialVersionUID = 853973422266861979L;
+
+    /**
+     * Constructs a ZipError with the given detail message.
+     */
+    public ZipError(String s) {
+        super(s);
+    }
+}
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipFile.java b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
index 6513622..a2ca35a 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipFile.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
@@ -181,7 +181,7 @@
 
     private void checkNotClosed() {
         if (mRaf == null) {
-            throw new IllegalStateException("Zip File closed.");
+            throw new IllegalStateException("Zip file closed");
         }
     }
 
@@ -458,6 +458,12 @@
 
         @Override
         public int available() throws IOException {
+            if (closed) {
+                // Our superclass will throw an exception, but there's a jtreg test that
+                // explicitly checks that the InputStream returned from ZipFile.getInputStream
+                // returns 0 even when closed.
+                return 0;
+            }
             return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
         }
     }
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
index 0d55a10..14141e9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
@@ -103,15 +103,13 @@
      *             if an {@code IOException} occurs.
      */
     public void closeEntry() throws IOException {
-        if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         if (currentEntry == null) {
             return;
         }
         if (currentEntry instanceof java.util.jar.JarEntry) {
             Attributes temp = ((JarEntry) currentEntry).getAttributes();
-            if (temp != null && temp.containsKey("hidden")) { //$NON-NLS-1$
+            if (temp != null && temp.containsKey("hidden")) {
                 return;
             }
         }
@@ -301,9 +299,7 @@
      */
     @Override
     public int read(byte[] buffer, int start, int length) throws IOException {
-        if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         if (inf.finished() || currentEntry == null) {
             return -1;
         }
@@ -385,9 +381,7 @@
 
     @Override
     public int available() throws IOException {
-        if (closed) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         // The InflaterInputStream contract says we must only return 0 or 1.
         return (currentEntry == null || inRead < currentEntry.size) ? 1 : 0;
     }
@@ -415,4 +409,10 @@
         l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
         return l;
     }
+
+    private void checkClosed() throws IOException {
+        if (closed) {
+            throw new IOException("Stream is closed");
+        }
+    }
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
index e53061a..3a9bd7e 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
@@ -109,9 +109,7 @@
      *             If an error occurs closing the entry.
      */
     public void closeEntry() throws IOException {
-        if (cDir == null) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         if (currentEntry == null) {
             return;
         }
@@ -196,8 +194,9 @@
      */
     @Override
     public void finish() throws IOException {
+        // TODO: is there a bug here? why not checkClosed?
         if (out == null) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
+            throw new IOException("Stream is closed");
         }
         if (cDir == null) {
             return;
@@ -261,10 +260,7 @@
                 throw new ZipException(Messages.getString("archive.21")); //$NON-NLS-1$
             }
         }
-        /* [MSG "archive.1E", "Stream is closed"] */
-        if (cDir == null) {
-            throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
-        }
+        checkClosed();
         if (entries.contains(ze.name)) {
             /* [MSG "archive.29", "Entry already exists: {0}"] */
             throw new ZipException(Messages.getString("archive.29", ze.name)); //$NON-NLS-1$
@@ -450,4 +446,10 @@
         }
         return result;
     }
+
+    private void checkClosed() throws IOException {
+        if (cDir == null) {
+            throw new IOException("Stream is closed");
+        }
+    }
 }
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
index 562b396..bf27661 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/AllTests.java
@@ -25,19 +25,19 @@
  */
 public class AllTests {
     public static Test suite() {
-        TestSuite suite = new TestSuite(
-                "Suite org.apache.harmony.archive.tests.java.util.zip");
-        // $JUnit-BEGIN$
+        TestSuite suite = new TestSuite("Suite org.apache.harmony.archive.tests.java.util.zip");
         suite.addTestSuite(Adler32Test.class);
         suite.addTestSuite(CheckedInputStreamTest.class);
         suite.addTestSuite(CheckedOutputStreamTest.class);
         suite.addTestSuite(CRC32Test.class);
         suite.addTestSuite(DataFormatExceptionTest.class);
+        suite.addTestSuite(DeflaterInputStreamTest.class);
         suite.addTestSuite(DeflaterOutputStreamTest.class);
         suite.addTestSuite(DeflaterTest.class);
         suite.addTestSuite(GZIPInputStreamTest.class);
         suite.addTestSuite(GZIPOutputStreamTest.class);
         suite.addTestSuite(InflaterInputStreamTest.class);
+        suite.addTestSuite(InflaterOutputStreamTest.class);
         suite.addTestSuite(InflaterTest.class);
         suite.addTestSuite(ZipEntryTest.class);
         suite.addTestSuite(ZipExceptionTest.class);
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java
new file mode 100644
index 0000000..9ed0f67
--- /dev/null
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterInputStreamTest.java
@@ -0,0 +1,378 @@
+/* 
+ * 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
+ * 
+ *     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.harmony.archive.tests.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterInputStream;
+
+import junit.framework.TestCase;
+
+public class DeflaterInputStreamTest extends TestCase {
+
+    String testStr = "Hi,this is a test";
+
+    InputStream is;
+
+    /**
+     * @tests DeflaterInputStream#available()
+     */
+    public void testAvailable() throws IOException {
+        byte[] buf = new byte[1024];
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        assertEquals(120, dis.read());
+        assertEquals(1, dis.available());
+        assertEquals(22, dis.read(buf, 0, 1024));
+        assertEquals(1, dis.available());
+        assertEquals(-1, dis.read());
+        assertEquals(0, dis.available());
+        dis.close();
+        try {
+            dis.available();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#close()
+     */
+    public void testClose() throws IOException {
+        byte[] buf = new byte[1024];
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        dis.close();
+        try {
+            dis.available();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(buf, 0, 1024);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        // can close after close
+        dis.close();
+    }
+
+    /**
+     * @tests DeflaterInputStream#mark()
+     */
+    public void testMark() throws IOException {
+        // mark do nothing
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        dis.mark(-1);
+        dis.mark(0);
+        dis.mark(1);
+        dis.close();
+        dis.mark(1);
+    }
+
+    /**
+     * @tests DeflaterInputStream#markSupported()
+     */
+    public void testMarkSupported() throws IOException {
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertFalse(dis.markSupported());
+        dis.close();
+        assertFalse(dis.markSupported());
+    }
+
+    /**
+     * @tests DeflaterInputStream#read()
+     */
+    public void testRead() throws IOException {
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        assertEquals(120, dis.read());
+        assertEquals(1, dis.available());
+        assertEquals(156, dis.read());
+        assertEquals(1, dis.available());
+        assertEquals(243, dis.read());
+        assertEquals(1, dis.available());
+        dis.close();
+        try {
+            dis.read();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#read(byte[],int,int)
+     */
+    public void testReadByteArrayIntInt() throws IOException {
+        byte[] buf1 = new byte[256];
+        byte[] buf2 = new byte[256];
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertEquals(23, dis.read(buf1, 0, 256));
+        dis = new DeflaterInputStream(is);
+        assertEquals(8, dis.read(buf2, 0, 256));
+        is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+        dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        assertEquals(120, dis.read());
+        assertEquals(1, dis.available());
+        assertEquals(22, dis.read(buf2, 0, 256));
+        assertEquals(1, dis.available());
+        assertEquals(-1, dis.read());
+        assertEquals(0, dis.available());
+        try {
+            dis.read(buf1, 0, 512);
+            fail("should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            dis.read(null, 0, 0);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            dis.read(null, -1, 0);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            dis.read(null, -1, -1);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            dis.read(buf1, -1, -1);
+            fail("should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            dis.read(buf1, 0, -1);
+            fail("should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        dis.close();
+        try {
+            dis.read(buf1, 0, 512);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(buf1, 0, 1);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(null, 0, 0);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(null, -1, 0);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(null, -1, -1);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(buf1, -1, -1);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.read(buf1, 0, -1);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#reset()
+     */
+    public void testReset() throws IOException {
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        try {
+            dis.reset();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        dis.close();
+        try {
+            dis.reset();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#skip()
+     */
+    public void testSkip() throws IOException {
+        byte[] buf = new byte[1024];
+        DeflaterInputStream dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        dis.skip(1);
+        assertEquals(1, dis.available());
+        assertEquals(22, dis.read(buf, 0, 1024));
+        assertEquals(1, dis.available());
+        dis.skip(1);
+        assertEquals(0, dis.available());
+        is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+        dis = new DeflaterInputStream(is);
+        assertEquals(1, dis.available());
+        dis.skip(56);
+        assertEquals(0, dis.available());
+        assertEquals(-1, dis.read(buf, 0, 1024));
+        try {
+            dis.skip(-1);
+            fail("should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        assertEquals(0, dis.available());
+        // can still skip
+        dis.skip(1);
+        dis.close();
+        try {
+            dis.skip(1);
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            dis.skip(-1);
+            fail("should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+        dis = new DeflaterInputStream(is);
+        assertEquals(23, dis.skip(Long.MAX_VALUE));
+        assertEquals(0, dis.available());
+    }
+
+    /**
+     * @tests DeflaterInputStream#DeflaterInputStream(InputStream)
+     */
+    public void testDeflaterInputStreamInputStream() {
+        // ok
+        new DeflaterInputStream(is);
+        // fail
+        try {
+            new DeflaterInputStream(null);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#DeflaterInputStream(InputStream,Deflater)
+     */
+    public void testDeflaterInputStreamInputStreamDeflater() {
+        // ok
+        new DeflaterInputStream(is, new Deflater());
+        // fail
+        try {
+            new DeflaterInputStream(is, null);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            new DeflaterInputStream(null, new Deflater());
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests DeflaterInputStream#DeflaterInputStream(InputStream,Deflater,int)
+     */
+    public void testDeflaterInputStreamInputStreamDeflaterInt() {
+        // ok
+        new DeflaterInputStream(is, new Deflater(), 1024);
+        // fail
+        try {
+            new DeflaterInputStream(is, null, 1024);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            new DeflaterInputStream(null, new Deflater(), 1024);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            new DeflaterInputStream(is, new Deflater(), -1);
+            fail("should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            new DeflaterInputStream(null, new Deflater(), -1);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            new DeflaterInputStream(is, null, -1);
+            fail("should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        is = new ByteArrayInputStream(testStr.getBytes("UTF-8"));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        is.close();
+        super.tearDown();
+    }
+}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java
new file mode 100644
index 0000000..9627613
--- /dev/null
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterOutputStreamTest.java
@@ -0,0 +1,392 @@
+/* 
+ * 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
+ * 
+ *     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.harmony.archive.tests.java.util.zip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterOutputStream;
+import java.util.zip.ZipException;
+
+import junit.framework.TestCase;
+
+public class InflaterOutputStreamTest extends TestCase {
+
+    private ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+    private byte[] compressedBytes = new byte[100];
+
+    private String testString = "Hello world";
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream)
+     */
+    public void test_ConstructorLjava_io_OutputStream() throws IOException {
+        new InflaterOutputStream(os);
+
+        try {
+            new InflaterOutputStream(null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream,Inflater)
+     */
+    public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Inflater() {
+        new InflaterOutputStream(os, new Inflater());
+
+        try {
+            new InflaterOutputStream(null, new Inflater());
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(os, null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#InflaterOutputStream(java.io.OutputStream,Inflater,int)
+     */
+    public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_InflaterI() {
+        new InflaterOutputStream(os, new Inflater(), 20);
+
+        try {
+            new InflaterOutputStream(null, null, 10);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(null, new Inflater(), -1);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(os, null, -1);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(null, null, -1);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(os, new Inflater(), 0);
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            new InflaterOutputStream(os, new Inflater(), -10000);
+            fail("Should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#close()
+     */
+    public void test_close() throws IOException {
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        ios.close();
+        // multiple close
+        ios.close();
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#flush()
+     */
+    public void test_flush() throws IOException {
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        ios.close();
+        try {
+            ios.flush();
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        
+        ios = new InflaterOutputStream(os);
+        ios.flush();
+        ios.flush();
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#finish()
+     */
+    public void test_finish() throws IOException {
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        ios.close();
+        try {
+            ios.finish();
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        
+        ios = new InflaterOutputStream(os);
+        ios.finish();
+        ios.finish();
+        ios.flush();
+        ios.flush();
+        ios.finish();
+        
+        byte[] bytes1 = {10,20,30,40,50};
+        Deflater defaultDeflater = new Deflater(Deflater.BEST_SPEED);
+        defaultDeflater.setInput(bytes1);
+        defaultDeflater.finish();
+        int length1 = defaultDeflater.deflate(compressedBytes);
+        
+        byte[] bytes2 = {100,90,80,70,60};
+        Deflater bestDeflater = new Deflater(Deflater.BEST_COMPRESSION );
+        bestDeflater.setInput(bytes2);
+        bestDeflater.finish();
+        int length2 = bestDeflater.deflate(compressedBytes,length1,compressedBytes.length-length1);
+        
+        ios = new InflaterOutputStream(os);
+        for (int i = 0; i < length1; i++) {
+            ios.write(compressedBytes[i]);
+        }
+        ios.finish();
+        ios.close();
+        
+        byte[] result = os.toByteArray();
+        for(int i =0;i<bytes1.length; i++){
+            assertEquals(bytes1[i],result[i]);
+        }
+        
+        ios = new InflaterOutputStream(os);
+        for (int i = length1; i < length2*2; i++) {
+            ios.write(compressedBytes[i]);
+        }
+        ios.finish();
+        ios.close();
+        
+        result = os.toByteArray();
+        for(int i =0;i<bytes2.length; i++){
+            assertEquals(bytes2[i],result[bytes1.length+i]);
+        }
+        
+    }
+    
+    /**
+     * @tests java.util.zip.InflaterOutputStream#write(int)
+     */
+    public void test_write_I() throws IOException {
+        int length = compressToBytes(testString);
+
+        // uncompress the data stored in the compressedBytes
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        for (int i = 0; i < length; i++) {
+            ios.write(compressedBytes[i]);
+        }
+
+        String result = new String(os.toByteArray());
+        assertEquals(testString, result);
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#write(int)
+     */
+    public void test_write_I_Illegal() throws IOException {
+
+        // write after close
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        ios.close();
+        try {
+            ios.write(-1);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#write(byte[],int,int)
+     */
+    public void test_write_$BII() throws IOException {
+        int length = compressToBytes(testString);
+
+        // uncompress the data stored in the compressedBytes
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        ios.write(compressedBytes, 0, length);
+
+        String result = new String(os.toByteArray());
+        assertEquals(testString, result);
+    }
+
+    /**
+     * @tests java.util.zip.InflaterOutputStream#write(byte[],int,int)
+     */
+    public void test_write_$BII_Illegal() throws IOException {
+        // write error compression (ZIP) format
+        InflaterOutputStream ios = new InflaterOutputStream(os);
+        byte[] bytes = { 0, 1, 2, 3 };
+        try {
+            ios.write(bytes, 0, 4);
+            fail("Should throw ZipException");
+        } catch (ZipException e) {
+            // expected
+        }
+        try {
+            ios.flush();
+            fail("Should throw ZipException");
+        } catch (ZipException e) {
+            // expected
+        }
+
+        // write after close
+        ios = new InflaterOutputStream(os);
+        ios.close();
+        try {
+            ios.write(bytes, 0, 4);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, -1, 4);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, -1, -4);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, 0, 400);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+        try {
+            ios.write(null, -1, 4);
+            fail("Should throw IOException");
+        } catch (IOException e) {
+            // expected
+        }
+
+        ios = new InflaterOutputStream(os);
+        try {
+            ios.write(null, 0, 4);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            ios.write(null, -1, 4);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            ios.write(null, 0, -4);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            ios.write(null, 0, 1000);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, -1, 4);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, 0, -4);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, 0, 100);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            ios.write(bytes, -100, 100);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        ios = new InflaterOutputStream(os);
+        ios.finish();
+        
+        try {
+            ios.write(bytes, -1,-100);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        try {
+            ios.write(null, -1,-100);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        
+        ios = new InflaterOutputStream(os);
+        ios.flush();
+        try {
+            ios.write(bytes, 0, 4);
+            fail("Should throw ZipException");
+        } catch (ZipException e) {
+            // expected
+        }
+    }
+
+    // Compress the test string into compressedBytes
+    private int compressToBytes(String string) {
+        byte[] input = string.getBytes();
+        Deflater deflater = new Deflater();
+        deflater.setInput(input);
+        deflater.finish();
+        return deflater.deflate(compressedBytes);
+    }
+
+}