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

/* @test
 * @bug 4140729
 * @summary Verify that writeReplace & readResolve are called by serialization.
 *          readResolve is used to maintain the invariant that the enums
 *          of TypeSafeEnum are singletons.
 */
import java.io.*;

public class TypeSafeEnum implements Serializable, ObjectInputValidation {
    private static int numWriteObject = 0;
    private static int numReadObject = 0;


    private String value;
    private TypeSafeEnum(String value) {
        this.value = value;
    }

    final public static TypeSafeEnum FIRST = new TypeSafeEnum("First");
    final public static TypeSafeEnum SECOND = new TypeSafeEnum("Second");
    final public static TypeSafeEnum THIRD = new TypeSafeEnum("Third");
    static int numReadResolve = 0;
    static int numWriteReplace = 0;
    static boolean verbose = false;


    private Object writeReplace() throws IOException {
        numWriteReplace++;
        if (verbose) {
            System.out.println("TypeSafeEnum.writeReplace() " +
                               this.toString());
        }
        return this;
    }

    private Object readResolve() throws IOException {
        numReadResolve++;
        if (verbose) {
            System.out.println("readResolve called on " + this.toString());
        }
        if (value.equals(FIRST.value)) {
            return FIRST;
        } else if (value.equals(SECOND.value)) {
            return SECOND;
        } else if (value.equals(THIRD.value)) {
            return THIRD;
        } else {
            //unknown type safe enum
            return this;
        }
    }

    private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        numReadObject++;
        in.defaultReadObject();
        if (verbose) {
            System.out.println("TypeSafeEnum.readObject() " + this.toString());
        }
        if (value == null) {
            in.registerValidation(this, 0);
        }
    }

    public void validateObject() throws InvalidObjectException {
        // only top level case has null for value, validate.
        if (numWriteObject != 4) {
            throw new Error("Expected 4 calls to writeObject, only " +
                            numWriteObject + " made");
        }
        if (numReadObject != 4) {
            throw new Error("Expected 4 calls to readObject, only " +
                            numReadObject + " made");
        }
        if (numWriteReplace != 4) {
            throw new Error("Expected 4 calls to writeReplace, only " +
                            numWriteReplace + " made");
        }
        if (numReadResolve != 4) {
            throw new Error("Expected 4 calls to readResolve, only " +
                            numReadResolve + " made");
        }
    }

    private void writeObject(ObjectOutputStream out) throws IOException
    {
        numWriteObject++;
        out.defaultWriteObject();
        if (verbose) {
            System.out.println("TypeSafeEnum.writeObject() " +
                               this.toString());
        }
    }

    public String toString() {
        return super.toString() + " value=" + value;
    }

    public static void main(String args[])
        throws IOException, ClassNotFoundException
    {
        if (args.length > 0 && args[0].equals("-verbose"))
            verbose = true;

        TypeSafeEnum[] writeArray = new TypeSafeEnum[7];
        writeArray[0] = FIRST;
        writeArray[1] = SECOND;
        writeArray[2] = THIRD;
        writeArray[3] = FIRST;
        writeArray[4] = SECOND;
        writeArray[5] = THIRD;
        writeArray[6] = new TypeSafeEnum("Third");

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream os = new ObjectOutputStream(baos);
        os.writeObject(writeArray);
        os.close();

        TypeSafeEnum[] readArray;
        ObjectInputStream in =
           new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        readArray = (TypeSafeEnum[])in.readObject();
        in.close();

        for (int i= 0; i < writeArray.length - 1 ; i++) {
            if (writeArray[i] != readArray[i]) {
                throw new Error("Serializa/deserialize did not preserve " +
                                "singleton for " +
                                readArray[i].toString() +
                                " and " + writeArray[i].toString());
            }
        }
   }
};
