/*
 * Copyright (c) 2010, 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.
 *
 * 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.
 */

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;

/*
 * @test
 * @bug 6985763
 * @summary verify that proper exceptions are thrown
 * @compile -XDignore.symbol.file Utils.java TestExceptions.java
 * @run main TestExceptions
 * @author ksrini
 */

public class TestExceptions {

    static final File testJar = new File("test.jar");
    static final File testPackFile = new File("test.pack");

    static void init() {
        Utils.jar("cvf", testJar.getAbsolutePath(), ".");
        JarFile jf = null;
        try {
            jf = new JarFile(testJar);
            Utils.pack(jf, testPackFile);
        } catch (IOException ioe) {
            throw new Error("Initialization error", ioe);
        } finally {
            Utils.close(jf);
        }
    }

    // a test that closes the input jarFile.
    static void pack200Test1() {
        PackTestInput ti = null;
        // setup the scenario
        try {
            ti = new PackTestInput(new JarFile(testJar), new ByteArrayOutputStream());
        } catch (Exception e) {
            throw new Error("Initialization error", e);
        } finally {
            Utils.close(ti.getJarFile());
        }
        // test the scenario
        try {
            System.out.println(ti);
            Pack200.Packer p = Pack200.newPacker();
            p.pack(ti.getJarFile(), ti.getOutputStream());
        } catch (Exception e) {
            ti.checkException(e);
        } finally {
            if (ti != null) {
                ti.close();
            }
        }
    }

    // test the Pack200.pack(JarFile, OutputStream);
    static void pack200Test2() {
        List<PackTestInput> tlist = new ArrayList<PackTestInput>();
        try {
            // setup the test scenarios
            try {
                tlist.add(new PackTestInput((JarFile)null, null));
                tlist.add(new PackTestInput(new JarFile(testJar), null));
                tlist.add(new PackTestInput((JarFile)null, new ByteArrayOutputStream()));
            } catch (Exception e) {
                throw new Error("Initialization error", e);
            }

            // test the scenarios
            for (PackTestInput ti : tlist) {
                System.out.println(ti);
                try {
                    Pack200.Packer p = Pack200.newPacker();
                    p.pack(ti.getJarFile(), ti.getOutputStream());
                } catch (Exception e) {
                    ti.checkException(e);
                }
            }
        } finally { // keep jprt happy
            for (TestInput ti : tlist) {
                if (ti != null) {
                    ti.close();
                }
            }
        }
    }

    // test the Pack200.pack(JarInputStream, OutputStream);
    static void pack200Test3() {
        List<PackTestJarInputStream> tlist = new ArrayList<PackTestJarInputStream>();
        try {
            // setup the test scenarios
            try {
                tlist.add(new PackTestJarInputStream((JarInputStream)null, null));
                tlist.add(new PackTestJarInputStream((JarInputStream)null,
                        new ByteArrayOutputStream()));
                tlist.add(new PackTestJarInputStream(
                        new JarInputStream(new FileInputStream(testJar)), null));

            } catch (Exception e) {
                throw new Error("Initialization error", e);
            }
            for (PackTestJarInputStream ti : tlist) {
                System.out.println(ti);
                try {
                    Pack200.Packer p = Pack200.newPacker();
                    p.pack(ti.getJarInputStream(), ti.getOutputStream());
                } catch (Exception e) {
                    ti.checkException(e);
                }
            }
        } finally { // keep jprt happy
            for (PackTestJarInputStream ti : tlist) {
                if (ti != null) {
                    ti.close();
                }
            }
        }
    }

    // test the Pack200.unpack(InputStream, OutputStream);
    static void unpack200Test1() {
        List<UnpackTestInput> tlist = new ArrayList<UnpackTestInput>();
        try {
            // setup the test scenarios
            try {
                tlist.add(new UnpackTestInput((InputStream)null, null));
                tlist.add(new UnpackTestInput(new FileInputStream(testPackFile),
                        null));
                tlist.add(new UnpackTestInput((InputStream) null,
                        new JarOutputStream(new ByteArrayOutputStream())));
            } catch (Exception e) {
                throw new Error("Initialization error", e);
            }

            // test the scenarios
            for (UnpackTestInput ti : tlist) {
                System.out.println(ti);
                try {
                    Pack200.Unpacker unpacker = Pack200.newUnpacker();
                    unpacker.unpack(ti.getInputStream(), ti.getJarOutputStream());
                } catch (Exception e) {
                    ti.checkException(e);
                }
            }
        } finally { // keep jprt happy
            for (TestInput ti : tlist) {
                if (ti != null) {
                    ti.close();
                }
            }
        }
    }

    // test the Pack200.unpack(File, OutputStream);
    static void unpack200Test2() {
        List<UnpackTestFileInput> tlist = new ArrayList<UnpackTestFileInput>();
        try {
            // setup the test scenarios
            try {
                tlist.add(new UnpackTestFileInput((File)null, null));
                tlist.add(new UnpackTestFileInput(testPackFile, null));
                tlist.add(new UnpackTestFileInput((File)null,
                        new JarOutputStream(new ByteArrayOutputStream())));
            } catch (Exception e) {
                throw new Error("Initialization error", e);
            }

            // test the scenarios
            for (UnpackTestFileInput ti : tlist) {
                System.out.println(ti);
                try {
                    Pack200.Unpacker unpacker = Pack200.newUnpacker();
                    unpacker.unpack(ti.getInputFile(), ti.getJarOutputStream());
                } catch (Exception e) {
                    ti.checkException(e);
                }
            }
        } finally { // keep jprt happy
            for (TestInput ti : tlist) {
                if (ti != null) {
                    ti.close();
                }
            }
        }
    }

    public static void main(String... args) {
        init();
        pack200Test1();
        pack200Test2();
        pack200Test3();
        unpack200Test1();
    }

    // containers for test inputs and management
    static abstract class TestInput {

        private final Object in;
        private final Object out;
        final boolean shouldNPE;
        final String testname;

        public TestInput(String name, Object in, Object out) {
            this.testname = name;
            this.in = in;
            this.out = out;
            shouldNPE = (in == null || out == null);
        }

        @Override
        public String toString() {
            StringBuilder outStr = new StringBuilder(testname);
            outStr.append(", input:").append(in);
            outStr.append(", output:").append(this.out);
            outStr.append(", should NPE:").append(shouldNPE);
            return outStr.toString();
        }

        void close() {
            if (in != null && (in instanceof Closeable)) {
                Utils.close((Closeable) in);
            }
            if (out != null && (out instanceof Closeable)) {
                Utils.close((Closeable) out);
            }
        }

        void checkException(Throwable t) {
            if (shouldNPE) {
                if (t instanceof NullPointerException) {
                    System.out.println("Got expected exception");
                    return;
                } else {
                    throw new RuntimeException("Expected NPE, but got ", t);
                }
            }
            if (t instanceof IOException) {
                System.out.println("Got expected exception");
                return;
            } else {
                throw new RuntimeException("Expected IOException but got ", t);
            }
        }
    }

    static class PackTestInput extends TestInput {

        public PackTestInput(JarFile jf, OutputStream out) {
            super("PackTestInput", jf, out);
        }

        JarFile getJarFile() {
            return (JarFile) super.in;
        }

        OutputStream getOutputStream() {
            return (OutputStream) super.out;
        }
    };

    static class PackTestJarInputStream extends TestInput {

        public PackTestJarInputStream(JarInputStream in, OutputStream out) {
            super("PackTestJarInputStream", in, out);
        }

        JarInputStream getJarInputStream() {
            return (JarInputStream) super.in;
        }

        OutputStream getOutputStream() {
            return (OutputStream) super.out;
        }
    };

    static class UnpackTestInput extends TestInput {

        public UnpackTestInput(InputStream in, JarOutputStream out) {
            super("UnpackTestInput", in, out);
        }

        InputStream getInputStream() {
            return (InputStream) super.in;
        }

        JarOutputStream getJarOutputStream() {
            return (JarOutputStream) super.out;
        }
    };

    static class UnpackTestFileInput extends TestInput {

        public UnpackTestFileInput(File in, JarOutputStream out) {
            super("UnpackTestInput", in, out);
        }

        File getInputFile() {
            return (File) super.in;
        }

        JarOutputStream getJarOutputStream() {
            return (JarOutputStream) super.out;
        }
    };
}
