/*
 * 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 example shows how to evolve a persistent format using the
 * Externalizable interface. A class using the Externalizable interface
 * is responsible for saving its own state, for saving the state of its 
 * supertype, and for versioning including skipping over data. 
 * This example specifically deals with 
 * versioning - (To see how to save the state of the supertype, which can
 * Externalizable or not, see relevant examples)... 
 *
 * How to Run:
 * 
 * Compile Original Class: 
 *         javac ExternExampleOriginalClass.java
 * Run Original Class with serialization flag: 
 *         java ExternExampleOriginalClass -s
 * Compile Evolved Class:
 *         javac ExternExampleEvolvedClass.java
 * Run Evolved Class with deserialization flag:
 *         java ExternExampleEvolvedClass -d
 *
 * This tests compatibility in one direction. Do the same in other
 * direction to see  bidirectional compatibility.
 *
 * Compiled and Tested with JDK1.1.4 & JDK1.2
 */

public class ExternExampleOriginalClass {

    /** 
     *  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(2, "oldclass");
	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.close();
	    } catch (Exception e) {
		e.printStackTrace();
		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) {
		e.printStackTrace();
		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 original ExternVersioningClass
 */
class ExternVersioningClass implements Externalizable  {

  // some data that we will explicitly save
  int dimension;
  int array[];
  String name;

  public static final int version = 1;
 
  // ***need to have a public-no-arg constructor*** with Exernalizable
  public ExternVersioningClass() {}

  ExternVersioningClass(int dim, String n) {
    // initialize 
    dimension = dim;
    array = new int[dimension];
    name = n;
  }

    /** 
     * Mandatory writeExternal method. It writes out the class version
     * number and then writes out the fields.
     * 
     * @serialData Writes out an int "class version number". Next, the
     *             dimension field is written as an int. Followed by
     *             the array field written as an object. Finally, the
     *             name field is written as an object.
     */
    public void writeExternal(ObjectOutput out) throws IOException {
	
	// we first write out the class version number (ie. # 1)
	out.writeInt(version);
	
	// now we write out the data
	out.writeInt(dimension);
	out.writeObject(array);
	out.writeObject(name);
    }

    /** 
     * Mandatory readExternal method. Just reads in the fields for this
     * class. Assumption is that later version of the class make only
     * compatible changes and that their additional data is appended.
     *
     * @serialData Read a class version number as an int, the dimension field 
     *             as an int, array field as an Object and name field as an 
     *             object.
     */
    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();
    }
}











