/*
 * Copyright (c) 2002-2012 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jgoodies.forms.layout;

import java.io.*;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

import junit.framework.TestCase;

import com.jgoodies.forms.factories.CC;

/**
 * Tests the serialization and deserialization of instances of
 * {@code FormLayout} and {@code JPanel}.
 *
 * @author Karsten Lentzsch
 * @version $Revision: 1.12 $
 *
 * @see java.io.Serializable
 */
public final class SerializationTest extends TestCase {


    /**
     * Tests the serialization of a FormLayout that has just been constructed.
     * The layout contains no components and the layout algorithm has not
     * been performed.
     */
    public static void testSerializeConstructedLayout() {
        FormLayout layout = createSampleLayout();
        OutputStream out = new ByteArrayOutputStream();
        serialize(out, layout);
    }


    /**
     * Tests the serialization of an empty FormLayout that has computed
     * a layout immediately after its construction.
     * The layout contains no components.
     */
    public static void testSerializeEmptyLayout() {
        FormLayout layout = createSampleLayout();
        doLayout(layout);
        OutputStream out = new ByteArrayOutputStream();
        serialize(out, layout);
    }


    /**
     * Tests the serialization of a panel that is laid out using
     * a FormLayout. The panel consists some sample components that in turn
     * uses some sample {@code CellConstraints}.
     */
    public static void testSerializePanel() {
        JPanel panel = createSamplePanel();
        OutputStream out = new ByteArrayOutputStream();
        serialize(out, panel);
    }


    /**
     * Tests the deserialization of a FormLayout that has just been constructed.
     * The layout contains no components and the layout algorithm has not
     * been performed.
     */
    public static void testDeserializeConstructedLayout() {
        FormLayout layout = createSampleLayout();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(out, layout);
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        deserialize(in);
    }


    /**
     * Tests the deserialization of an empty FormLayout that has computed
     * a layout immediately after its construction.
     * The layout contains no components.
     */
    public static void testDeserializeEmptyLayout() {
        FormLayout layout = createSampleLayout();
        doLayout(layout);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(out, layout);
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        deserialize(in);
    }


    /**
     * Tests the deserialization of a panel that is laid out using
     * a FormLayout. The panel consists some sample components that in turn
     * uses some sample {@code CellConstraints}.
     */
    public static void testDeserializePanel() {
        JPanel panel = createSamplePanel();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(out, panel);
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        deserialize(in);
    }


    /**
     * Tests that the a layout can be computed with a deserialized
     * empty FormLayout.
     */
    public static void testLayoutDeserializedEmptyLayout() {
        FormLayout layout = createSampleLayout();
        doLayout(layout);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(out, layout);
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        FormLayout layout2 = (FormLayout) deserialize(in);
        doLayout(layout2);
    }


    /**
     * Tests that the a panel can be laid out with a deserialized
     * FormLayout.
     */
    public static void testLayoutDeserializedPanel() {
        JPanel panel = createSamplePanel();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(out, panel);
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        JPanel panel2 = (JPanel) deserialize(in);
        panel2.doLayout();
    }


    // Helper Code *********************************************************

    /**
     * Creates and returns a sample {@code FormLayout} instance
     * that uses all prebuilt alignments and {@code Size} implementations
     * so we can test their serialization and deserialization.
     *
     * @return a sample layout
     */
    private static FormLayout createSampleLayout() {
        return new FormLayout(
                "l:1px, c:2dlu, r:3mm, f:m, p, d, max(p;3dlu), min(p;7px)",
                "t:1px, c:2dlu, b:3mm, f:m, p, d, max(p;3dlu), min(p;7px)");
    }


    /**
     * Creates and returns a sample panel that uses the sample layout
     * created by {@code #createSampleLayout}. Useful to test the
     * FormLayout serialization in combination with a layout container
     * and some components managed by the FormLayout. Especially it tests
     * the serialization of {@code CellConstraints} objects.
     *
     * @return a sample panel
     */
    private static JPanel createSamplePanel() {
        JPanel panel = new JPanel(createSampleLayout());
        panel.add(new JLabel("Test1"),  CC.xy(1, 1, "l, t"));
        panel.add(new JButton("Test2"), CC.xy(2, 2, "c, c"));
        panel.add(new JButton("Test3"), CC.xy(3, 3, "r, b"));
        panel.add(new JButton("Test4"), CC.xy(4, 4, "f, f"));
        panel.doLayout();
        return panel;
    }


    /**
     * Lays out a container using the given {@code FormLayout}
     * and returns the layout info object.
     *
     * @param layout    the FormLayout used to lay out
     * @return the layout info after the container has been laid out
     */
    private static FormLayout.LayoutInfo doLayout(FormLayout layout) {
        JPanel panel = new JPanel(layout);
        panel.doLayout();
        FormLayout.LayoutInfo info = layout.getLayoutInfo(panel);
        return info;
    }


    /**
     * Serializes the given object and writes it to the given
     * output stream.
     *
     * @param out      the stream to write the serialized object
     * @param object   the object to be serialized
     */
    private static void serialize(OutputStream out, Object object) {
        ObjectOutputStream objectOut = null;
        try {
            objectOut = new ObjectOutputStream(out);
            objectOut.writeObject(object);
        } catch (IOException e) {
            e.printStackTrace();
            fail("IO Exception");
        } finally {
            if (objectOut != null) {
                try {
                    objectOut.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }


    /**
     * Deserializes and returns an object that is contained in serialized form
     * in the given input stream.
     *
     * @param in   the stream to read from
     * @return the deserialized object
     */
    private static Object deserialize(InputStream in) {
        ObjectInputStream objectIn = null;
        try {
            objectIn = new ObjectInputStream(in);
            return objectIn.readObject();
        } catch (IOException e) {
            e.printStackTrace();
            fail("IO Exception");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            fail("Class not found");
        } finally {
            if (objectIn != null) {
                try {
                    objectIn.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return null;
    }


}
