/*
 * Copyright 2002 Sun Microsystems, Inc. All  Rights Reserved.
 *  
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met:
 * 
 * -Redistributions of source code must retain the above copyright  
 *  notice, this list of conditions and the following disclaimer.
 * 
 * -Redistribution 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.
 *  
 * Neither the name of Sun Microsystems, Inc. or the names of 
 * contributors may be used to endorse or promote products derived 
 * from this software without specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any 
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 
 * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY 
 * DAMAGES OR LIABILITIES  SUFFERED BY LICENSEE AS A RESULT OF OR 
 * RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR 
 * ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE 
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, 
 * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER 
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF 
 * THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *  
 * You acknowledge that Software is not designed, licensed or 
 * intended for use in the design, construction, operation or 
 * maintenance of any nuclear facility. 
 */

import java.io.*;
import java.util.*;

/** 
 * This is a continuation of the Externalizable Persistence Example.
 * This file contains the evolved version of the ExternVersioningClass.
 * Of course, normally, the evolved class would actually take place of the
 * original class but we just keep both of them to demonstrate the evolution
 *
 * For information on how to run, see file ExternExampleOriginalClass.java
 */
public class ExternExampleEvolvedClass {
 
    /** 
     *  There are two options: either a user can serialize an object or
     *  deserialize it. (using the -s or -d flag). These options allow
     *  for the demonstration of bidirection readability and writeability
     *  between the original and the evolved class. In other words,
     *  one can serialize an object here and deserialize it with the evolved
     *  class or vice versa.
     */
    public static void main(String args[]) {  
	
	boolean serialize = false;
	boolean deserialize = false;
	
	ExternVersioningClass wobj = new ExternVersioningClass
	    (1, "evolvedclass", true, 1.23);
	ExternVersioningClass robj = null;
	/* 
	 * see if we are serializing or deserializing.
	 * The ability to deserialize or serialize allows
	 * us to see the bidirectional readability and writeability
	 */
	if (args.length == 1) {
	    if (args[0].equals("-d")) {
		deserialize = true;
		} else if (args[0].equals("-s")) {
		    serialize = true;
		} else {
		    usage();
		    System.exit(0);
		}
	} else {
	    usage();
	    System.exit(0);
	}
	
	/* 
	 * serialize, if that's the chosen option
	 */
	if (serialize) {
	    try {
		FileOutputStream fo = new FileOutputStream("evolve.tmp");
		ObjectOutputStream so = new ObjectOutputStream(fo);
		so.writeObject(wobj);
		so.flush();
	    } catch (Exception e) {
		System.out.println(e);
		System.exit(1);
	    }
	}
	
	/* 
	 * deserialize, if that's the chosen option
	 */
	if (deserialize) {
	    try {
		FileInputStream fi = new FileInputStream("evolve.tmp");
		ObjectInputStream si = new ObjectInputStream(fi);
		robj = (ExternVersioningClass) si.readObject();
	    } catch (Exception e) {
		System.out.println(e);
		System.exit(1);
	    }
	}
    }

    /** 
     * Prints out the usage
     */
    static void usage() {
	System.out.println("Usage:");
	System.out.println("      -s (in order to serialize)");
	System.out.println("      -d (in order to deserialize)");
    }
}
	
   
/**
 * The evolved class!
 */
class ExternVersioningClass implements Externalizable  {

    /* 
     * Each versioned class must identify the original class version from which
     * it evolved. This SUID is obtained using the command serialver on the 
     * original class
     */
    static final long serialVersionUID = -6527577423406625824L;
    public static final int version = 2;
    
    // the data that was part of the original class
    int dimension;
    int array[];
    String name;
    
    // new data 
    boolean b;
    double d;
        
    // ***need to have a public-no-arg constructor***
    public ExternVersioningClass() {}
        
    ExternVersioningClass(int dim, String n, boolean boo, double dou){
	// initialize 
	dimension = dim;
	array = new int[dimension];
	name = n;
	b = boo;
	d = dou;
    }

    /** 
     * the mandatory writeExternal method. First writes out the version number
     * (ie. 2 because this is the evolved class.) Then writes out the fields
     * that were a part of the original class and then the fields that are
     * in this class. (since the original class assumes that the new data
     * fields are appended)
     *
     * @serialData Writes out the int "class version number" 2. Next, the
     *             dimension field is written as an int. Followed by
     *             the array field written as an object. The
     *             name field is written as an object. The new b field is
     *             written as a boolean. Lastly, the new d field is written
     *             as a double.
     */
    public void writeExternal(ObjectOutput out) throws IOException {
	
	// we first write out the class version number (ie. # 2)
	out.writeInt(version);
	
	// We have to first write out the data of the original class because it
	// it assumes that we appended the new data.
	out.writeInt(dimension);
	out.writeObject(array);
	out.writeObject(name);
	
	// the original class will ignore this data.
	out.writeBoolean(b);
	out.writeDouble(d);
    }

    /** 
     * mandatory readExternal method. 
     *
     * @serialData First reads in the version number and
     *             then if the version number suggests that the object
     *             was written as an original class, gives the new fields
     *             default values but if the object was written by this 
     *             evolved class, then reads in all the fields accordingly.
     */
    public void readExternal(ObjectInput in) 
	throws IOException, java.lang.ClassNotFoundException 
    {
	int version = in.readInt();
	System.out.println
	    ("Reading an Object written by Version #: " + version);
	
	dimension = in.readInt();
	
	// need to allocate memory for the array we will read in
	array = (int[]) in.readObject();
	name = (String) in.readObject();
	
	
	/*
	 * only if the object was written out by the evolved class, do we 
	 * read in the rest of the data.
	 */
	if (version == 2) {
	    b = in.readBoolean();
	    d = in.readDouble();
	}
	
	// otherwise, we give it default values
	else if(version == 1) {
	    b = true;
	    d = 1.00;
	}
    }
}
