/*
 * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE 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
 *
 */

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 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 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 IOException {
        super();
        resetStream();
    }

    final void setOrbStream(org.omg.CORBA_2_3.portable.InputStream os) {
        orbStream = os;
    }

    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 = (ValueHandlerImpl) vh;
    }

    public final ValueHandler getValueHandler() {
        return (ValueHandler) vhandler;
    }

    final void increaseRecursionDepth(){
        recursionDepth++;
    }

    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 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 synchronized 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 synchronized Object simpleReadObject(Class clz,
                                  String repositoryID,
                                  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 synchronized  void simpleSkipObject(String repositoryID,
                                       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 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
     */
    final synchronized void defaultReadObjectDelegate()
    /* throws IOException, ClassNotFoundException, NotActiveException */
    {
        try {
            if (currentObject == null || currentClassDesc == null)
                // XXX I18N, logging needed.
                throw new NotActiveException("defaultReadObjectDelegate");

            if (!currentClassDesc.forClass().isAssignableFrom(
                    currentObject.getClass())) {
                throw new IOException("Object Type mismatch");
            }

            // 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 synchronized Object inputObject(Class clz,
                               String repositoryID,
                               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 (Enum.class.isAssignableFrom( clz )) {
                int ordinal = orbStream.read_long() ;
                String value = (String)orbStream.read_value( String.class ) ;
                return Enum.valueOf( clz, value ) ;
            } else 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

            if (currentClass.getName().equals("java.lang.String")) {
                return this.readUTF();
            }
            /* 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,
                                          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 synchronized Object inputObjectUsingFVD(Class clz,
                                       String repositoryID,
                                       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,
                                      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();
                    if (field.getField() != null) {
                        bridge.putByte( o, field.getFieldID(), byteValue ) ;
                        //reflective code: field.getField().setByte( o, byteValue ) ;
                    }
                    break;
                case 'Z':
                    boolean booleanValue = orbStream.read_boolean();
                    if (field.getField() != null) {
                        bridge.putBoolean( o, field.getFieldID(), booleanValue ) ;
                        //reflective code: field.getField().setBoolean( o, booleanValue ) ;
                    }
                    break;
                case 'C':
                    char charValue = orbStream.read_wchar();
                    if (field.getField() != null) {
                        bridge.putChar( o, field.getFieldID(), charValue ) ;
                        //reflective code: field.getField().setChar( o, charValue ) ;
                    }
                    break;
                case 'S':
                    short shortValue = orbStream.read_short();
                    if (field.getField() != null) {
                        bridge.putShort( o, field.getFieldID(), shortValue ) ;
                        //reflective code: field.getField().setShort( o, shortValue ) ;
                    }
                    break;
                case 'I':
                    int intValue = orbStream.read_long();
                    if (field.getField() != null) {
                        bridge.putInt( o, field.getFieldID(), intValue ) ;
                        //reflective code: field.getField().setInt( o, intValue ) ;
                    }
                    break;
                case 'J':
                    long longValue = orbStream.read_longlong();
                    if (field.getField() != null) {
                        bridge.putLong( o, field.getFieldID(), longValue ) ;
                        //reflective code: field.getField().setLong( o, longValue ) ;
                    }
                    break;
                case 'F' :
                    float floatValue = orbStream.read_float();
                    if (field.getField() != null) {
                        bridge.putFloat( o, field.getFieldID(), floatValue ) ;
                        //reflective code: field.getField().setFloat( o, floatValue ) ;
                    }
                    break;
                case 'D' :
                    double doubleValue = orbStream.read_double();
                    if (field.getField() != null) {
                        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(ValueMember field,
                                    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 = 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 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(Map fieldToValueMap)
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IOException {

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

    private final void inputRemoteMembersForReadFields(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(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, final Class<?> cl,
                                  ObjectStreamField[] fields,
                                  CodeBase sender)
        throws InvalidClassException, StreamCorruptedException,
               ClassNotFoundException, IOException
    {

        int primFields = fields.length - currentClassDesc.objFields;

        if (o != null) {
            for (int i = 0; i < primFields; ++i) {
                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 {
                    Class<?> fieldCl = fields[i].getClazz();
                    if ((objectValue != null)
                            && (!fieldCl.isAssignableFrom(
                                    objectValue.getClass()))) {
                        throw new IllegalArgumentException("Field mismatch");
                    }
                    Field declaredClassField = null;
                    final String inputStreamFieldName = fields[i].getName();
                    try {
                        declaredClassField = getDeclaredField( cl, inputStreamFieldName);
                    } catch (PrivilegedActionException paEx) {
                        throw new IllegalArgumentException(
                            (NoSuchFieldException) paEx.getException());
                    } catch (SecurityException secEx) {
                        throw new IllegalArgumentException(secEx);
                    }  catch (NullPointerException npEx) {
                        continue;
                    } catch (NoSuchFieldException e) {
                        continue;
                    }

                    if (declaredClassField == null) {
                        continue;
                    }
                    Class<?> declaredFieldClass = declaredClassField.getType();

                    // check input field type is a declared field type
                    // input field is a subclass of the declared field
                    if (!declaredFieldClass.isAssignableFrom(fieldCl)) {
                        throw new IllegalArgumentException(
                                "Field Type mismatch");
                    }
                    if (objectValue != null && !fieldCl.isInstance(objectValue)) {
                        throw new IllegalArgumentException();
                    }
                    bridge.putObject( o, fields[i].getFieldID(), objectValue ) ;
                    // reflective code: fields[i].getField().set( o, objectValue ) ;
                } catch (IllegalArgumentException iaEx) {
                    String objectValueClassName = "null";
                    String currentClassDescClassName = "null";
                    String fieldName = "null";
                    if (objectValue != null) {
                        objectValueClassName = objectValue.getClass().getName();
                    }
                    if (currentClassDesc != null) {
                        currentClassDescClassName = currentClassDesc.getName();
                    }
                    if (fields[i] != null && fields[i].getField() != null) {
                        fieldName = fields[i].getField().getName();
                    }
                    ClassCastException ccEx = new ClassCastException(
                            "Assigning instance of class " + objectValueClassName
                                    + " to field " + currentClassDescClassName + '#' + fieldName);
                    ccEx.initCause( iaEx ) ;
                    throw ccEx ;
                }
            } // 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,
                                  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,
                                    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,
                               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;")) ) {
                            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 = getDeclaredField( c, fieldName ) ;
            Class fieldCl = fld.getType();
            if(v != null && !fieldCl.isInstance(v)) {
                throw new Exception();
            }
            long key = bridge.objectFieldOffset( fld ) ;
            bridge.putObject( o, key, v ) ;
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetObjectField( e, fieldName,
                    o.toString(),
                    v.toString() ) ;
            } else {
                throw utilWrapper.errorSetObjectField( e, fieldName,
                    "null " + c.getName() + " object",
                    v.toString() ) ;
            }
        }
    }

    private static void setBooleanField(Object o, Class<?> c, String fieldName, boolean v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Boolean.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putBoolean( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
            throw utilWrapper.errorSetBooleanField( e, fieldName,
                o.toString(),
                new Boolean(v) ) ;
            } else {
                throw utilWrapper.errorSetBooleanField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Boolean(v) ) ;
            }
        }
    }

    private static void setByteField(Object o, Class<?> c, String fieldName, byte v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Byte.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putByte( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetByteField( e, fieldName,
                    o.toString(),
                    new Byte(v) ) ;
            } else {
                throw utilWrapper.errorSetByteField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Byte(v) ) ;
            }
        }
    }

    private static void setCharField(Object o, Class<?> c, String fieldName, char v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Character.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putChar( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetCharField( e, fieldName,
                    o.toString(),
                    new Character(v) ) ;
            } else {
                throw utilWrapper.errorSetCharField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Character(v) ) ;
            }
        }
    }

    private static void setShortField(Object o, Class<?> c, String fieldName, short v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Short.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putShort( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
            throw utilWrapper.errorSetShortField( e, fieldName,
                o.toString(),
                new Short(v) ) ;
            } else {
                throw utilWrapper.errorSetShortField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Short(v) ) ;
            }
        }
    }

    private static void setIntField(Object o, final Class<?> c, final String fieldName, int v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Integer.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putInt( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetIntField( e, fieldName,
                    o.toString(),
                    new Integer(v) ) ;
            } else {
                throw utilWrapper.errorSetIntField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Integer(v) ) ;
            }
        }
    }

    private static void setLongField(Object o, Class<?> c, String fieldName, long v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Long.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putLong( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetLongField( e, fieldName,
                    o.toString(),
                    new Long(v) ) ;
            } else {
                throw utilWrapper.errorSetLongField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Long(v) ) ;
            }
        }
    }

    private static void setFloatField(Object o, Class<?> c, String fieldName, float v)
    {
        try {
            Field fld = getDeclaredField( c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Float.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putFloat( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetFloatField( e, fieldName,
                    o.toString(),
                    new Float(v) ) ;
            } else {
                throw utilWrapper.errorSetFloatField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Float(v) ) ;
            }
        }
    }

    private static void setDoubleField(Object o, Class<?> c, String fieldName, double v)
    {
        try {
            Field fld = getDeclaredField(c, fieldName ) ;
            if ((fld != null) && (fld.getType() == Double.TYPE)) {
                long key = bridge.objectFieldOffset( fld ) ;
                bridge.putDouble( o, key, v ) ;
            } else {
                throw new InvalidObjectException("Field Type mismatch");
            }
        } catch (Exception e) {
            if (o != null) {
                throw utilWrapper.errorSetDoubleField( e, fieldName,
                    o.toString(),
                    new Double(v) ) ;
            } else {
                throw utilWrapper.errorSetDoubleField( e, fieldName,
                    "null " + c.getName() + " object",
                    new Double(v) ) ;
            }
        }
    }


    private static Field getDeclaredField(final Class<?> c,
                                           final String fieldName)
        throws PrivilegedActionException, NoSuchFieldException, SecurityException {
        if (System.getSecurityManager() == null) {
            return c.getDeclaredField(fieldName);
        } else {
            return AccessController
                .doPrivileged(new PrivilegedExceptionAction<Field>() {
                    public Field run()
                            throws NoSuchFieldException {
                        return c.getDeclaredField(fieldName);
                    }
                });
        }
    }

    /**
     * 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<Integer, Object> 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));
        }
    }
}
