/*
 * @(#)IIOPInputStream.java	1.75 05/11/17
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Licensed Materials - Property of IBM
 * RMI-IIOP v1.0
 * Copyright IBM Corp. 1998 1999  All Rights Reserved
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

package com.sun.corba.se.impl.io;

import java.io.InputStream;
import java.io.IOException;
import java.io.StreamCorruptedException;
import java.io.ObjectInputValidation;
import java.io.NotActiveException;
import java.io.InvalidObjectException;
import java.io.InvalidClassException;
import java.io.DataInputStream;
import java.io.OptionalDataException;
import java.io.WriteAbortedException;
import java.io.Externalizable;
import java.io.EOFException;
import java.lang.reflect.*;
import java.util.Vector;
import java.util.Stack;
import java.util.Hashtable;
import java.util.Enumeration;

import sun.corba.Bridge ;

import java.security.AccessController ;
import java.security.PrivilegedAction ;

import com.sun.corba.se.impl.io.ObjectStreamClass;
import com.sun.corba.se.impl.util.Utility;

import org.omg.CORBA.portable.ValueInputStream;

import org.omg.CORBA.ValueMember;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.ORB; 
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.portable.IndirectionException;
import org.omg.CORBA.MARSHAL;
import org.omg.CORBA.TypeCode;

import com.sun.org.omg.CORBA.ValueDefPackage.FullValueDescription;
import com.sun.org.omg.SendingContext.CodeBase;  

import javax.rmi.PortableRemoteObject;
import javax.rmi.CORBA.Util;
import javax.rmi.CORBA.ValueHandler;

import java.security.*;
import java.util.*;

import com.sun.corba.se.impl.orbutil.ObjectUtility ;
import com.sun.corba.se.impl.logging.OMGSystemException ;
import com.sun.corba.se.impl.logging.UtilSystemException ;

import com.sun.corba.se.spi.logging.CORBALogDomains ;

/**
 * IIOPInputStream is used by the ValueHandlerImpl to handle Java serialization
 * input semantics.
 *
 * @author  Stephen Lewallen
 * @since   JDK1.1.6
 */

public class IIOPInputStream
    extends com.sun.corba.se.impl.io.InputStreamHook
{
    private static Bridge bridge = 
	(Bridge)AccessController.doPrivileged(
	    new PrivilegedAction() {
		public Object run() {
		    return Bridge.get() ;
		}
	    } 
	) ;

    private static OMGSystemException omgWrapper = OMGSystemException.get(
	CORBALogDomains.RPC_ENCODING ) ;
    private static UtilSystemException utilWrapper = UtilSystemException.get(
	CORBALogDomains.RPC_ENCODING ) ;

    // Necessary to pass the appropriate fields into the
    // defaultReadObjectDelegate method (which takes no
    // parameters since it's called from 
    // java.io.ObjectInpuStream defaultReadObject()
    // which we can't change).
    //
    // This is only used in the case where the fields had 
    // to be obtained remotely because of a serializable
    // version difference.  Set in inputObjectUsingFVD.
    // Part of serialization evolution fixes for Ladybird,
    // bug 4365188.
    private ValueMember defaultReadObjectFVDMembers[] = null;

    private org.omg.CORBA_2_3.portable.InputStream orbStream;

    private CodeBase cbSender;  

    private ValueHandlerImpl vhandler;  //d4365188

    private Object currentObject = null;

    private ObjectStreamClass currentClassDesc = null;

    private Class currentClass = null;

    private int recursionDepth = 0;

    private int simpleReadDepth = 0;

    // The ActiveRecursionManager replaces the old RecursionManager which
    // used to record how many recursions were made, and resolve them after
    // an object was completely deserialized.
    //
    // That created problems (as in bug 4414154) because when custom
    // unmarshaling in readObject, there can be recursive references
    // to one of the objects currently being unmarshaled, and the
    // passive recursion system failed.
    ActiveRecursionManager activeRecursionMgr = new ActiveRecursionManager();

    private IOException abortIOException = null;

    /* Remember the first exception that stopped this stream. */
    private ClassNotFoundException abortClassNotFoundException = null;

    /* Vector of validation callback objects
     * The vector is created as needed. The vector is maintained in
     * order of highest (first) priority to lowest
     */
    private Vector callbacks;

    // Serialization machinery fields
    /* Arrays used to keep track of classes and ObjectStreamClasses
     * as they are being merged; used in inputObject.
     * spClass is the stack pointer for both.  */
    ObjectStreamClass[] classdesc;
    Class[] classes;
    int spClass;

    private static final String kEmptyStr = "";

    // TCKind TypeCodes used in FVD inputClassFields
    //public static final TypeCode kRemoteTypeCode = new TypeCodeImpl(TCKind._tk_objref);
    //public static final TypeCode kValueTypeCode =  new TypeCodeImpl(TCKind._tk_value);
    // removed TypeCodeImpl dependency
    public static final TypeCode kRemoteTypeCode = ORB.init().get_primitive_tc(TCKind.tk_objref);
    public static final TypeCode kValueTypeCode =  ORB.init().get_primitive_tc(TCKind.tk_value);

    // TESTING CODE - useFVDOnly should be made final before FCS in order to
    // optimize out the check.
    private static final boolean useFVDOnly = false;

    private byte streamFormatVersion;

    // Since java.io.OptionalDataException's constructors are
    // package private, but we need to throw it in some special
    // cases, we try to do it by reflection.
    private static final Constructor OPT_DATA_EXCEPTION_CTOR;
   
    private Object[] readObjectArgList = { this } ;

    static {
        OPT_DATA_EXCEPTION_CTOR = getOptDataExceptionCtor();
    }

    // Grab the OptionalDataException boolean ctor and make
    // it accessible.  Note that any exceptions 
    // will be wrapped in ExceptionInInitializerErrors.
    private static Constructor getOptDataExceptionCtor() {

        try {

            Constructor result =
                
                (Constructor) AccessController.doPrivileged(
                                    new PrivilegedExceptionAction() {
                    public java.lang.Object run() 
                        throws NoSuchMethodException,
                        SecurityException {
                        
                        Constructor boolCtor 
                            = OptionalDataException.class.getDeclaredConstructor(
                                                               new Class[] {
                                Boolean.TYPE });
                        
                        boolCtor.setAccessible(true);
                        
                        return boolCtor;
                    }});
            
            if (result == null)
		// XXX I18N, logging needed.
                throw new Error("Unable to find OptionalDataException constructor");
        
            return result;

        } catch (Exception ex) {
	    // XXX I18N, logging needed.
            throw new ExceptionInInitializerError(ex);
        }
    }

    // Create a new OptionalDataException with the EOF marker
    // set to true.  See handleOptionalDataMarshalException.
    private OptionalDataException createOptionalDataException() {
        try {
            OptionalDataException result
                = (OptionalDataException)
                   OPT_DATA_EXCEPTION_CTOR.newInstance(new Object[] { 
                       Boolean.TRUE });

            if (result == null)
		// XXX I18N, logging needed.
                throw new Error("Created null OptionalDataException");

            return result;

        } catch (Exception ex) {
	    // XXX I18N, logging needed.
            throw new Error("Couldn't create OptionalDataException", ex);
        }
    }

    // Return the stream format version currently being used
    // to deserialize an object
    protected byte getStreamFormatVersion() {
        return streamFormatVersion;
    }

    // At the beginning of data sent by a writeObject or
    // writeExternal method there is a byte telling the
    // reader the stream format version.
    private void readFormatVersion() throws IOException {

        streamFormatVersion = orbStream.read_octet();

        if (streamFormatVersion < 1 || 
            streamFormatVersion > vhandler.getMaximumStreamFormatVersion()) {
	    SystemException sysex = omgWrapper.unsupportedFormatVersion(
		    CompletionStatus.COMPLETED_MAYBE);
	    // XXX I18N?  Logging for IOException?
	    IOException result = new IOException("Unsupported format version: "
                                                 + streamFormatVersion);
	    result.initCause( sysex ) ;
	    throw result ;
        }

        if (streamFormatVersion == 2) {
            if (!(orbStream instanceof ValueInputStream)) {
		SystemException sysex = omgWrapper.notAValueinputstream( 
		    CompletionStatus.COMPLETED_MAYBE);
		// XXX I18N?  Logging for IOException?
		IOException result = new IOException("Not a ValueInputStream");
		result.initCause( sysex ) ;
		throw result;
            }
        }
    }

    public static void setTestFVDFlag(boolean val){
	//  useFVDOnly = val;
    }

    /**
     * Dummy constructor; passes upper stream a dummy stream;
     **/
    public IIOPInputStream()
    	throws java.io.IOException {
    	super();
	resetStream();
    }
	
    public final void setOrbStream(org.omg.CORBA_2_3.portable.InputStream os) {
    	orbStream = os;
    }

    public final org.omg.CORBA_2_3.portable.InputStream getOrbStream() {
    	return orbStream;
    }

    //added setSender and getSender
    public final void setSender(CodeBase cb) {
        cbSender = cb;
    }

    public final CodeBase getSender() {
        return cbSender;
    }

    // 4365188 this is added to enable backward compatability w/ wrong
    // rep-ids
    public final void setValueHandler(ValueHandler vh) {
        vhandler = (com.sun.corba.se.impl.io.ValueHandlerImpl) vh;
    }

    public final ValueHandler getValueHandler() {
	return (javax.rmi.CORBA.ValueHandler) vhandler;
    }
	
    public final void increaseRecursionDepth(){
	recursionDepth++;
    }

    public final int decreaseRecursionDepth(){
	return --recursionDepth;
    }

    /**
     * Override the actions of the final method "readObject()"
     * in ObjectInputStream.
     * @since     JDK1.1.6
     *
     * Read an object from the ObjectInputStream.
     * The class of the object, the signature of the class, and the values
     * of the non-transient and non-static fields of the class and all
     * of its supertypes are read.  Default deserializing for a class can be
     * overriden using the writeObject and readObject methods.
     * Objects referenced by this object are read transitively so
     * that a complete equivalent graph of objects is reconstructed by readObject. <p>
     *
     * The root object is completly restored when all of its fields
     * and the objects it references are completely restored.  At this
     * point the object validation callbacks are executed in order
     * based on their registered priorities. The callbacks are
     * registered by objects (in the readObject special methods)
     * as they are individually restored.
     *
     * Exceptions are thrown for problems with the InputStream and for classes
     * that should not be deserialized.  All exceptions are fatal to the
     * InputStream and leave it in an indeterminate state; it is up to the caller
     * to ignore or recover the stream state.
     * @exception java.lang.ClassNotFoundException Class of a serialized object
     *      cannot be found.
     * @exception InvalidClassException Something is wrong with a class used by
     *     serialization.
     * @exception StreamCorruptedException Control information in the
     *     stream is inconsistent.
     * @exception OptionalDataException Primitive data was found in the
     * stream instead of objects.
     * @exception IOException Any of the usual Input/Output related exceptions.
     * @since     JDK1.1
     */
    public final Object readObjectDelegate() throws IOException
    {
	try {

            readObjectState.readData(this);

            return orbStream.read_abstract_interface();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, true);
            throw marshalException;
	} catch(IndirectionException cdrie)
	    {
                // The CDR stream had never seen the given offset before,
                // so check the recursion manager (it will throw an
                // IOException if it doesn't have a reference, either).
                return activeRecursionMgr.getObject(cdrie.offset);
	    }
    }

    final Object simpleReadObject(Class clz,
                                  String repositoryID,
                                  com.sun.org.omg.SendingContext.CodeBase sender,
                                  int offset)
					 /* throws OptionalDataException, ClassNotFoundException, IOException */
    {

    	/* Save the current state and get ready to read an object. */
    	Object prevObject = currentObject;
    	ObjectStreamClass prevClassDesc = currentClassDesc;
        Class prevClass = currentClass;
        byte oldStreamFormatVersion = streamFormatVersion;

    	simpleReadDepth++;	// Entering
    	Object obj = null;

    	/*
    	 * Check for reset, handle it before reading an object.
    	 */
    	try {
	    // d4365188: backward compatability
	    if (vhandler.useFullValueDescription(clz, repositoryID)) {
		obj = inputObjectUsingFVD(clz, repositoryID, sender, offset);
	    } else {
                obj = inputObject(clz, repositoryID, sender, offset);
	    }

	    obj = currentClassDesc.readResolve(obj);
    	}
    	catch(ClassNotFoundException cnfe)
	    {
		bridge.throwException( cnfe ) ;
		return null;
	    }
    	catch(IOException ioe)
	    {
		// System.out.println("CLZ = " + clz + "; " + ioe.toString());
		bridge.throwException(ioe) ;
		return null;
	    }
    	finally {
    	    simpleReadDepth --;
    	    currentObject = prevObject;
    	    currentClassDesc = prevClassDesc;
            currentClass = prevClass;
            streamFormatVersion = oldStreamFormatVersion;
    	}


    	/* Check for thrown exceptions and re-throw them, clearing them if
    	 * this is the last recursive call .
    	 */
    	IOException exIOE = abortIOException;
    	if (simpleReadDepth == 0)
    	    abortIOException = null;
    	if (exIOE != null){
	    bridge.throwException( exIOE ) ;
            return null;
    	}


    	ClassNotFoundException exCNF = abortClassNotFoundException;
    	if (simpleReadDepth == 0)
    	    abortClassNotFoundException = null;
    	if (exCNF != null) {
	    bridge.throwException( exCNF ) ;
            return null;
    	}

    	return obj;
    }

    public final void simpleSkipObject(String repositoryID,
				       com.sun.org.omg.SendingContext.CodeBase sender)
				       /* throws OptionalDataException, ClassNotFoundException, IOException */
    {
				
    	/* Save the current state and get ready to read an object. */
    	Object prevObject = currentObject;
    	ObjectStreamClass prevClassDesc = currentClassDesc;
        Class prevClass = currentClass;
        byte oldStreamFormatVersion = streamFormatVersion;

    	simpleReadDepth++;	// Entering
    	Object obj = null;

    	/*
    	 * Check for reset, handle it before reading an object.
    	 */
    	try {
	    skipObjectUsingFVD(repositoryID, sender);
    	}
    	catch(ClassNotFoundException cnfe)
	    {
		bridge.throwException( cnfe ) ;
		return;
	    }
    	catch(IOException ioe)
	    {
		bridge.throwException( ioe ) ;
		return;
	    }
    	finally {
    	    simpleReadDepth --;
            streamFormatVersion = oldStreamFormatVersion;
    	    currentObject = prevObject;
    	    currentClassDesc = prevClassDesc;
            currentClass = prevClass;
    	}


    	/* Check for thrown exceptions and re-throw them, clearing them if
    	 * this is the last recursive call .
    	 */
    	IOException exIOE = abortIOException;
    	if (simpleReadDepth == 0)
    	    abortIOException = null;
    	if (exIOE != null){
	    bridge.throwException( exIOE ) ;
            return;
    	}


    	ClassNotFoundException exCNF = abortClassNotFoundException;
    	if (simpleReadDepth == 0)
    	    abortClassNotFoundException = null;
    	if (exCNF != null) {
	    bridge.throwException( exCNF ) ;
            return;
    	}

	return;
    }
    /////////////////

    /**
     * This method is called by trusted subclasses of ObjectOutputStream
     * that constructed ObjectOutputStream using the
     * protected no-arg constructor. The subclass is expected to provide
     * an override method with the modifier "final".
     *
     * @return the Object read from the stream.
     *
     * @see #ObjectInputStream()
     * @see #readObject
     * @since JDK 1.2
     */
    protected final Object readObjectOverride()
 	throws OptionalDataException, ClassNotFoundException, IOException
    {
        return readObjectDelegate();
    }

    /**
     * Override the actions of the final method "defaultReadObject()"
     * in ObjectInputStream.
     * @since     JDK1.1.6
     *
     * Read the non-static and non-transient fields of the current class
     * from this stream.  This may only be called from the readObject method
     * of the class being deserialized. It will throw the NotActiveException
     * if it is called otherwise.
     *
     * @exception java.lang.ClassNotFoundException if the class of a serialized
     *              object could not be found.
     * @exception IOException        if an I/O error occurs.
     * @exception NotActiveException if the stream is not currently reading
     *              objects.
     * @since     JDK1.1
     */
    public final void defaultReadObjectDelegate()
    /* throws IOException, ClassNotFoundException, NotActiveException */
    {
        try {
	    if (currentObject == null || currentClassDesc == null)
		// XXX I18N, logging needed.
		throw new NotActiveException("defaultReadObjectDelegate");

            // The array will be null unless fields were retrieved
            // remotely because of a serializable version difference.
            // Bug fix for 4365188.  See the definition of
            // defaultReadObjectFVDMembers for more information.
            if (defaultReadObjectFVDMembers != null &&
                defaultReadObjectFVDMembers.length > 0) {

                // WARNING:  Be very careful!  What if some of
                // these fields actually have to do this, too?
                // This works because the defaultReadObjectFVDMembers
                // reference is passed to inputClassFields, but
                // there is no guarantee that
                // defaultReadObjectFVDMembers will point to the
                // same array after calling inputClassFields.

                // Use the remote fields to unmarshal.
                inputClassFields(currentObject, 
                                 currentClass, 
                                 currentClassDesc,
                                 defaultReadObjectFVDMembers,
                                 cbSender);

            } else {

                // Use the local fields to unmarshal.
                ObjectStreamField[] fields =
                    currentClassDesc.getFieldsNoCopy();
                if (fields.length > 0) {
                    inputClassFields(currentObject, currentClass, fields, cbSender); 
                }
            }
        }
        catch(NotActiveException nae)
	    {
		bridge.throwException( nae ) ;
	    }
        catch(IOException ioe)
	    {
		bridge.throwException( ioe ) ;
	    }
        catch(ClassNotFoundException cnfe)
	    {
		bridge.throwException( cnfe ) ;
	    }

    }

    /**
     * Override the actions of the final method "enableResolveObject()"
     * in ObjectInputStream.
     * @since     JDK1.1.6
     *
     * Enable the stream to allow objects read from the stream to be replaced.
     * If the stream is a trusted class it is allowed to enable replacment.
     * Trusted classes are those classes with a classLoader equals null. <p>
     *
     * When enabled the resolveObject method is called for every object
     * being deserialized.
     *
     * @exception SecurityException The classloader of this stream object is non-null.
     * @since     JDK1.1
     */
    public final boolean enableResolveObjectDelegate(boolean enable)
    /* throws SecurityException */
    {
	return false;
    }

    // The following three methods allow the implementing orbStream
    // to provide mark/reset behavior as defined in java.io.InputStream.

    public final void mark(int readAheadLimit) {
        orbStream.mark(readAheadLimit);
    }
    
    public final boolean markSupported() {
        return orbStream.markSupported();
    }
    
    public final void reset() throws IOException {
        try {
            orbStream.reset();
        } catch (Error e) {
            IOException err = new IOException(e.getMessage());
	    err.initCause(e) ;
	    throw err ;
        }
    }

    public final int available() throws IOException{
        return 0; // unreliable
    }

    public final void close() throws IOException{
        // no op
    }

    public final int read() throws IOException{
        try{
            readObjectState.readData(this);

            return (orbStream.read_octet() << 0) & 0x000000FF;
        } catch (MARSHAL marshalException) {
            if (marshalException.minor 
                == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {
                setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);
                return -1;
            }

            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e) ;
	    throw exc ;
	}
    }

    public final int read(byte data[], int offset, int length) throws IOException{
        try{
            readObjectState.readData(this);

            orbStream.read_octet_array(data, offset, length);
            return length;
        } catch (MARSHAL marshalException) {
            if (marshalException.minor 
                == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {
                setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);
                return -1;
            }

            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e) ;
	    throw exc ;
	}

    }

    public final boolean readBoolean() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_boolean();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;

        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final byte readByte() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_octet();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;

        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final char readChar() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_wchar();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;

        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final double readDouble() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_double();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final float readFloat() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_float();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final void readFully(byte data[]) throws IOException{
// d11623 : implement readFully, required for serializing some core classes

        readFully(data, 0, data.length);
    }

    public final void readFully(byte data[],  int offset,  int size) throws IOException{
// d11623 : implement readFully, required for serializing some core classes
        try{
            readObjectState.readData(this);

            orbStream.read_octet_array(data, offset, size);
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final int readInt() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_long();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final String readLine() throws IOException{
	// XXX I18N, logging needed.
        throw new IOException("Method readLine not supported");
    }

    public final long readLong() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_longlong();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final short readShort() throws IOException{
        try{
            readObjectState.readData(this);

            return orbStream.read_short();
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    protected final void readStreamHeader() throws IOException, StreamCorruptedException{
        // no op
    }

    public final int readUnsignedByte() throws IOException{
        try{
            readObjectState.readData(this);

    	    return (orbStream.read_octet() << 0) & 0x000000FF;
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
	} catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    public final int readUnsignedShort() throws IOException{
        try{
            readObjectState.readData(this);

    	    return (orbStream.read_ushort() << 0) & 0x0000FFFF;
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    /**
     * Helper method for correcting the Kestrel bug 4367783 (dealing
     * with larger than 8-bit chars).  The old behavior is preserved
     * in orbutil.IIOPInputStream_1_3 in order to interoperate with
     * our legacy ORBs.
     */
    protected String internalReadUTF(org.omg.CORBA.portable.InputStream stream)
    {
        return stream.read_wstring();
    }

    public final String readUTF() throws IOException{
        try{
            readObjectState.readData(this);

            return internalReadUTF(orbStream);
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            throw marshalException;
        } catch(Error e) {
	    IOException exc = new IOException(e.getMessage());
	    exc.initCause(e);
	    throw exc ;
	}
    }

    // If the ORB stream detects an incompatibility between what's
    // on the wire and what our Serializable's readObject wants,
    // it throws a MARSHAL exception with a specific minor code.
    // This is rethrown to the readObject as an OptionalDataException.
    // So far in RMI-IIOP, this process isn't specific enough to
    // tell the readObject how much data is available, so we always
    // set the OptionalDataException's EOF marker to true.
    private void handleOptionalDataMarshalException(MARSHAL marshalException,
                                                    boolean objectRead)
        throws IOException {

        // Java Object Serialization spec 3.4: "If the readObject method
        // of the class attempts to read more data than is present in the
        // optional part of the stream for this class, the stream will
        // return -1 for bytewise reads, throw an EOFException for
        // primitive data reads, or throw an OptionalDataException
        // with the eof field set to true for object reads."
        if (marshalException.minor 
            == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {

            IOException result;

            if (!objectRead)
                result = new EOFException("No more optional data");
            else
                result = createOptionalDataException();

            result.initCause(marshalException);

            setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);

            throw result;
        }
    }

    public final synchronized void registerValidation(ObjectInputValidation obj,
						      int prio)
	throws NotActiveException, InvalidObjectException{
	// XXX I18N, logging needed.
        throw new Error("Method registerValidation not supported");
    }

    protected final Class resolveClass(ObjectStreamClass v)
	throws IOException, ClassNotFoundException{
	// XXX I18N, logging needed.
        throw new IOException("Method resolveClass not supported");
    }

    protected final Object resolveObject(Object obj) throws IOException{
	// XXX I18N, logging needed.
        throw new IOException("Method resolveObject not supported");
    }

    public final int skipBytes(int len) throws IOException{
        try{
            readObjectState.readData(this);

            byte buf[] = new byte[len];
            orbStream.read_octet_array(buf, 0, len);
            return len;
        } catch (MARSHAL marshalException) {
            handleOptionalDataMarshalException(marshalException, false);
            
            throw marshalException;
        } catch(Error e) {
            IOException exc = new IOException(e.getMessage());
	    exc.initCause(e) ;
	    throw exc ;
        }
    }

    private Object inputObject(Class clz,
			       String repositoryID,
			       com.sun.org.omg.SendingContext.CodeBase sender,
                               int offset)
	throws IOException, ClassNotFoundException
    {

    	/*
    	 * Get the descriptor and then class of the incoming object.
    	 */

    	currentClassDesc = ObjectStreamClass.lookup(clz);
    	currentClass = currentClassDesc.forClass();
    	//currentClassDesc.setClass(currentClass);
    	if (currentClass == null)
	    // XXX I18N, logging needed.
    	    throw new ClassNotFoundException(currentClassDesc.getName());

        try {
            /* If Externalizable,
             *  Create an instance and tell it to read its data.
             * else,
             *  Handle it as a serializable class.
             */
            if (currentClassDesc.isExternalizable()) {
                try {
                    currentObject = (currentClass == null) ?
                        null : currentClassDesc.newInstance();
                    if (currentObject != null) {

                        // Store this object and its beginning position
                        // since there might be indirections to it while
                        // it's been unmarshalled.
                        activeRecursionMgr.addObject(offset, currentObject);

                        // Read format version
                        readFormatVersion();

                        Externalizable ext = (Externalizable)currentObject;
                        ext.readExternal(this);
		}
	    } catch (InvocationTargetException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "InvocationTargetException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
	    } catch (UnsupportedOperationException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "UnsupportedOperationException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
    	    } catch (InstantiationException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "InstantiationException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
    	    }
    	} // end : if (currentClassDesc.isExternalizable())
	else {
    	    /* Count number of classes and descriptors we might have
    	     * to work on.
    	     */

    	    ObjectStreamClass currdesc = currentClassDesc;
    	    Class currclass = currentClass;

    	    int spBase = spClass;	// current top of stack

    	    /* The object's classes should be processed from supertype to subtype
    	     * Push all the clases of the current object onto a stack.
    	     * Note that only the serializable classes are represented
    	     * in the descriptor list.
    	     *
    	     * Handle versioning where one or more supertypes of
    	     * have been inserted or removed.  The stack will
    	     * contain pairs of descriptors and the corresponding
    	     * class.  If the object has a class that did not occur in
    	     * the original the descriptor will be null.  If the
    	     * original object had a descriptor for a class not
    	     * present in the local hierarchy of the object the class will be
    	     * null.
    	     *
    	     */

    	    /*
    	     * This is your basic diff pattern, made simpler
    	     * because reordering is not allowed.
    	     */
            // sun.4296963 ibm.11861 
            // d11861 we should stop when we find the highest serializable class
            // We need this so that when we allocate the new object below, we
            // can call the constructor of the non-serializable superclass.
            // Note that in the JRMP variant of this code the
            // ObjectStreamClass.lookup() method handles this, but we've put
            // this fix here rather than change lookup because the new behaviour
            // is needed in other cases.

    	    for (currdesc = currentClassDesc, currclass = currentClass;
    		 currdesc != null && currdesc.isSerializable();   /*sun.4296963 ibm.11861*/
    		 currdesc = currdesc.getSuperclass()) {

    		/*
    		 * Search the classes to see if the class of this
    		 * descriptor appears further up the hierarchy. Until
    		 * it's found assume its an inserted class.  If it's
    		 * not found, its the descriptor's class that has been
    		 * removed.
    		 */
    		Class cc = currdesc.forClass();
    		Class cl;
    		for (cl = currclass; cl != null; cl = cl.getSuperclass()) {
    		    if (cc == cl) {
    			// found a superclass that matches this descriptor
    			break;
    		    } else {
    			/* Ignore a class that doesn't match.  No
    			 * action is needed since it is already
    			 * initialized.
    			 */
    		    }
		} // end : for (cl = currclass; cl != null; cl = cl.getSuperclass()) 
    		/* Test if there is room for this new entry.
    		 * If not, double the size of the arrays and copy the contents.
    		 */
    		spClass++;
    		if (spClass >= classes.length) {
    		    int newlen = classes.length * 2;
    		    Class[] newclasses = new Class[newlen];
    		    ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen];

    		    System.arraycopy(classes, 0,
				     newclasses, 0,
				     classes.length);
    		    System.arraycopy(classdesc, 0,
    				     newclassdesc, 0,
    				     classes.length);

    		    classes = newclasses;
    		    classdesc = newclassdesc;
    		}

    		if (cl == null) {
    		    /* Class not found corresponding to this descriptor.
    		     * Pop off all the extra classes pushed.
    		     * Push the descriptor and a null class.
    		     */
    		    classdesc[spClass] = currdesc;
    		    classes[spClass] = null;
    		} else {
		    /* Current class descriptor matches current class.
    		     * Some classes may have been inserted.
    		     * Record the match and advance the class, continue
    		     * with the next descriptor.
    		     */
    		    classdesc[spClass] = currdesc;
    		    classes[spClass] = cl;
    		    currclass = cl.getSuperclass();
    		}
    	    } // end : for (currdesc = currentClassDesc, currclass = currentClass;

    	    /* Allocate a new object.  The object is only constructed
    	     * above the highest serializable class and is set to
    	     * default values for all more specialized classes.
    	     */
    	    try {
    		currentObject = (currentClass == null) ?
		    null : currentClassDesc.newInstance() ;

                // Store this object and its beginning position
                // since there might be indirections to it while
                // it's been unmarshalled.
                activeRecursionMgr.addObject(offset, currentObject);
	    } catch (InvocationTargetException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "InvocationTargetException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
	    } catch (UnsupportedOperationException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "UnsupportedOperationException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
    	    } catch (InstantiationException e) {
		InvalidClassException exc = new InvalidClassException(
		    currentClass.getName(), 
		    "InstantiationException accessing no-arg constructor");
		exc.initCause( e ) ;
		throw exc ;
    	    }

    	    /*
    	     * For all the pushed descriptors and classes.
    	     * 	if the class has its own writeObject and readObject methods
    	     *	    call the readObject method
    	     *	else
    	     *	    invoke the defaultReadObject method
    	     */
    	    try {
    		for (spClass = spClass; spClass > spBase; spClass--) {
    		    /*
    		     * Set current descriptor and corresponding class
    		     */
    		    currentClassDesc = classdesc[spClass];
    		    currentClass = classes[spClass];
    		    if (classes[spClass] != null) {
    			/* Read the data from the stream described by the
    			 * descriptor and store into the matching class.
    			 */

                        ReadObjectState oldState = readObjectState;
                        setState(DEFAULT_STATE);

                        try {

                            // Changed since invokeObjectReader no longer does this.
                            if (currentClassDesc.hasWriteObject()) {

                                // Read format version
                                readFormatVersion();

                                // Read defaultWriteObject indicator
                                boolean calledDefaultWriteObject = readBoolean();

                                readObjectState.beginUnmarshalCustomValue(this,
                                                                          calledDefaultWriteObject,
                                                                          (currentClassDesc.readObjectMethod
                                                                           != null));
                            } else {
                                if (currentClassDesc.hasReadObject())
                                    setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED);
                            }

                            if (!invokeObjectReader(currentClassDesc, currentObject, currentClass) ||
                                readObjectState == IN_READ_OBJECT_DEFAULTS_SENT) {

                                // Error case of no readObject and didn't call
                                // defaultWriteObject handled in default state

				ObjectStreamField[] fields =
				    currentClassDesc.getFieldsNoCopy();
				if (fields.length > 0) {
				    inputClassFields(currentObject, currentClass, fields, sender);
				}
                            }

                            if (currentClassDesc.hasWriteObject())
                                readObjectState.endUnmarshalCustomValue(this);

                        } finally {
                            setState(oldState);
                        }

    		    } else {

			// _REVISIT_ : Can we ever get here?
			/* No local class for this descriptor,
			 * Skip over the data for this class.
			 * like defaultReadObject with a null currentObject.
			 * The code will read the values but discard them.
			 */
			    ObjectStreamField[] fields =
				currentClassDesc.getFieldsNoCopy();
			    if (fields.length > 0) {
				inputClassFields(null, currentClass, fields, sender);
			    }
						
			}
					
		}
    	    } finally {
				// Make sure we exit at the same stack level as when we started.
		spClass = spBase;
    	    }
    	}
        } finally {
            // We've completed deserializing this object.  Any
            // future indirections will be handled correctly at the
            // CDR level.  The ActiveRecursionManager only deals with
            // objects currently being deserialized.
            activeRecursionMgr.removeObject(offset);
        }
		
    	return currentObject;
    }

    // This retrieves a vector of FVD's for the hierarchy of serializable classes stemming from 
    // repositoryID.  It is assumed that the sender will not provide base_value id's for non-serializable
    // classes!
    private Vector getOrderedDescriptions(String repositoryID,
					  com.sun.org.omg.SendingContext.CodeBase sender) {
	Vector descs = new Vector();

        if (sender == null) {
            return descs;
        }
        
	FullValueDescription aFVD = sender.meta(repositoryID);
	while (aFVD != null) {
	    descs.insertElementAt(aFVD, 0);
	    if ((aFVD.base_value != null) && !kEmptyStr.equals(aFVD.base_value)) {
		aFVD = sender.meta(aFVD.base_value);
	    }
	    else return descs;
	}

	return descs;
    }

    /**
     * This input method uses FullValueDescriptions retrieved from the sender's runtime to 
     * read in the data.  This method is capable of throwing out data not applicable to client's fields.
     * This method handles instances where the reader has a class not sent by the sender, the sender sent
     * a class not present on the reader, and/or the reader's class does not match the sender's class.
     *
     * NOTE : If the local description indicates custom marshaling and the remote type's FVD also
     * indicates custom marsahling than the local type is used to read the data off the wire.  However,
     * if either says custom while the other does not, a MARSHAL error is thrown.  Externalizable is 
     * a form of custom marshaling.
     *
     */
    private Object inputObjectUsingFVD(Class clz,
				       String repositoryID,
				       com.sun.org.omg.SendingContext.CodeBase sender,
                                       int offset)
	throws IOException, ClassNotFoundException
    {
	int spBase = spClass;	// current top of stack
	try{
			
	    /*
	     * Get the descriptor and then class of the incoming object.
	     */
			
	    ObjectStreamClass currdesc = currentClassDesc = ObjectStreamClass.lookup(clz);
	    Class currclass = currentClass = clz;
			
	    /* If Externalizable,
	     *  Create an instance and tell it to read its data.
	     * else,
	     *  Handle it as a serializable class.
	     */
	    if (currentClassDesc.isExternalizable()) {
		try {
		    currentObject = (currentClass == null) ?
			null : currentClassDesc.newInstance();
		    if (currentObject != null) {
                        // Store this object and its beginning position
                        // since there might be indirections to it while
                        // it's been unmarshalled.
                        activeRecursionMgr.addObject(offset, currentObject);

			// Read format version
			readFormatVersion();
						
			Externalizable ext = (Externalizable)currentObject;
			ext.readExternal(this);
		    }
		} catch (InvocationTargetException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"InvocationTargetException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		} catch (UnsupportedOperationException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"UnsupportedOperationException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		} catch (InstantiationException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"InstantiationException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		}
	    } else {
		/*
		 * This is your basic diff pattern, made simpler
		 * because reordering is not allowed.
		 */
		for (currdesc = currentClassDesc, currclass = currentClass;
		     currdesc != null && currdesc.isSerializable();   /*sun.4296963 ibm.11861*/

		     currdesc = currdesc.getSuperclass()) {
					
		    /*
		     * Search the classes to see if the class of this
		     * descriptor appears further up the hierarchy. Until
		     * it's found assume its an inserted class.  If it's
		     * not found, its the descriptor's class that has been
		     * removed.
		     */
		    Class cc = currdesc.forClass();
		    Class cl;
		    for (cl = currclass; cl != null; cl = cl.getSuperclass()) {
			if (cc == cl) {
			    // found a superclass that matches this descriptor
			    break;
			} else {
			    /* Ignore a class that doesn't match.  No
			     * action is needed since it is already
			     * initialized.
			     */
			}
		    } // end : for (cl = currclass; cl != null; cl = cl.getSuperclass()) 
		    /* Test if there is room for this new entry.
		     * If not, double the size of the arrays and copy the contents.
		     */
		    spClass++;
		    if (spClass >= classes.length) {
			int newlen = classes.length * 2;
			Class[] newclasses = new Class[newlen];
			ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen];
				
			System.arraycopy(classes, 0,
					 newclasses, 0,
					 classes.length);
			System.arraycopy(classdesc, 0,
					 newclassdesc, 0,
					 classes.length);
						
			classes = newclasses;
			classdesc = newclassdesc;
    		    }

		    if (cl == null) {
			/* Class not found corresponding to this descriptor.
			 * Pop off all the extra classes pushed.
			 * Push the descriptor and a null class.
			 */
			classdesc[spClass] = currdesc;
			classes[spClass] = null;
		    } else {
			/* Current class descriptor matches current class.
			 * Some classes may have been inserted.
			 * Record the match and advance the class, continue
			 * with the next descriptor.
			 */
			classdesc[spClass] = currdesc;
			classes[spClass] = cl;
			currclass = cl.getSuperclass();
		    }
		} // end : for (currdesc = currentClassDesc, currclass = currentClass;
				
		/* Allocate a new object.  
		 */
		try {
		    currentObject = (currentClass == null) ?
			null : currentClassDesc.newInstance();

                    // Store this object and its beginning position
                    // since there might be indirections to it while
                    // it's been unmarshalled.
                    activeRecursionMgr.addObject(offset, currentObject);
		} catch (InvocationTargetException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"InvocationTargetException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		} catch (UnsupportedOperationException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"UnsupportedOperationException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		} catch (InstantiationException e) {
		    InvalidClassException exc = new InvalidClassException(
			currentClass.getName(), 
			"InstantiationException accessing no-arg constructor");
		    exc.initCause( e ) ;
		    throw exc ;
		}
				
		Enumeration fvdsList = getOrderedDescriptions(repositoryID, sender).elements();
				
		while((fvdsList.hasMoreElements()) && (spClass > spBase)) {
		    FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
	            // d4365188: backward compatability
		    String repIDForFVD = vhandler.getClassName(fvd.id);
		    String repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass));
					
		    while ((spClass > spBase) &&
			   (!repIDForFVD.equals(repIDForClass))) {
			int pos = findNextClass(repIDForFVD, classes, spClass, spBase);
			if (pos != -1) {
			    spClass = pos;
			    currclass = currentClass = classes[spClass];
			    repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass));
			}
			else { // Read and throw away one level of the fvdslist

                            // This seems to mean that the sender had a superclass that
                            // we don't have

			    if (fvd.is_custom) {

                                readFormatVersion();
                                boolean calledDefaultWriteObject = readBoolean();

                                if (calledDefaultWriteObject)
                                    inputClassFields(null, null, null, fvd.members, sender);

                                if (getStreamFormatVersion() == 2) {

                                    ((ValueInputStream)getOrbStream()).start_value();
                                    ((ValueInputStream)getOrbStream()).end_value();
                                }

                                // WARNING: If stream format version is 1 and there's
                                // optional data, we'll get some form of exception down
                                // the line or data corruption.

			    } else {

				inputClassFields(null, currentClass, null, fvd.members, sender);
			    }

			    if (fvdsList.hasMoreElements()){
				fvd = (FullValueDescription)fvdsList.nextElement();
				repIDForFVD = vhandler.getClassName(fvd.id);
			    }
			    else return currentObject;
			}
		    }

		    currdesc = currentClassDesc = ObjectStreamClass.lookup(currentClass);

		    if (!repIDForClass.equals("java.lang.Object")) {

                        // If the sender used custom marshaling, then it should have put
                        // the two bytes on the wire indicating stream format version
                        // and whether or not the writeObject method called 
                        // defaultWriteObject/writeFields.

                        ReadObjectState oldState = readObjectState;
                        setState(DEFAULT_STATE);

                        try {

                            if (fvd.is_custom) {

                                // Read format version
                                readFormatVersion();

                                // Read defaultWriteObject indicator
                                boolean calledDefaultWriteObject = readBoolean();

                                readObjectState.beginUnmarshalCustomValue(this,
                                                                          calledDefaultWriteObject,
                                                                          (currentClassDesc.readObjectMethod
                                                                           != null));
                            }

                            boolean usedReadObject = false;

                            // Always use readObject if it exists, and fall back to default
                            // unmarshaling if it doesn't.
                            try {

                                if (!fvd.is_custom && currentClassDesc.hasReadObject())
                                    setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED);

                                // See the definition of defaultReadObjectFVDMembers
                                // for more information.  This concerns making sure
                                // we use the remote FVD's members in defaultReadObject.
                                defaultReadObjectFVDMembers = fvd.members;
                                usedReadObject = invokeObjectReader(currentClassDesc,
                                                                    currentObject,
                                                                    currentClass);

                            } finally {
                                defaultReadObjectFVDMembers = null;
                            }

                            // Note that the !usedReadObject !calledDefaultWriteObject
                            // case is handled by the beginUnmarshalCustomValue method
                            // of the default state
                            if (!usedReadObject || readObjectState == IN_READ_OBJECT_DEFAULTS_SENT)
                                inputClassFields(currentObject, currentClass, currdesc, fvd.members, sender);

                            if (fvd.is_custom)
                                readObjectState.endUnmarshalCustomValue(this);

                        } finally {
                            setState(oldState);
                        }
                           
                        currclass = currentClass = classes[--spClass];

		    } else { 

			// The remaining hierarchy of the local class does not match the sender's FVD.
			// So, use remaining FVDs to read data off wire.  If any remaining FVDs indicate
			// custom marshaling, throw MARSHAL error.
			inputClassFields(null, currentClass, null, fvd.members, sender);
						
			while (fvdsList.hasMoreElements()){
			    fvd = (FullValueDescription)fvdsList.nextElement();

                            if (fvd.is_custom)
                                skipCustomUsingFVD(fvd.members, sender);
			    else
                                inputClassFields(null, currentClass, null, fvd.members, sender);
			}
						
		    }
					
		} // end : while(fvdsList.hasMoreElements()) 
		while (fvdsList.hasMoreElements()){

		    FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
		    if (fvd.is_custom)
                        skipCustomUsingFVD(fvd.members, sender);
		    else 
                        throwAwayData(fvd.members, sender);			
		}
	    }
			
	    return currentObject;
	}
	finally {
    		// Make sure we exit at the same stack level as when we started.
    		spClass = spBase;

                // We've completed deserializing this object.  Any
                // future indirections will be handled correctly at the
                // CDR level.  The ActiveRecursionManager only deals with
                // objects currently being deserialized.
                activeRecursionMgr.removeObject(offset);
    	    }
		
    	}

    /**
     * This input method uses FullValueDescriptions retrieved from the sender's runtime to 
     * read in the data.  This method is capable of throwing out data not applicable to client's fields.
     *
     * NOTE : If the local description indicates custom marshaling and the remote type's FVD also
     * indicates custom marsahling than the local type is used to read the data off the wire.  However,
     * if either says custom while the other does not, a MARSHAL error is thrown.  Externalizable is 
     * a form of custom marshaling.
     *
     */
    private Object skipObjectUsingFVD(String repositoryID,
				      com.sun.org.omg.SendingContext.CodeBase sender)
	throws IOException, ClassNotFoundException
    {

	Enumeration fvdsList = getOrderedDescriptions(repositoryID, sender).elements();
		
	while(fvdsList.hasMoreElements()) {
	    FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
	    String repIDForFVD = vhandler.getClassName(fvd.id);
			
	    if (!repIDForFVD.equals("java.lang.Object")) {
		if (fvd.is_custom) {

                    readFormatVersion();
                        
                    boolean calledDefaultWriteObject = readBoolean();
                        
                    if (calledDefaultWriteObject)
                        inputClassFields(null, null, null, fvd.members, sender);

                    if (getStreamFormatVersion() == 2) {

                        ((ValueInputStream)getOrbStream()).start_value();
                        ((ValueInputStream)getOrbStream()).end_value();
                    }

                    // WARNING: If stream format version is 1 and there's
                    // optional data, we'll get some form of exception down
                    // the line.

		} else { 
		    // Use default marshaling
		    inputClassFields(null, null, null, fvd.members, sender);
		}
	    }

	} // end : while(fvdsList.hasMoreElements()) 
	return null;
		
    }

    ///////////////////

    private int findNextClass(String classname, Class classes[], int _spClass, int _spBase){

	for (int i = _spClass; i > _spBase; i--){
	    if (classname.equals(classes[i].getName())) {
		return i;
	    }
	}

	return -1;
    }

    /*
     * Invoke the readObject method if present.  Assumes that in the case of custom
     * marshaling, the format version and defaultWriteObject indicator were already
     * removed.
     */
    private boolean invokeObjectReader(ObjectStreamClass osc, Object obj, Class aclass)
	throws InvalidClassException, StreamCorruptedException,
	       ClassNotFoundException, IOException
    {
	if (osc.readObjectMethod == null) {
	    return false;
	}

	try {
	    osc.readObjectMethod.invoke( obj, readObjectArgList ) ;
	    return true;
	} catch (InvocationTargetException e) {
	    Throwable t = e.getTargetException();
	    if (t instanceof ClassNotFoundException)
		throw (ClassNotFoundException)t;
	    else if (t instanceof IOException)
		throw (IOException)t;
	    else if (t instanceof RuntimeException)
		throw (RuntimeException) t;
	    else if (t instanceof Error)
		throw (Error) t;
	    else
		// XXX I18N, logging needed.
		throw new Error("internal error");
	} catch (IllegalAccessException e) {
	    return false;
	}
    }

    /*
     * Reset the stream to be just like it was after the constructor.
     */
    private void resetStream() throws IOException {

	if (classes == null)
	    classes = new Class[20];
	else {
	    for (int i = 0; i < classes.length; i++)
		classes[i] = null;
	}
	if (classdesc == null)
	    classdesc = new ObjectStreamClass[20];
	else {
	    for (int i = 0; i < classdesc.length; i++)
		classdesc[i] = null;
	}
	spClass = 0;

	if (callbacks != null)
	    callbacks.setSize(0);	// discard any pending callbacks
    }

    /**
     * Factored out of inputClassFields  This reads a primitive value and sets it 
     * in the field of o described by the ObjectStreamField field.
     * 
     * Note that reflection cannot be used here, because reflection cannot be used
     * to set final fields. 
     */
    private void inputPrimitiveField(Object o, Class cl, ObjectStreamField field)
        throws InvalidClassException, IOException {

        try {
            switch (field.getTypeCode()) {
                case 'B':
                    byte byteValue = orbStream.read_octet();
		    bridge.putByte( o, field.getFieldID(), byteValue ) ;
		    //reflective code: field.getField().setByte( o, byteValue ) ;
                    break;
                case 'Z':
                    boolean booleanValue = orbStream.read_boolean();
		    bridge.putBoolean( o, field.getFieldID(), booleanValue ) ;
		    //reflective code: field.getField().setBoolean( o, booleanValue ) ;
                    break;
		case 'C':
                    char charValue = orbStream.read_wchar();
		    bridge.putChar( o, field.getFieldID(), charValue ) ;
		    //reflective code: field.getField().setChar( o, charValue ) ;
                    break;
		case 'S':
                    short shortValue = orbStream.read_short();
		    bridge.putShort( o, field.getFieldID(), shortValue ) ;
		    //reflective code: field.getField().setShort( o, shortValue ) ;
                    break;
		case 'I':
                    int intValue = orbStream.read_long();
		    bridge.putInt( o, field.getFieldID(), intValue ) ;
		    //reflective code: field.getField().setInt( o, intValue ) ;
                    break;
		case 'J':
                    long longValue = orbStream.read_longlong();
		    bridge.putLong( o, field.getFieldID(), longValue ) ;
		    //reflective code: field.getField().setLong( o, longValue ) ;
                    break;
		case 'F' :
                    float floatValue = orbStream.read_float();
		    bridge.putFloat( o, field.getFieldID(), floatValue ) ;
		    //reflective code: field.getField().setFloat( o, floatValue ) ;
                    break;
		case 'D' :
                    double doubleValue = orbStream.read_double();
		    bridge.putDouble( o, field.getFieldID(), doubleValue ) ;
		    //reflective code: field.getField().setDouble( o, doubleValue ) ;
                    break;
		default:
		    // XXX I18N, logging needed.
                    throw new InvalidClassException(cl.getName());
            }
        } catch (IllegalArgumentException e) {
            /* This case should never happen. If the field types
               are not the same, InvalidClassException is raised when
               matching the local class to the serialized ObjectStreamClass. */
            ClassCastException cce = new ClassCastException("Assigning instance of class " +
                                         field.getType().getName() +
                                         " to field " +
                                         currentClassDesc.getName() + '#' +
                                         field.getField().getName());
	    cce.initCause( e ) ;
	    throw cce ;
	}
     }

    private Object inputObjectField(org.omg.CORBA.ValueMember field,
                                    com.sun.org.omg.SendingContext.CodeBase sender)
        throws IndirectionException, ClassNotFoundException, IOException,
               StreamCorruptedException {

        Object objectValue = null;
        Class type = null;
        String id = field.id;
							
        try {
            type = vhandler.getClassFromType(id);
        } catch(ClassNotFoundException cnfe) {
            // Make sure type = null
            type = null;
        }

        String signature = null;
        if (type != null)
            signature = ValueUtility.getSignature(field);
								
        if (signature != null && (signature.equals("Ljava/lang/Object;") ||
                                  signature.equals("Ljava/io/Serializable;") ||
                                  signature.equals("Ljava/io/Externalizable;"))) {
            objectValue = javax.rmi.CORBA.Util.readAny(orbStream);
        } else {
            // Decide what method call to make based on the type. If
            // it is a type for which we need to load a stub, convert
            // the type to the correct stub type.
            //
            // NOTE : Since FullValueDescription does not allow us
            // to ask whether something is an interface we do not
            // have the ability to optimize this check.
            
            int callType = ValueHandlerImpl.kValueType;
            
            if (!vhandler.isSequence(id)) {

                if (field.type.kind().value() == kRemoteTypeCode.kind().value()) {

                    // RMI Object reference...
                    callType = ValueHandlerImpl.kRemoteType;
                    
                } else {

                    // REVISIT.  If we don't have the local class,
                    // we should probably verify that it's an RMI type, 
                    // query the remote FVD, and use is_abstract.
                    // Our FVD seems to get NullPointerExceptions for any
                    // non-RMI types.

                    // This uses the local class in the same way as
                    // inputObjectField(ObjectStreamField) does.  REVISIT
                    // inputObjectField(ObjectStreamField)'s loadStubClass
                    // logic.  Assumption is that the given type cannot
                    // evolve to become a CORBA abstract interface or
                    // a RMI abstract interface.

                    if (type != null && type.isInterface() &&
                        (vhandler.isAbstractBase(type) ||
                         ObjectStreamClassCorbaExt.isAbstractInterface(type))) {
                
                        callType = ValueHandlerImpl.kAbstractType;
                    }
                }
            }
                
            // Now that we have used the FVD of the field to determine the proper course
            // of action, it is ok to use the type (Class) from this point forward since 
            // the rep. id for this read will also follow on the wire.

            switch (callType) {
                case ValueHandlerImpl.kRemoteType: 
                    if (type != null)
                        objectValue = Utility.readObjectAndNarrow(orbStream, type);
                    else
                        objectValue = orbStream.read_Object();
                    break;
                case ValueHandlerImpl.kAbstractType: 
                    if (type != null)
                        objectValue = Utility.readAbstractAndNarrow(orbStream, type);
                    else
                        objectValue = orbStream.read_abstract_interface();
                    break;
                case ValueHandlerImpl.kValueType:
                    if (type != null)
                        objectValue = orbStream.read_value(type);
                    else
                                            objectValue = orbStream.read_value();
                    break;
                default:
		    // XXX I18N, logging needed.
                    throw new StreamCorruptedException("Unknown callType: " + callType);
            }
        }

        return objectValue;
    }

    /**
     * Factored out of inputClassFields and reused in 
     * inputCurrentClassFieldsForReadFields.
     *
     * Reads the field (which of an Object type as opposed to a primitive) 
     * described by ObjectStreamField field and returns it.
     */
    private Object inputObjectField(ObjectStreamField field) 
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IndirectionException, IOException {

        if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) {
            return javax.rmi.CORBA.Util.readAny(orbStream);
        }

        Object objectValue = null;

        // fields have an API to provide the actual class
        // corresponding to the data type
        // Class type = osc.forClass();
        Class fieldType = field.getType();
	Class actualType = fieldType; // This may change if stub loaded.
				
        // Decide what method call to make based on the fieldType. If
        // it is a type for which we need to load a stub, convert
        // the type to the correct stub type.
        
        int callType = ValueHandlerImpl.kValueType;
        boolean narrow = false;
        
        if (fieldType.isInterface()) { 
            boolean loadStubClass = false;
            
            if (java.rmi.Remote.class.isAssignableFrom(fieldType)) {
                
                // RMI Object reference...
                callType = ValueHandlerImpl.kRemoteType;
                
            } else if (org.omg.CORBA.Object.class.isAssignableFrom(fieldType)){
                
                // IDL Object reference...
                callType = ValueHandlerImpl.kRemoteType;
                loadStubClass = true;
                
            } else if (vhandler.isAbstractBase(fieldType)) {
                // IDL Abstract Object reference...
                
                callType = ValueHandlerImpl.kAbstractType;
                loadStubClass = true;
            } else if (ObjectStreamClassCorbaExt.isAbstractInterface(fieldType)) {
                // RMI Abstract Object reference...
                
                callType = ValueHandlerImpl.kAbstractType;
            }
            
            if (loadStubClass) {
                try {
                    String codebase = Util.getCodebase(fieldType);
                    String repID = vhandler.createForAnyType(fieldType);
                    Class stubType =
			Utility.loadStubClass(repID, codebase, fieldType); 
		    actualType = stubType;
                } catch (ClassNotFoundException e) {
                    narrow = true;
                }
            } else {
                narrow = true;
            }
        }			

        switch (callType) {
            case ValueHandlerImpl.kRemoteType: 
                if (!narrow) 
                    objectValue = (Object)orbStream.read_Object(actualType);
                else
                    objectValue = Utility.readObjectAndNarrow(orbStream, actualType);
                break;
            case ValueHandlerImpl.kAbstractType: 
                if (!narrow)
                    objectValue = (Object)orbStream.read_abstract_interface(actualType); 
                else
                    objectValue = Utility.readAbstractAndNarrow(orbStream, actualType);
                break;
            case ValueHandlerImpl.kValueType:
                objectValue = (Object)orbStream.read_value(actualType);
                break;
            default:
		// XXX I18N, logging needed.
                throw new StreamCorruptedException("Unknown callType: " + callType);
        }

        return objectValue;
    }

    private final boolean mustUseRemoteValueMembers() {
        return defaultReadObjectFVDMembers != null;
    }

    void readFields(java.util.Map fieldToValueMap)
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IOException {

        if (mustUseRemoteValueMembers()) {
            inputRemoteMembersForReadFields(fieldToValueMap);
        } else
            inputCurrentClassFieldsForReadFields(fieldToValueMap);
    }

    private final void inputRemoteMembersForReadFields(java.util.Map fieldToValueMap)
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IOException {

        // Must have this local variable since defaultReadObjectFVDMembers
        // may get mangled by recursion.
        ValueMember fields[] = defaultReadObjectFVDMembers;

	try {

	    for (int i = 0; i < fields.length; i++) {

                switch (fields[i].type.kind().value()) {

                case TCKind._tk_octet:
                    byte byteValue = orbStream.read_octet();
                    fieldToValueMap.put(fields[i].name, new Byte(byteValue));
                    break;
                case TCKind._tk_boolean:
                    boolean booleanValue = orbStream.read_boolean();
                    fieldToValueMap.put(fields[i].name, new Boolean(booleanValue));
                    break;
                case TCKind._tk_char:
                    // Backwards compatibility.  Older Sun ORBs sent
                    // _tk_char even though they read and wrote wchars
                    // correctly.
                    //
                    // Fall through to the _tk_wchar case.
                case TCKind._tk_wchar:
                    char charValue = orbStream.read_wchar();
                    fieldToValueMap.put(fields[i].name, new Character(charValue));
                    break;
                case TCKind._tk_short:
                    short shortValue = orbStream.read_short();
                    fieldToValueMap.put(fields[i].name, new Short(shortValue));
                    break;
                case TCKind._tk_long:
                    int intValue = orbStream.read_long();
                    fieldToValueMap.put(fields[i].name, new Integer(intValue));
                    break;
                case TCKind._tk_longlong:
                    long longValue = orbStream.read_longlong();
                    fieldToValueMap.put(fields[i].name, new Long(longValue));
                    break;
                case TCKind._tk_float:
                    float floatValue = orbStream.read_float();
                    fieldToValueMap.put(fields[i].name, new Float(floatValue));
                    break;
                case TCKind._tk_double:
                    double doubleValue = orbStream.read_double();
                    fieldToValueMap.put(fields[i].name, new Double(doubleValue));
                    break;
                case TCKind._tk_value:
                case TCKind._tk_objref:
                case TCKind._tk_value_box:
                    Object objectValue = null;
                    try {
                        objectValue = inputObjectField(fields[i],
                                                       cbSender);

                    } catch (IndirectionException cdrie) {
                        // The CDR stream had never seen the given offset before,
                        // so check the recursion manager (it will throw an
                        // IOException if it doesn't have a reference, either).
                        objectValue = activeRecursionMgr.getObject(cdrie.offset);
                    }

                    fieldToValueMap.put(fields[i].name, objectValue);
                    break;
                default:
		    // XXX I18N, logging needed.
                    throw new StreamCorruptedException("Unknown kind: "
                                                       + fields[i].type.kind().value());
                }
            }
        } catch (Throwable t) {
            StreamCorruptedException result = new StreamCorruptedException(t.getMessage());
            result.initCause(t);
            throw result;
	}
    }

    /**
     * Called from InputStreamHook.
     *
     * Reads the fields of the current class (could be the ones
     * queried from the remote FVD) and puts them in
     * the given Map, name to value.  Wraps primitives in the
     * corresponding java.lang Objects.
     */
    private final void inputCurrentClassFieldsForReadFields(java.util.Map fieldToValueMap)
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IOException {

        ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy();

	int primFields = fields.length - currentClassDesc.objFields;

        // Handle the primitives first
        for (int i = 0; i < primFields; ++i) {

            switch (fields[i].getTypeCode()) {
                case 'B':
                    byte byteValue = orbStream.read_octet();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Byte(byteValue));
                    break;
                case 'Z':
                   boolean booleanValue = orbStream.read_boolean();
                   fieldToValueMap.put(fields[i].getName(),
                                       new Boolean(booleanValue));
                   break;
		case 'C':
                    char charValue = orbStream.read_wchar();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Character(charValue));
                    break;
		case 'S':
                    short shortValue = orbStream.read_short();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Short(shortValue));
                    break;
		case 'I':
                    int intValue = orbStream.read_long();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Integer(intValue));
                    break;
		case 'J':
                    long longValue = orbStream.read_longlong();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Long(longValue));
                    break;
		case 'F' :
                    float floatValue = orbStream.read_float();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Float(floatValue));
                    break;
		case 'D' :
                    double doubleValue = orbStream.read_double();
                    fieldToValueMap.put(fields[i].getName(),
                                        new Double(doubleValue));
                    break;
		default:
		    // XXX I18N, logging needed.
                    throw new InvalidClassException(currentClassDesc.getName());
	    }
	}

	/* Read and set object fields from the input stream. */
	if (currentClassDesc.objFields > 0) {
	    for (int i = primFields; i < fields.length; i++) {
                Object objectValue = null;
                try {
                    objectValue = inputObjectField(fields[i]);
                } catch(IndirectionException cdrie) {
                    // The CDR stream had never seen the given offset before,
                    // so check the recursion manager (it will throw an
                    // IOException if it doesn't have a reference, either).
                    objectValue = activeRecursionMgr.getObject(cdrie.offset);
                }

                fieldToValueMap.put(fields[i].getName(), objectValue);
            }
        }
    }

    /*
     * Read the fields of the specified class from the input stream and set
     * the values of the fields in the specified object. If the specified
     * object is null, just consume the fields without setting any values. If
     * any ObjectStreamField does not have a reflected Field, don't try to set
     * that field in the object.
     *
     * REVISIT -- This code doesn't do what the comment says to when
     * getField() is null!
     */
    private void inputClassFields(Object o, Class cl,
				  ObjectStreamField[] fields, 
				  com.sun.org.omg.SendingContext.CodeBase sender)
	throws InvalidClassException, StreamCorruptedException,
	       ClassNotFoundException, IOException
    {
		
	int primFields = fields.length - currentClassDesc.objFields;

	if (o != null) {
	    for (int i = 0; i < primFields; ++i) {
		if (fields[i].getField() == null)
		    continue;

                inputPrimitiveField(o, cl, fields[i]);
	    }
	}

	/* Read and set object fields from the input stream. */
	if (currentClassDesc.objFields > 0) {
	    for (int i = primFields; i < fields.length; i++) {
		Object objectValue = null;

                try {
                    objectValue = inputObjectField(fields[i]);
                } catch(IndirectionException cdrie) {
                    // The CDR stream had never seen the given offset before,
                    // so check the recursion manager (it will throw an
                    // IOException if it doesn't have a reference, either).
                    objectValue = activeRecursionMgr.getObject(cdrie.offset);
                }

		if ((o == null) || (fields[i].getField() == null)) {
		    continue;
		}

		try {
		    bridge.putObject( o, fields[i].getFieldID(), objectValue ) ;
		    // reflective code: fields[i].getField().set( o, objectValue ) ;
		} catch (IllegalArgumentException e) {
		    ClassCastException exc = new ClassCastException("Assigning instance of class " +
						 objectValue.getClass().getName() +
						 " to field " +
						 currentClassDesc.getName() +
						 '#' +
						 fields[i].getField().getName());
		    exc.initCause( e ) ;
		    throw exc ;
		}
	    } // end : for loop
	    }
	}

    /*
     * Read the fields of the specified class from the input stream and set
     * the values of the fields in the specified object. If the specified
     * object is null, just consume the fields without setting any values. If
     * any ObjectStreamField does not have a reflected Field, don't try to set
     * that field in the object.
     */
    private void inputClassFields(Object o, Class cl, 
				  ObjectStreamClass osc,
				  ValueMember[] fields,
				  com.sun.org.omg.SendingContext.CodeBase sender)
	throws InvalidClassException, StreamCorruptedException,
	       ClassNotFoundException, IOException
    {
	try{
	    for (int i = 0; i < fields.length; ++i) {
		try {
		    switch (fields[i].type.kind().value()) {
		    case TCKind._tk_octet:
			byte byteValue = orbStream.read_octet();
			if ((o != null) && osc.hasField(fields[i]))
			setByteField(o, cl, fields[i].name, byteValue);
			break;
		    case TCKind._tk_boolean:
			boolean booleanValue = orbStream.read_boolean();
			if ((o != null) && osc.hasField(fields[i]))
			setBooleanField(o, cl, fields[i].name, booleanValue);
			break;
		    case TCKind._tk_char:
                        // Backwards compatibility.  Older Sun ORBs sent
                        // _tk_char even though they read and wrote wchars
                        // correctly.
                        //
                        // Fall through to the _tk_wchar case.
                    case TCKind._tk_wchar:
			char charValue = orbStream.read_wchar();
			if ((o != null) && osc.hasField(fields[i]))
			setCharField(o, cl, fields[i].name, charValue);
			break;
		    case TCKind._tk_short:
			short shortValue = orbStream.read_short();
			if ((o != null) && osc.hasField(fields[i]))
			setShortField(o, cl, fields[i].name, shortValue);
			break;
		    case TCKind._tk_long:
			int intValue = orbStream.read_long();
			if ((o != null) && osc.hasField(fields[i]))
			setIntField(o, cl, fields[i].name, intValue);
			break;
		    case TCKind._tk_longlong:
			long longValue = orbStream.read_longlong();
			if ((o != null) && osc.hasField(fields[i]))
			setLongField(o, cl, fields[i].name, longValue);
			break;
		    case TCKind._tk_float:
			float floatValue = orbStream.read_float();
			if ((o != null) && osc.hasField(fields[i]))
			setFloatField(o, cl, fields[i].name, floatValue);
			break;
		    case TCKind._tk_double:
			double doubleValue = orbStream.read_double();
			if ((o != null) && osc.hasField(fields[i]))
			setDoubleField(o, cl, fields[i].name, doubleValue);
			break;
                    case TCKind._tk_value:
		    case TCKind._tk_objref:
		    case TCKind._tk_value_box:
                        Object objectValue = null;
                        try {
                            objectValue = inputObjectField(fields[i], sender);
                        } catch (IndirectionException cdrie) {
                            // The CDR stream had never seen the given offset before,
                            // so check the recursion manager (it will throw an
                            // IOException if it doesn't have a reference, either).
                            objectValue = activeRecursionMgr.getObject(cdrie.offset);
                        }
								
			if (o == null)
			    continue;
			try {
			    if (osc.hasField(fields[i])){
                                setObjectField(o, 
                                               cl, 
                                               fields[i].name, 
                                               objectValue);
			    } else {
                                // REVISIT.  Convert to a log message.
                                // This is a normal case when fields have
                                // been added as part of evolution, but
                                // silently skipping can make it hard to
                                // debug if there's an error
//                                 System.out.println("**** warning, not setting field: "
//                                                    + fields[i].name
//                                                    + " since not on class "
//                                                    + osc.getName());

                            }
			} catch (IllegalArgumentException e) {
			    // XXX I18N, logging needed.
			    ClassCastException cce = new ClassCastException("Assigning instance of class " + 
				objectValue.getClass().getName() + " to field " + fields[i].name);
			    cce.initCause(e) ;
			    throw cce ;
			}		
			break;
                    default:
			// XXX I18N, logging needed.
                        throw new StreamCorruptedException("Unknown kind: "
                                                           + fields[i].type.kind().value());
		    }
		} catch (IllegalArgumentException e) {
		    /* This case should never happen. If the field types
		       are not the same, InvalidClassException is raised when
		       matching the local class to the serialized ObjectStreamClass. */
		    // XXX I18N, logging needed.
		    ClassCastException cce = new ClassCastException("Assigning instance of class " + fields[i].id + 
			" to field " + currentClassDesc.getName() + '#' + fields[i].name);
		    cce.initCause( e ) ;
		    throw cce ;
		}
	    }
	} catch(Throwable t){
	    // XXX I18N, logging needed.
	    StreamCorruptedException sce = new StreamCorruptedException(t.getMessage());
	    sce.initCause(t) ;
	    throw sce ;
	}
    }

    private void skipCustomUsingFVD(ValueMember[] fields,
                                    com.sun.org.omg.SendingContext.CodeBase sender)
                                    throws InvalidClassException, StreamCorruptedException,
                                           ClassNotFoundException, IOException 
    {
        readFormatVersion();
        boolean calledDefaultWriteObject = readBoolean();

        if (calledDefaultWriteObject)
            throwAwayData(fields, sender);

        if (getStreamFormatVersion() == 2) {
            
            ((ValueInputStream)getOrbStream()).start_value();
            ((ValueInputStream)getOrbStream()).end_value();
        }
    }
	
    /*
     * Read the fields of the specified class from the input stream throw data away.
     * This must handle same switch logic as above.
     */
    private void throwAwayData(ValueMember[] fields,
			       com.sun.org.omg.SendingContext.CodeBase sender)
	throws InvalidClassException, StreamCorruptedException,
	       ClassNotFoundException, IOException
    {
	for (int i = 0; i < fields.length; ++i) {
	
	    try {
					
		switch (fields[i].type.kind().value()) {
		case TCKind._tk_octet:
		    orbStream.read_octet();
		    break;
		case TCKind._tk_boolean:
		    orbStream.read_boolean();
		    break;
		case TCKind._tk_char:
                    // Backwards compatibility.  Older Sun ORBs sent
                    // _tk_char even though they read and wrote wchars
                    // correctly.
                    //
                    // Fall through to the _tk_wchar case.
                case TCKind._tk_wchar:
		    orbStream.read_wchar();
		    break;
		case TCKind._tk_short:
		    orbStream.read_short();
		    break;
		case TCKind._tk_long:
		    orbStream.read_long();
		    break;
		case TCKind._tk_longlong:
		    orbStream.read_longlong();
		    break;
		case TCKind._tk_float:
		    orbStream.read_float();
		    break;
		case TCKind._tk_double:
		    orbStream.read_double();
		    break;
                case TCKind._tk_value:
		case TCKind._tk_objref:
		case TCKind._tk_value_box:
		    Class type = null;
	            String id = fields[i].id;

		    try {
			type = vhandler.getClassFromType(id);
		    }
		    catch(ClassNotFoundException cnfe){
			// Make sure type = null
			type = null;
		    }
		    String signature = null;
		    if (type != null)
			signature = ValueUtility.getSignature(fields[i]);
								
		    // Read value
		    try {
			if ((signature != null) && ( signature.equals("Ljava/lang/Object;") ||
						     signature.equals("Ljava/io/Serializable;") ||
						     signature.equals("Ljava/io/Externalizable;")) ) {
			    javax.rmi.CORBA.Util.readAny(orbStream);
			}
			else {
			    // Decide what method call to make based on the type.
			    //
			    // NOTE : Since FullValueDescription does not allow us
			    // to ask whether something is an interface we do not
			    // have the ability to optimize this check.
										
			    int callType = ValueHandlerImpl.kValueType;

			    if (!vhandler.isSequence(id)) {
				FullValueDescription fieldFVD = sender.meta(fields[i].id);
				if (kRemoteTypeCode == fields[i].type) {

				    // RMI Object reference...
				    callType = ValueHandlerImpl.kRemoteType;
				} else if (fieldFVD.is_abstract) {
				    // RMI Abstract Object reference...

				    callType = ValueHandlerImpl.kAbstractType;
				}
			    }
										
			    // Now that we have used the FVD of the field to determine the proper course
			    // of action, it is ok to use the type (Class) from this point forward since 
			    // the rep. id for this read will also follow on the wire.

			    switch (callType) {
			    case ValueHandlerImpl.kRemoteType: 
				orbStream.read_Object();
				break;
			    case ValueHandlerImpl.kAbstractType: 
				orbStream.read_abstract_interface(); 
				break;
			    case ValueHandlerImpl.kValueType:
				if (type != null) {
				    orbStream.read_value(type);
				} else {
				    orbStream.read_value();
				}
				break;
                            default:
				// XXX I18N, logging needed.
                                throw new StreamCorruptedException("Unknown callType: "
                                                                   + callType);
			    }
			}
										
		    }
		    catch(IndirectionException cdrie) {
			// Since we are throwing this away, don't bother handling recursion.
			continue;
		    }
									
		    break;
                default:
		    // XXX I18N, logging needed.
                    throw new StreamCorruptedException("Unknown kind: "
                                                       + fields[i].type.kind().value());

		}
	    } catch (IllegalArgumentException e) {
		/* This case should never happen. If the field types
		   are not the same, InvalidClassException is raised when
		   matching the local class to the serialized ObjectStreamClass. */
		// XXX I18N, logging needed.
		ClassCastException cce = new ClassCastException("Assigning instance of class " + 
		    fields[i].id + " to field " + currentClassDesc.getName() + 
		    '#' + fields[i].name);
		cce.initCause(e) ;
		throw cce ;
	    }
	}
		
    }

    private static void setObjectField(Object o, Class c, String fieldName, Object v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putObject( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetObjectField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		ObjectUtility.compactObjectToString( v )) ;
	}
    }

    private static void setBooleanField(Object o, Class c, String fieldName, boolean v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putBoolean( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetBooleanField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Boolean(v) ) ;
	}
    }

    private static void setByteField(Object o, Class c, String fieldName, byte v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putByte( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetByteField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Byte(v) ) ;
	}
    }

    private static void setCharField(Object o, Class c, String fieldName, char v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putChar( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetCharField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Character(v) ) ;
	}
    }

    private static void setShortField(Object o, Class c, String fieldName, short v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putShort( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetShortField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Short(v) ) ;
	}
    }

    private static void setIntField(Object o, Class c, String fieldName, int v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putInt( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetIntField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Integer(v) ) ;
	}
    }

    private static void setLongField(Object o, Class c, String fieldName, long v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putLong( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetLongField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Long(v) ) ;
	}
    }

    private static void setFloatField(Object o, Class c, String fieldName, float v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putFloat( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetFloatField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Float(v) ) ;
	}
    }

    private static void setDoubleField(Object o, Class c, String fieldName, double v)
    {
	try {
	    Field fld = c.getDeclaredField( fieldName ) ;
	    long key = bridge.objectFieldOffset( fld ) ;
	    bridge.putDouble( o, key, v ) ;
	} catch (Exception e) {
	    throw utilWrapper.errorSetDoubleField( e, fieldName, 
		ObjectUtility.compactObjectToString( o ),
		new Double(v) ) ;
	}
    }

    /**
     * This class maintains a map of stream position to
     * an Object currently being deserialized.  It is used
     * to handle the cases where the are indirections to
     * an object on the recursion stack.  The CDR level
     * handles indirections to objects previously seen
     * (and completely deserialized) in the stream.
     */
    static class ActiveRecursionManager
    {
        private Map offsetToObjectMap;
        
        public ActiveRecursionManager() {
            // A hash map is unsynchronized and allows
            // null values
            offsetToObjectMap = new HashMap();
        }

        // Called right after allocating a new object.
        // Offset is the starting position in the stream
        // of the object.
        public void addObject(int offset, Object value) {
            offsetToObjectMap.put(new Integer(offset), value);
        }

        // If the given starting position doesn't refer
        // to the beginning of an object currently being
        // deserialized, this throws an IOException.
        // Otherwise, it returns a reference to the
        // object.
        public Object getObject(int offset) throws IOException {
            Integer position = new Integer(offset);

            if (!offsetToObjectMap.containsKey(position))
		// XXX I18N, logging needed.
                throw new IOException("Invalid indirection to offset "
                                      + offset);

            return offsetToObjectMap.get(position);
        }
        
        // Called when an object has been completely
        // deserialized, so it should no longer be in
        // this mapping.  The CDR level can handle
        // further indirections.
        public void removeObject(int offset) {
            offsetToObjectMap.remove(new Integer(offset));
        }

        // If the given offset doesn't map to an Object,
        // then it isn't an indirection to an object
        // currently being deserialized.
        public boolean containsObject(int offset) {
            return offsetToObjectMap.containsKey(new Integer(offset));
        }
    }
}
