/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

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

import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import java.io.IOException;
import java.io.PrintStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.omg.CORBA.TypeCode ;
import org.omg.CORBA.StructMember ;
import org.omg.CORBA.UnionMember ;
import org.omg.CORBA.ValueMember ;
import org.omg.CORBA.TCKind ;
import org.omg.CORBA.Any ;
import org.omg.CORBA.Principal ;
import org.omg.CORBA.BAD_TYPECODE ;
import org.omg.CORBA.BAD_PARAM ;
import org.omg.CORBA.BAD_OPERATION ;
import org.omg.CORBA.INTERNAL ;
import org.omg.CORBA.MARSHAL ;
import org.omg.CORBA.TypeCodePackage.BadKind ;
import org.omg.CORBA_2_3.portable.InputStream;
import org.omg.CORBA_2_3.portable.OutputStream;

import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.encoding.MarshalInputStream;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.encoding.CDRInputStream;
import com.sun.corba.se.impl.encoding.CDROutputStream;
import com.sun.corba.se.impl.encoding.TypeCodeInputStream;
import com.sun.corba.se.impl.encoding.TypeCodeOutputStream;
import com.sun.corba.se.impl.encoding.TypeCodeReader;
import com.sun.corba.se.impl.encoding.WrapperInputStream;

import com.sun.corba.se.impl.logging.ORBUtilSystemException;

// no chance of subclasses, so no problems with runtime helper lookup
public final class TypeCodeImpl extends TypeCode
{
    //static final boolean debug = false;

    // the indirection TCKind, needed for recursive typecodes.
    protected static final int tk_indirect = 0xFFFFFFFF;

    // typecode encodings have three different categories that determine
    // how the encoding should be done.

    private static final int EMPTY = 0; // no parameters
    private static final int SIMPLE = 1;        // simple parameters.
    private static final int COMPLEX = 2; // complex parameters. need to
    // use CDR encapsulation for
    // parameters

    // a table storing the encoding category for the various typecodes.

    private static final int typeTable[] = {
        EMPTY,  // tk_null
        EMPTY,  // tk_void
        EMPTY,  // tk_short
        EMPTY,  // tk_long
        EMPTY,  // tk_ushort
        EMPTY,  // tk_ulong
        EMPTY,  // tk_float
        EMPTY,  // tk_double
        EMPTY,  // tk_boolean
        EMPTY,  // tk_char
        EMPTY,  // tk_octet
        EMPTY,  // tk_any
        EMPTY,  // tk_typecode
        EMPTY,  // tk_principal
        COMPLEX,        // tk_objref
        COMPLEX,        // tk_struct
        COMPLEX,        // tk_union
        COMPLEX,        // tk_enum
        SIMPLE, // tk_string
        COMPLEX,        // tk_sequence
        COMPLEX,        // tk_array
        COMPLEX,        // tk_alias
        COMPLEX,        // tk_except
        EMPTY,  // tk_longlong
        EMPTY,  // tk_ulonglong
        EMPTY,  // tk_longdouble
        EMPTY,  // tk_wchar
        SIMPLE, // tk_wstring
        SIMPLE, // tk_fixed
        COMPLEX,        // tk_value
        COMPLEX,        // tk_value_box
        COMPLEX,        // tk_native
        COMPLEX // tk_abstract_interface
    };

    // Maps TCKind values to names
    // This is also used in AnyImpl.
    static final String[] kindNames = {
        "null",
        "void",
        "short",
        "long",
        "ushort",
        "ulong",
        "float",
        "double",
        "boolean",
        "char",
        "octet",
        "any",
        "typecode",
        "principal",
        "objref",
        "struct",
        "union",
        "enum",
        "string",
        "sequence",
        "array",
        "alias",
        "exception",
        "longlong",
        "ulonglong",
        "longdouble",
        "wchar",
        "wstring",
        "fixed",
        "value",
        "valueBox",
        "native",
        "abstractInterface"
    };

    private int                 _kind           = 0;    // the typecode kind

    // data members for representing the various kinds of typecodes.
    private String          _id             = "";   // the typecode repository id
    private String          _name           = "";   // the typecode name
    private int             _memberCount    = 0;    // member count
    private String          _memberNames[]  = null; // names of members
    private TypeCodeImpl    _memberTypes[]  = null; // types of members
    private AnyImpl         _unionLabels[]  = null; // values of union labels
    private TypeCodeImpl    _discriminator  = null; // union discriminator type
    private int             _defaultIndex   = -1;   // union default index
    private int             _length         = 0;    // string/seq/array length
    private TypeCodeImpl    _contentType    = null; // seq/array/alias type
    // fixed
    private short           _digits         = 0;
    private short           _scale          = 0;
    // value type
    // _REVISIT_ We might want to keep references to the ValueMember classes
    // passed in at initialization instead of copying the relevant data.
    // Is the data immutable? What about StructMember, UnionMember etc.?
    private short           _type_modifier  = -1;   // VM_NONE, VM_CUSTOM,
    // VM_ABSTRACT, VM_TRUNCATABLE
    private TypeCodeImpl    _concrete_base  = null; // concrete base type
    private short           _memberAccess[] = null; // visibility of ValueMember
    // recursive sequence support
    private TypeCodeImpl    _parent         = null; // the enclosing type code
    private int             _parentOffset   = 0;    // the level of enclosure
    // recursive type code support
    private TypeCodeImpl    _indirectType   = null;

    // caches the byte buffer written in write_value for quick remarshaling...
    private byte[] outBuffer                = null;
    // ... but only if caching is enabled
    private boolean cachingEnabled          = false;

    // the ORB instance: may be instanceof ORBSingleton or ORB
    private ORB _orb;
    private ORBUtilSystemException wrapper ;

    ///////////////////////////////////////////////////////////////////////////
    // Constructors...

    public TypeCodeImpl(ORB orb)
    {
        // initialized to tk_null
        _orb = orb;
        wrapper = ORBUtilSystemException.get(
            (com.sun.corba.se.spi.orb.ORB)orb, CORBALogDomains.RPC_PRESENTATION ) ;
    }

    public TypeCodeImpl(ORB orb, TypeCode tc)
    // to handle conversion of "remote" typecodes into "native" style.
    // also see the 'convertToNative(ORB orb, TypeCode tc)' function
    {
        this(orb) ;

        // This is a protection against misuse of this constructor.
        // Should only be used if tc is not an instance of this class!
        // Otherwise we run into problems with recursive/indirect type codes.
        // _REVISIT_ We should make this constructor private
        if (tc instanceof TypeCodeImpl) {
            TypeCodeImpl tci = (TypeCodeImpl)tc;
            if (tci._kind == tk_indirect)
                throw wrapper.badRemoteTypecode() ;
            if (tci._kind == TCKind._tk_sequence && tci._contentType == null)
                throw wrapper.badRemoteTypecode() ;
        }

        // set up kind
        _kind   = tc.kind().value();

        try {
            // set up parameters
            switch (_kind) {
            case TCKind._tk_value:
                _type_modifier = tc.type_modifier();
                // concrete base may be null
                TypeCode tccb = tc.concrete_base_type();
                if (tccb != null) {
                    _concrete_base = convertToNative(_orb, tccb);
                } else {
                    _concrete_base = null;
                }
                //_memberAccess = tc._memberAccess;
                // Need to reconstruct _memberAccess using member_count() and member_visibility()
                _memberAccess = new short[tc.member_count()];
                for (int i=0; i < tc.member_count(); i++) {
                    _memberAccess[i] = tc.member_visibility(i);
                }
            case TCKind._tk_except:
            case TCKind._tk_struct:
            case TCKind._tk_union:
                // set up member types
                _memberTypes = new TypeCodeImpl[tc.member_count()];
                for (int i=0; i < tc.member_count(); i++) {
                    _memberTypes[i] = convertToNative(_orb, tc.member_type(i));
                    _memberTypes[i].setParent(this);
                }
            case TCKind._tk_enum:
                // set up member names
                _memberNames = new String[tc.member_count()];
                for (int i=0; i < tc.member_count(); i++) {
                    _memberNames[i] = tc.member_name(i);
                }
                // set up member count
                _memberCount = tc.member_count();
            case TCKind._tk_objref:
            case TCKind._tk_alias:
            case TCKind._tk_value_box:
            case TCKind._tk_native:
            case TCKind._tk_abstract_interface:
                setId(tc.id());
                _name = tc.name();
                break;
            }

            // set up stuff for unions
            switch (_kind) {
            case TCKind._tk_union:
                _discriminator = convertToNative(_orb, tc.discriminator_type());
                _defaultIndex  = tc.default_index();
                _unionLabels = new AnyImpl[_memberCount];
                for (int i=0; i < _memberCount; i++)
                    _unionLabels[i] = new AnyImpl(_orb, tc.member_label(i));
                break;
            }

            // set up length
            switch (_kind) {
            case TCKind._tk_string:
            case TCKind._tk_wstring:
            case TCKind._tk_sequence:
            case TCKind._tk_array:
                _length = tc.length();
            }

            // set up content type
            switch (_kind) {
            case TCKind._tk_sequence:
            case TCKind._tk_array:
            case TCKind._tk_alias:
            case TCKind._tk_value_box:
                _contentType = convertToNative(_orb, tc.content_type());
            }
        } catch (org.omg.CORBA.TypeCodePackage.Bounds e) {} catch (BadKind e) {}
        // dont have to worry about these since code ensures we dont step
        // out of bounds.
    }

    public TypeCodeImpl(ORB orb, int creationKind)
    // for primitive types
    {
        this(orb);

        // private API. dont bother checking that
        //     (creationKind < 0 || creationKind > typeTable.length)

        _kind = creationKind;

        // do initialization for special cases
        switch (_kind) {
        case TCKind._tk_objref:
            {
                // this is being used to create typecode for CORBA::Object
                setId("IDL:omg.org/CORBA/Object:1.0");
                _name = "Object";
                break;
            }

        case TCKind._tk_string:
        case TCKind._tk_wstring:
            {
                _length =0;
                break;
            }

        case TCKind._tk_value:
            {
                _concrete_base = null;
                break;
            }
        }
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name,
                        StructMember[] members)
                        // for structs and exceptions
    {
        this(orb);

        if ((creationKind == TCKind._tk_struct) || (creationKind == TCKind._tk_except)) {
            _kind               = creationKind;
            setId(id);
            _name               = name;
            _memberCount        = members.length;

            _memberNames = new String[_memberCount];
            _memberTypes = new TypeCodeImpl[_memberCount];

            for (int i = 0 ; i < _memberCount ; i++) {
                _memberNames[i] = members[i].name;
                _memberTypes[i] = convertToNative(_orb, members[i].type);
                _memberTypes[i].setParent(this);
            }
        } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name,
                        TypeCode discriminator_type,
                        UnionMember[] members)
                        // for unions
    {
        this(orb) ;

        if (creationKind == TCKind._tk_union) {
            _kind               = creationKind;
            setId(id);
            _name               = name;
            _memberCount        = members.length;
            _discriminator      = convertToNative(_orb, discriminator_type);

            _memberNames = new String[_memberCount];
            _memberTypes = new TypeCodeImpl[_memberCount];
            _unionLabels = new AnyImpl[_memberCount];

            for (int i = 0 ; i < _memberCount ; i++) {
                _memberNames[i] = members[i].name;
                _memberTypes[i] = convertToNative(_orb, members[i].type);
                _memberTypes[i].setParent(this);
                _unionLabels[i] = new AnyImpl(_orb, members[i].label);
                // check whether this is the default branch.
                if (_unionLabels[i].type().kind() == TCKind.tk_octet) {
                    if (_unionLabels[i].extract_octet() == (byte)0) {
                        _defaultIndex = i;
                    }
                }
            }
        } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name,
                        short type_modifier,
                        TypeCode concrete_base,
                        ValueMember[] members)
                        // for value types
    {
        this(orb) ;

        if (creationKind == TCKind._tk_value) {
            _kind               = creationKind;
            setId(id);
            _name               = name;
            _type_modifier      = type_modifier;
            if (concrete_base != null) {
                _concrete_base = convertToNative(_orb, concrete_base);
            }
            _memberCount        = members.length;

            _memberNames = new String[_memberCount];
            _memberTypes = new TypeCodeImpl[_memberCount];
            _memberAccess = new short[_memberCount];

            for (int i = 0 ; i < _memberCount ; i++) {
                _memberNames[i] = members[i].name;
                _memberTypes[i] = convertToNative(_orb, members[i].type);
                _memberTypes[i].setParent(this);
                _memberAccess[i] = members[i].access;
            }
        } // else initializes to null
    }


    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name,
                        String[] members)
                        // for enums
    {
        this(orb) ;

        if (creationKind == TCKind._tk_enum)
            {
                _kind           = creationKind;
                setId(id);
                _name           = name;
                _memberCount    = members.length;

                _memberNames = new String[_memberCount];

                for (int i = 0 ; i < _memberCount ; i++)
                    _memberNames[i] = members[i];
            } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name,
                        TypeCode original_type)
                        // for aliases and value boxes
    {
        this(orb) ;

        if ( creationKind == TCKind._tk_alias || creationKind == TCKind._tk_value_box )
            {
                _kind           = creationKind;
                setId(id);
                _name           = name;
                _contentType    = convertToNative(_orb, original_type);
            }
        // else initializes to null

    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        String id,
                        String name)
    {
        this(orb) ;

        if (creationKind == TCKind._tk_objref ||
            creationKind == TCKind._tk_native ||
            creationKind == TCKind._tk_abstract_interface)
            {
                _kind           = creationKind;
                setId(id);
                _name           = name;
            } // else initializes to null
    }


    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        int bound)
                        // for strings
    {
        this(orb) ;

        if (bound < 0)
            throw wrapper.negativeBounds() ;

        if ((creationKind == TCKind._tk_string) || (creationKind == TCKind._tk_wstring)) {
            _kind               = creationKind;
            _length             = bound;
        } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        int bound,
                        TypeCode element_type)
                        // for sequences and arrays
    {
        this(orb) ;

        if ( creationKind == TCKind._tk_sequence || creationKind == TCKind._tk_array ) {
            _kind               = creationKind;
            _length             = bound;
            _contentType        = convertToNative(_orb, element_type);
        } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        int bound,
                        int offset)
                        // for recursive sequences
    {
        this(orb) ;

        if (creationKind == TCKind._tk_sequence) {
            _kind               = creationKind;
            _length             = bound;
            _parentOffset       = offset;
        } // else initializes to null
    }

    public TypeCodeImpl(ORB orb,
                        String id)
                        // for recursive type codes
    {
        this(orb) ;

        _kind   = tk_indirect;
        // This is the type code of the type we stand in for, not our own.
        _id             = id;
        // Try to resolve it now. May return null in which case
        // we try again later (see indirectType()).
        tryIndirectType();
    }

    public TypeCodeImpl(ORB orb,
                        int creationKind,
                        short digits,
                        short scale)
                        // for fixed
    {
        this(orb) ;

        //if (digits < 1 || digits > 31)
        //throw new BAD_TYPECODE();

        if (creationKind == TCKind._tk_fixed) {
            _kind               = creationKind;
            _digits             = digits;
            _scale              = scale;
        } // else initializes to null
    }

    ///////////////////////////////////////////////////////////////////////////
    // Other creation functions...

    // Optimization:
    // If we checked for and returned constant primitive typecodes
    // here we could reduce object creation and also enable more
    // efficient typecode comparisons for primitive typecodes.
    //
    protected static TypeCodeImpl convertToNative(ORB orb,
                                                  TypeCode tc)
    {
        if (tc instanceof TypeCodeImpl)
            return (TypeCodeImpl) tc;
        else
            return new TypeCodeImpl(orb, tc);
    }

    public static CDROutputStream newOutputStream(ORB orb) {
        TypeCodeOutputStream tcos =
            sun.corba.OutputStreamFactory.newTypeCodeOutputStream(orb);
        //if (debug) System.out.println("Created TypeCodeOutputStream " + tcos +
        // " with no parent");
        return tcos;
    }

    // Support for indirect/recursive type codes

    private TypeCodeImpl indirectType() {
        _indirectType = tryIndirectType();
        if (_indirectType == null) {
            // Nothing we can do about that.
            throw wrapper.unresolvedRecursiveTypecode() ;
        }
        return _indirectType;
    }

    private TypeCodeImpl tryIndirectType() {
        // Assert that _kind == tk_indirect
        if (_indirectType != null)
            return _indirectType;

        setIndirectType(_orb.getTypeCode(_id));

        return _indirectType;
    }

    private void setIndirectType(TypeCodeImpl newType) {
        _indirectType = newType;
        if (_indirectType != null) {
            try {
                _id = _indirectType.id();
            } catch (BadKind e) {
                // can't happen
                throw wrapper.badkindCannotOccur() ;
            }
        }
    }

    private void setId(String newID) {
        _id = newID;
        if (_orb instanceof TypeCodeFactory) {
            ((TypeCodeFactory)_orb).setTypeCode(_id, this);
        }
        // check whether return value != this which would indicate that the
        // repository id isn't unique.
    }

    private void setParent(TypeCodeImpl parent) {
        _parent = parent;
    }

    private TypeCodeImpl getParentAtLevel(int level) {
        if (level == 0)
            return this;

        if (_parent == null)
            throw wrapper.unresolvedRecursiveTypecode() ;

        return _parent.getParentAtLevel(level - 1);
    }

    private TypeCodeImpl lazy_content_type() {
        if (_contentType == null) {
            if (_kind == TCKind._tk_sequence && _parentOffset > 0 && _parent != null) {
                // This is an unresolved recursive sequence tc.
                // Try to resolve it now if the hierarchy is complete.
                TypeCodeImpl realParent = getParentAtLevel(_parentOffset);
                if (realParent != null && realParent._id != null) {
                    // Create a recursive type code object as the content type.
                    // This is when the recursive sequence typecode morphes
                    // into a sequence typecode containing a recursive typecode.
                    _contentType = new TypeCodeImpl((ORB)_orb, realParent._id);
                }
            }
        }
        return _contentType;
    }

    // Other private functions

    private TypeCode realType(TypeCode aType) {
        TypeCode realType = aType;
        try {
            // Note: Indirect types are handled in kind() method
            while (realType.kind().value() == TCKind._tk_alias) {
                realType = realType.content_type();
            }
        } catch (BadKind bad) {
            // impossible
            throw wrapper.badkindCannotOccur() ;
        }
        return realType;
    }

    ///////////////////////////////////////////////////////////////////////////
    // TypeCode operations

    public final boolean equal(TypeCode tc)
    // _REVISIT_ for all optional names/ids, we might want to check that
    // they are equal in case both are non-nil.
    {
        if (tc == this)
            return true;

        try {

            if (_kind == tk_indirect) {
                //return indirectType().equal(tc);
                if (_id != null && tc.id() != null)
                    return _id.equals(tc.id());
                return (_id == null && tc.id() == null);
            }

            // make sure kinds are identical.
            if (_kind != tc.kind().value()) {
                return false;
            }

            switch (typeTable[_kind]) {
            case EMPTY:
                // no parameters to check.
                return true;

            case SIMPLE:
                switch (_kind) {
                case TCKind._tk_string:
                case TCKind._tk_wstring:
                    // check for bound.
                    return (_length == tc.length());

                case TCKind._tk_fixed:
                    return (_digits == tc.fixed_digits() && _scale == tc.fixed_scale());
                default:
                    return false;
                }

            case COMPLEX:

                switch(_kind) {

                case TCKind._tk_objref:
                    {
                        // check for logical id.
                        if (_id.compareTo(tc.id()) == 0) {
                            return true;
                        }

                        if (_id.compareTo(
                            (_orb.get_primitive_tc(_kind)).id()) == 0)
                        {
                            return true;
                        }

                        if (tc.id().compareTo(
                            (_orb.get_primitive_tc(_kind)).id()) == 0)
                        {
                            return true;
                        }

                        return false;
                    }

                case TCKind._tk_native:
                case TCKind._tk_abstract_interface:
                    {
                        // check for logical id.
                        if (_id.compareTo(tc.id()) != 0) {
                            return false;

                        }
                        // ignore name since its optional.
                        return true;
                    }

                case TCKind._tk_struct:
                case TCKind._tk_except:
                    {
                        // check for member count
                        if (_memberCount != tc.member_count())
                            return false;
                        // check for repository id
                        if (_id.compareTo(tc.id()) != 0)
                            return false;
                        // check for member types.
                        for (int i = 0 ; i < _memberCount ; i++)
                            if (! _memberTypes[i].equal(tc.member_type(i)))
                                return false;
                        // ignore id and names since those are optional.
                        return true;
                    }

                case TCKind._tk_union:
                    {
                        // check for member count
                        if (_memberCount != tc.member_count())
                            return false;
                        // check for repository id
                        if (_id.compareTo(tc.id()) != 0)
                            return false;
                        // check for default index
                        if (_defaultIndex != tc.default_index())
                            return false;
                        // check for discriminator type
                        if (!_discriminator.equal(tc.discriminator_type()))
                            return false;
                        // check for label types and values
                        for (int i = 0 ; i < _memberCount ; i++)
                            if (! _unionLabels[i].equal(tc.member_label(i)))
                                return false;
                        // check for branch types
                        for (int i = 0 ; i < _memberCount ; i++)
                            if (! _memberTypes[i].equal(tc.member_type(i)))
                                return false;
                        // ignore id and names since those are optional.
                        return true;
                    }

                case TCKind._tk_enum:
                    {
                        // check for repository id
                        if (_id.compareTo(tc.id()) != 0)
                            return false;
                        // check member count
                        if (_memberCount != tc.member_count())
                            return false;
                        // ignore names since those are optional.
                        return true;
                    }

                case TCKind._tk_sequence:
                case TCKind._tk_array:
                    {
                        // check bound/length
                        if (_length != tc.length()) {
                            return false;
                        }
                        // check content type
                        if (! lazy_content_type().equal(tc.content_type())) {
                            return false;
                        }
                        // ignore id and name since those are optional.
                        return true;
                    }

                case TCKind._tk_value:
                    {
                        // check for member count
                        if (_memberCount != tc.member_count())
                            return false;
                        // check for repository id
                        if (_id.compareTo(tc.id()) != 0)
                            return false;
                        // check for member types.
                        for (int i = 0 ; i < _memberCount ; i++)
                            if (_memberAccess[i] != tc.member_visibility(i) ||
                                ! _memberTypes[i].equal(tc.member_type(i)))
                                return false;
                        if (_type_modifier == tc.type_modifier())
                            return false;
                        // concrete_base may be null
                        TypeCode tccb = tc.concrete_base_type();
                        if ((_concrete_base == null && tccb != null) ||
                            (_concrete_base != null && tccb == null) ||
                            ! _concrete_base.equal(tccb))
                        {
                            return false;
                        }
                        // ignore id and names since those are optional.
                        return true;
                    }

                case TCKind._tk_alias:
                case TCKind._tk_value_box:
                    {
                        // check for repository id
                        if (_id.compareTo(tc.id()) != 0) {
                            return false;
                        }
                        // check for equality with the true type
                        return _contentType.equal(tc.content_type());
                    }
                }
            }
        } catch (org.omg.CORBA.TypeCodePackage.Bounds e) {} catch (BadKind e) {}
        // dont have to worry about these since the code ensures these dont
        // arise.
        return false;
    }

    /**
    * The equivalent operation is used by the ORB when determining type equivalence
    * for values stored in an IDL any.
    */
    public boolean equivalent(TypeCode tc) {
        if (tc == this) {
            return true;
        }

        // If the result of the kind operation on either TypeCode is tk_alias, recursively
        // replace the TypeCode with the result of calling content_type, until the kind
        // is no longer tk_alias.
        // Note: Always resolve indirect types first!
        TypeCode myRealType = (_kind == tk_indirect ? indirectType() : this);
        myRealType = realType(myRealType);
        TypeCode otherRealType = realType(tc);

        // If results of the kind operation on each typecode differ,
        // equivalent returns false.
        if (myRealType.kind().value() != otherRealType.kind().value()) {
            return false;
        }

        String myID = null;
        String otherID = null;
        try {
            myID = this.id();
            otherID = tc.id();
            // At this point the id operation is valid for both TypeCodes.

            // Return true if the results of id for both TypeCodes are non-empty strings
            // and both strings are equal.
            // If both ids are non-empty but are not equal, then equivalent returns FALSE.
            if (myID != null && otherID != null) {
                return (myID.equals(otherID));
            }
        } catch (BadKind e) {
            // id operation is not valid for either or both TypeCodes
        }

        // If either or both id is an empty string, or the TypeCode kind does not support
        // the id operation, perform a structural comparison of the TypeCodes.

        int myKind = myRealType.kind().value();
        try {
            if (myKind == TCKind._tk_struct ||
                myKind == TCKind._tk_union ||
                myKind == TCKind._tk_enum ||
                myKind == TCKind._tk_except ||
                myKind == TCKind._tk_value)
            {
                if (myRealType.member_count() != otherRealType.member_count())
                    return false;
            }
            if (myKind == TCKind._tk_union)
            {
                if (myRealType.default_index() != otherRealType.default_index())
                    return false;
            }
            if (myKind == TCKind._tk_string ||
                myKind == TCKind._tk_wstring ||
                myKind == TCKind._tk_sequence ||
                myKind == TCKind._tk_array)
            {
                if (myRealType.length() != otherRealType.length())
                    return false;
            }
            if (myKind == TCKind._tk_fixed)
            {
                if (myRealType.fixed_digits() != otherRealType.fixed_digits() ||
                    myRealType.fixed_scale() != otherRealType.fixed_scale())
                    return false;
            }
            if (myKind == TCKind._tk_union)
            {
                for (int i=0; i<myRealType.member_count(); i++) {
                    if (myRealType.member_label(i) != otherRealType.member_label(i))
                        return false;
                }
                if ( ! myRealType.discriminator_type().equivalent(
                    otherRealType.discriminator_type()))
                    return false;
            }
            if (myKind == TCKind._tk_alias ||
                myKind == TCKind._tk_value_box ||
                myKind == TCKind._tk_sequence ||
                myKind == TCKind._tk_array)
            {
                if ( ! myRealType.content_type().equivalent(otherRealType.content_type()))
                    return false;
            }
            if (myKind == TCKind._tk_struct ||
                myKind == TCKind._tk_union ||
                myKind == TCKind._tk_except ||
                myKind == TCKind._tk_value)
            {
                for (int i=0; i<myRealType.member_count(); i++) {
                    if ( ! myRealType.member_type(i).equivalent(
                        otherRealType.member_type(i)))
                        return false;
                }
            }
        } catch (BadKind e) {
            // impossible if we checked correctly above
            throw wrapper.badkindCannotOccur() ;
        } catch (org.omg.CORBA.TypeCodePackage.Bounds e) {
            // impossible if we checked correctly above
            throw wrapper.boundsCannotOccur() ;
        }

        // Structural comparison succeeded!
        return true;
    }

    public TypeCode get_compact_typecode() {
        // _REVISIT_ It isn't clear whether this method should operate on this or a copy.
        // For now just return this unmodified because the name and member_name fields
        // aren't used for comparison anyways.
        return this;
    }

    public TCKind kind()
    {
        if (_kind == tk_indirect)
            return indirectType().kind();
        return TCKind.from_int(_kind);
    }

    public boolean is_recursive()
    {
        // Recursive is the only form of indirect type codes right now.
        // Indirection can also be used for repeated type codes.
        return (_kind == tk_indirect);
    }

    public String id()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            //return indirectType().id(); // same as _id
        case TCKind._tk_except:
        case TCKind._tk_objref:
        case TCKind._tk_struct:
        case TCKind._tk_union:
        case TCKind._tk_enum:
        case TCKind._tk_alias:
        case TCKind._tk_value:
        case TCKind._tk_value_box:
        case TCKind._tk_native:
        case TCKind._tk_abstract_interface:
            // exception and objref typecodes must have a repository id.
            // structs, unions, enums, and aliases may or may not.
            return _id;
        default:
            // all other typecodes throw the BadKind exception.
            throw new BadKind();
        }
    }

    public String name()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().name();
        case TCKind._tk_except:
        case TCKind._tk_objref:
        case TCKind._tk_struct:
        case TCKind._tk_union:
        case TCKind._tk_enum:
        case TCKind._tk_alias:
        case TCKind._tk_value:
        case TCKind._tk_value_box:
        case TCKind._tk_native:
        case TCKind._tk_abstract_interface:
            return _name;
        default:
            throw new BadKind();
        }
    }

    public int member_count()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().member_count();
        case TCKind._tk_except:
        case TCKind._tk_struct:
        case TCKind._tk_union:
        case TCKind._tk_enum:
        case TCKind._tk_value:
            return _memberCount;
        default:
            throw new BadKind();
        }
    }

    public String member_name(int index)
        throws BadKind, org.omg.CORBA.TypeCodePackage.Bounds
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().member_name(index);
        case TCKind._tk_except:
        case TCKind._tk_struct:
        case TCKind._tk_union:
        case TCKind._tk_enum:
        case TCKind._tk_value:
            try {
                return _memberNames[index];
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new org.omg.CORBA.TypeCodePackage.Bounds();
            }
        default:
            throw new BadKind();
        }
    }

    public TypeCode member_type(int index)
        throws BadKind, org.omg.CORBA.TypeCodePackage.Bounds
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().member_type(index);
        case TCKind._tk_except:
        case TCKind._tk_struct:
        case TCKind._tk_union:
        case TCKind._tk_value:
            try {
                return _memberTypes[index];
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new org.omg.CORBA.TypeCodePackage.Bounds();
            }
        default:
            throw new BadKind();
        }
    }

    public Any member_label(int index)
        throws BadKind, org.omg.CORBA.TypeCodePackage.Bounds
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().member_label(index);
        case TCKind._tk_union:
            try {
                // _REVISIT_ Why create a new Any for this?
                return new AnyImpl(_orb, _unionLabels[index]);
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new org.omg.CORBA.TypeCodePackage.Bounds();
            }
        default:
            throw new BadKind();
        }
    }

    public TypeCode discriminator_type()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().discriminator_type();
        case TCKind._tk_union:
            return _discriminator;
        default:
            throw new BadKind();
        }
    }

    public int default_index()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().default_index();
        case TCKind._tk_union:
            return _defaultIndex;
        default:
            throw new BadKind();
        }
    }

    public int length()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().length();
        case TCKind._tk_string:
        case TCKind._tk_wstring:
        case TCKind._tk_sequence:
        case TCKind._tk_array:
            return _length;
        default:
            throw new BadKind();
        }
    }

    public TypeCode content_type()
        throws BadKind
    {
        switch (_kind) {
        case tk_indirect:
            return indirectType().content_type();
        case TCKind._tk_sequence:
            return lazy_content_type();
        case TCKind._tk_array:
        case TCKind._tk_alias:
        case TCKind._tk_value_box:
            return _contentType;
        default:
            throw new BadKind();
        }
    }

    public short fixed_digits() throws BadKind {
        switch (_kind) {
        case TCKind._tk_fixed:
            return _digits;
        default:
            throw new BadKind();
        }
    }

    public short fixed_scale() throws BadKind {
        switch (_kind) {
        case TCKind._tk_fixed:
            return _scale;
        default:
            throw new BadKind();
        }
    }

    public short member_visibility(int index) throws BadKind,
        org.omg.CORBA.TypeCodePackage.Bounds {
        switch (_kind) {
        case tk_indirect:
            return indirectType().member_visibility(index);
        case TCKind._tk_value:
            try {
                return _memberAccess[index];
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new org.omg.CORBA.TypeCodePackage.Bounds();
            }
        default:
            throw new BadKind();
        }
    }

    public short type_modifier() throws BadKind {
        switch (_kind) {
        case tk_indirect:
            return indirectType().type_modifier();
        case TCKind._tk_value:
            return _type_modifier;
        default:
            throw new BadKind();
        }
    }

    public TypeCode concrete_base_type() throws BadKind {
        switch (_kind) {
        case tk_indirect:
            return indirectType().concrete_base_type();
        case TCKind._tk_value:
            return _concrete_base;
        default:
            throw new BadKind();
        }
    }

    public void read_value(InputStream is) {
        if (is instanceof TypeCodeReader) {
            // hardly possible unless caller knows our "private" stream classes.
            if (read_value_kind((TypeCodeReader)is))
                read_value_body(is);
        } else if (is instanceof CDRInputStream) {
            WrapperInputStream wrapper = new WrapperInputStream((CDRInputStream)is);
            //if (debug) System.out.println("Created WrapperInputStream " + wrapper +
            // " with no parent");
            if (read_value_kind((TypeCodeReader)wrapper))
                read_value_body(wrapper);
        } else {
            read_value_kind(is);
            read_value_body(is);
        }
    }

    private void read_value_recursive(TypeCodeInputStream is) {
        // don't wrap a CDRInputStream reading "inner" TypeCodes.
        if (is instanceof TypeCodeReader) {
            if (read_value_kind((TypeCodeReader)is))
                read_value_body(is);
        } else {
            read_value_kind((InputStream)is);
            read_value_body(is);
        }
    }

    boolean read_value_kind(TypeCodeReader tcis)
    {
        _kind = tcis.read_long();

        // Bug fix 5034649: allow for padding that precedes the typecode kind.
        int myPosition = tcis.getTopLevelPosition()-4;

        // check validity of kind
        if ((_kind < 0 || _kind > typeTable.length) && _kind != tk_indirect) {
            throw wrapper.cannotMarshalBadTckind() ;
        }

        // Don't do any work if this is native
        if (_kind == TCKind._tk_native)
            throw wrapper.cannotMarshalNative() ;

        // We have to remember the stream and position for EVERY type code
        // in case some recursive or indirect type code references it.
        TypeCodeReader topStream = tcis.getTopLevelStream();

        if (_kind == tk_indirect) {
            int streamOffset = tcis.read_long();
            if (streamOffset > -4)
                throw wrapper.invalidIndirection( new Integer(streamOffset) ) ;

            // The encoding used for indirection is the same as that used for recursive ,
            // TypeCodes i.e., a 0xffffffff indirection marker followed by a long offset
            // (in units of octets) from the beginning of the long offset.
            int topPos = tcis.getTopLevelPosition();
            // substract 4 to get back to the beginning of the long offset.
            int indirectTypePosition = topPos - 4 + streamOffset;

            // Now we have to find the referenced type
            // by its indirectTypePosition within topStream.
            //if (debug) System.out.println(
            // "TypeCodeImpl looking up indirection at position topPos " +
            //topPos + " - 4 + offset " + streamOffset + " = " + indirectTypePosition);
            TypeCodeImpl type = topStream.getTypeCodeAtPosition(indirectTypePosition);
            if (type == null)
                throw wrapper.indirectionNotFound( new Integer(indirectTypePosition) ) ;
            setIndirectType(type);
            return false;
        }

        topStream.addTypeCodeAtPosition(this, myPosition);
        return true;
    }

    void read_value_kind(InputStream is) {
        // unmarshal the kind
        _kind = is.read_long();

        // check validity of kind
        if ((_kind < 0 || _kind > typeTable.length) && _kind != tk_indirect) {
            throw wrapper.cannotMarshalBadTckind() ;
        }
        // Don't do any work if this is native
        if (_kind == TCKind._tk_native)
            throw wrapper.cannotMarshalNative() ;

        if (_kind == tk_indirect) {
            throw wrapper.recursiveTypecodeError() ;
        }
    }

    void read_value_body(InputStream is) {
        // start unmarshaling the rest of the typecode, based on the
        // encoding (empty, simple or complex).

        switch (typeTable[_kind]) {
        case EMPTY:
            // nothing to unmarshal
            break;

        case SIMPLE:
            switch (_kind) {
            case TCKind._tk_string:
            case TCKind._tk_wstring:
                _length = is.read_long();
                break;
            case TCKind._tk_fixed:
                _digits = is.read_ushort();
                _scale = is.read_short();
                break;
            default:
                throw wrapper.invalidSimpleTypecode() ;
            }
            break;

        case COMPLEX:
            {
                TypeCodeInputStream _encap = TypeCodeInputStream.readEncapsulation(is,
                    is.orb());

                switch(_kind) {

                case TCKind._tk_objref:
                case TCKind._tk_abstract_interface:
                    {
                        // get the repository id
                        setId(_encap.read_string());
                        // get the name
                        _name = _encap.read_string();
                    }
                    break;

                case TCKind._tk_union:
                    {
                        // get the repository id
                        setId(_encap.read_string());

                        // get the name
                        _name = _encap.read_string();

                        // discriminant typecode
                        _discriminator = new TypeCodeImpl((ORB)is.orb());
                        _discriminator.read_value_recursive(_encap);

                        // default index
                        _defaultIndex = _encap.read_long();

                        // get the number of members
                        _memberCount = _encap.read_long();

                        // create arrays for the label values, names and types of members
                        _unionLabels = new AnyImpl[_memberCount];
                        _memberNames = new String[_memberCount];
                        _memberTypes = new TypeCodeImpl[_memberCount];

                        // read off label values, names and types
                        for (int i=0; i < _memberCount; i++) {
                            _unionLabels[i] = new AnyImpl((ORB)is.orb());
                            if (i == _defaultIndex)
                                // for the default case, read off the zero octet
                                _unionLabels[i].insert_octet(_encap.read_octet());
                            else {
                                switch (realType(_discriminator).kind().value()) {
                                case TCKind._tk_short:
                                    _unionLabels[i].insert_short(_encap.read_short());
                                    break;
                                case TCKind._tk_long:
                                    _unionLabels[i].insert_long(_encap.read_long());
                                    break;
                                case TCKind._tk_ushort:
                                    _unionLabels[i].insert_ushort(_encap.read_short());
                                    break;
                                case TCKind._tk_ulong:
                                    _unionLabels[i].insert_ulong(_encap.read_long());
                                    break;
                                case TCKind._tk_float:
                                    _unionLabels[i].insert_float(_encap.read_float());
                                    break;
                                case TCKind._tk_double:
                                    _unionLabels[i].insert_double(_encap.read_double());
                                    break;
                                case TCKind._tk_boolean:
                                    _unionLabels[i].insert_boolean(_encap.read_boolean());
                                    break;
                                case TCKind._tk_char:
                                    _unionLabels[i].insert_char(_encap.read_char());
                                    break;
                                case TCKind._tk_enum:
                                    _unionLabels[i].type(_discriminator);
                                    _unionLabels[i].insert_long(_encap.read_long());
                                    break;
                                case TCKind._tk_longlong:
                                    _unionLabels[i].insert_longlong(_encap.read_longlong());
                                    break;
                                case TCKind._tk_ulonglong:
                                    _unionLabels[i].insert_ulonglong(_encap.read_longlong());
                                    break;
                                    // _REVISIT_ figure out long double mapping
                                    // case TCKind.tk_longdouble:
                                    // _unionLabels[i].insert_longdouble(_encap.getDouble());
                                    // break;
                                case TCKind._tk_wchar:
                                    _unionLabels[i].insert_wchar(_encap.read_wchar());
                                    break;
                                default:
                                    throw wrapper.invalidComplexTypecode() ;
                                }
                            }
                            _memberNames[i] = _encap.read_string();
                            _memberTypes[i] = new TypeCodeImpl((ORB)is.orb());
                            _memberTypes[i].read_value_recursive(_encap);
                            _memberTypes[i].setParent(this);
                        }
                    }
                    break;

                case TCKind._tk_enum:
                    {
                        // get the repository id
                        setId(_encap.read_string());

                        // get the name
                        _name = _encap.read_string();

                        // get the number of members
                        _memberCount = _encap.read_long();

                        // create arrays for the identifier names
                        _memberNames = new String[_memberCount];

                        // read off identifier names
                        for (int i=0; i < _memberCount; i++)
                            _memberNames[i] = _encap.read_string();
                    }
                    break;

                case TCKind._tk_sequence:
                    {
                        // get the type of the sequence
                        _contentType = new TypeCodeImpl((ORB)is.orb());
                        _contentType.read_value_recursive(_encap);

                        // get the bound on the length of the sequence
                        _length = _encap.read_long();
                    }
                    break;

                case TCKind._tk_array:
                    {
                        // get the type of the array
                        _contentType = new TypeCodeImpl((ORB)is.orb());
                        _contentType.read_value_recursive(_encap);

                        // get the length of the array
                        _length = _encap.read_long();
                    }
                    break;

                case TCKind._tk_alias:
                case TCKind._tk_value_box:
                    {
                        // get the repository id
                        setId(_encap.read_string());

                        // get the name
                        _name = _encap.read_string();

                        // get the type aliased
                        _contentType = new TypeCodeImpl((ORB)is.orb());
                        _contentType.read_value_recursive(_encap);
                    }
                    break;

                case TCKind._tk_except:
                case TCKind._tk_struct:
                    {
                        // get the repository id
                        setId(_encap.read_string());

                        // get the name
                        _name = _encap.read_string();

                        // get the number of members
                        _memberCount = _encap.read_long();

                        // create arrays for the names and types of members
                        _memberNames = new String[_memberCount];
                        _memberTypes = new TypeCodeImpl[_memberCount];

                        // read off member names and types
                        for (int i=0; i < _memberCount; i++) {
                            _memberNames[i] = _encap.read_string();
                            _memberTypes[i] = new TypeCodeImpl((ORB)is.orb());
                            //if (debug) System.out.println("TypeCode " + _name +
                            // " reading member " + _memberNames[i]);
                            _memberTypes[i].read_value_recursive(_encap);
                            _memberTypes[i].setParent(this);
                        }
                    }
                    break;

                case TCKind._tk_value:
                    {
                        // get the repository id
                        setId(_encap.read_string());

                        // get the name
                        _name = _encap.read_string();

                        // get the type modifier
                        _type_modifier = _encap.read_short();

                        // get the type aliased
                        _concrete_base = new TypeCodeImpl((ORB)is.orb());
                        _concrete_base.read_value_recursive(_encap);
                        if (_concrete_base.kind().value() == TCKind._tk_null) {
                            _concrete_base = null;
                        }

                        // get the number of members
                        _memberCount = _encap.read_long();

                        // create arrays for the names, types and visibility of members
                        _memberNames = new String[_memberCount];
                        _memberTypes = new TypeCodeImpl[_memberCount];
                        _memberAccess = new short[_memberCount];

                        // read off value member visibilities
                        for (int i=0; i < _memberCount; i++) {
                            _memberNames[i] = _encap.read_string();
                            _memberTypes[i] = new TypeCodeImpl((ORB)is.orb());
                            //if (debug) System.out.println("TypeCode " + _name +
                            // " reading member " + _memberNames[i]);
                            _memberTypes[i].read_value_recursive(_encap);
                            _memberTypes[i].setParent(this);
                            _memberAccess[i] = _encap.read_short();
                        }
                    }
                    break;

                default:
                    throw wrapper.invalidTypecodeKindMarshal() ;
                }
                break;
            }
        }
    }

    public void write_value(OutputStream os) {
        // Wrap OutputStream into TypeCodeOutputStream.
        // This test shouldn't be necessary according to the Java language spec.
        if (os instanceof TypeCodeOutputStream) {
            this.write_value((TypeCodeOutputStream)os);
        } else {
            TypeCodeOutputStream wrapperOutStream = null;

            if (outBuffer == null) {
                wrapperOutStream = TypeCodeOutputStream.wrapOutputStream(os);
                this.write_value(wrapperOutStream);
                if (cachingEnabled) {
                    // Cache the buffer for repeated writes
                    outBuffer = wrapperOutStream.getTypeCodeBuffer();
                    //if (outBuffer != null)
                        //System.out.println("Caching outBuffer with length = " +
                        //outBuffer.length + " for id = " + _id);
                }
            } else {
                //System.out.println("Using cached outBuffer: length = " + outBuffer.length +
                                   //", id = " + _id);
            }
            // Write the first 4 bytes first to trigger alignment.
            // We know that it is the kind.
            if (cachingEnabled && outBuffer != null) {
                os.write_long(_kind);
                os.write_octet_array(outBuffer, 0, outBuffer.length);
            } else {
                //System.out.println("Buffer is empty for " + _id);
                wrapperOutStream.writeRawBuffer(os, _kind);
            }
        }
    }

    public void write_value(TypeCodeOutputStream tcos) {

        // Don't do any work if this is native
        if (_kind == TCKind._tk_native)
            throw wrapper.cannotMarshalNative() ;

        TypeCodeOutputStream topStream = tcos.getTopLevelStream();
        //if (debug) tcos.printBuffer();

        if (_kind == tk_indirect) {
            //if (debug) System.out.println("Writing indirection " + _name + "to " + _id);
            // The encoding used for indirection is the same as that used for recursive ,
            // TypeCodes i.e., a 0xffffffff indirection marker followed by a long offset
            // (in units of octets) from the beginning of the long offset.
            int pos = topStream.getPositionForID(_id);
            int topPos = tcos.getTopLevelPosition();
            //if (debug) System.out.println("TypeCodeImpl " + tcos +
            // " writing indirection " + _id +
                //" to position " + pos + " at position " + topPos);
            tcos.writeIndirection(tk_indirect, pos);
            // All that gets written is _kind and offset.
            return;
        }

        // The original approach changed for 5034649
        // topStream.addIDAtPosition(_id, tcos.getTopLevelPosition());

        // marshal the kind
        tcos.write_long(_kind);

        //if (debug) System.out.println("Writing " + _name + " with id " + _id);
        // We have to remember the stream and position for EVERY type code
        // in case some recursive or indirect type code references it.
        //
        // Bug fix 5034649:
        // Do this AFTER the write of the _kind in case the alignment
        // for the long changes the position.
        topStream.addIDAtPosition(_id, tcos.getTopLevelPosition()-4);

        switch (typeTable[_kind]) {
        case EMPTY:
            // nothing more to marshal
            break;

        case SIMPLE:
            switch (_kind) {
            case TCKind._tk_string:
            case TCKind._tk_wstring:
                // marshal the bound on string length
                tcos.write_long(_length);
                break;
            case TCKind._tk_fixed:
                tcos.write_ushort(_digits);
                tcos.write_short(_scale);
                break;
            default:
                // unknown typecode kind
                throw wrapper.invalidSimpleTypecode() ;
            }
            break;

        case COMPLEX:
            {
                // create an encapsulation
                TypeCodeOutputStream _encap = tcos.createEncapsulation(tcos.orb());

                switch(_kind) {

                case TCKind._tk_objref:
                case TCKind._tk_abstract_interface:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);
                    }
                    break;

                case TCKind._tk_union:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);

                        // discriminant typecode
                        _discriminator.write_value(_encap);

                        // default index
                        _encap.write_long(_defaultIndex);

                        // put the number of members
                        _encap.write_long(_memberCount);

                        // marshal label values, names and types
                        for (int i=0; i < _memberCount; i++) {

                            // for the default case, marshal the zero octet
                            if (i == _defaultIndex)
                                _encap.write_octet(_unionLabels[i].extract_octet());

                            else {
                                switch (realType(_discriminator).kind().value()) {
                                case TCKind._tk_short:
                                    _encap.write_short(_unionLabels[i].extract_short());
                                    break;
                                case TCKind._tk_long:
                                    _encap.write_long(_unionLabels[i].extract_long());
                                    break;
                                case TCKind._tk_ushort:
                                    _encap.write_short(_unionLabels[i].extract_ushort());
                                    break;
                                case TCKind._tk_ulong:
                                    _encap.write_long(_unionLabels[i].extract_ulong());
                                    break;
                                case TCKind._tk_float:
                                    _encap.write_float(_unionLabels[i].extract_float());
                                    break;
                                case TCKind._tk_double:
                                    _encap.write_double(_unionLabels[i].extract_double());
                                    break;
                                case TCKind._tk_boolean:
                                    _encap.write_boolean(_unionLabels[i].extract_boolean());
                                    break;
                                case TCKind._tk_char:
                                    _encap.write_char(_unionLabels[i].extract_char());
                                    break;
                                case TCKind._tk_enum:
                                    _encap.write_long(_unionLabels[i].extract_long());
                                    break;
                                case TCKind._tk_longlong:
                                    _encap.write_longlong(_unionLabels[i].extract_longlong());
                                    break;
                                case TCKind._tk_ulonglong:
                                    _encap.write_longlong(_unionLabels[i].extract_ulonglong());
                                    break;
                                    // _REVISIT_ figure out long double mapping
                                    // case TCKind.tk_longdouble:
                                    // _encap.putDouble(_unionLabels[i].extract_longdouble());
                                    // break;
                                case TCKind._tk_wchar:
                                    _encap.write_wchar(_unionLabels[i].extract_wchar());
                                    break;
                                default:
                                    throw wrapper.invalidComplexTypecode() ;
                                }
                            }
                            _encap.write_string(_memberNames[i]);
                            _memberTypes[i].write_value(_encap);
                        }
                    }
                    break;

                case TCKind._tk_enum:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);

                        // put the number of members
                        _encap.write_long(_memberCount);

                        // marshal identifier names
                        for (int i=0; i < _memberCount; i++)
                            _encap.write_string(_memberNames[i]);
                    }
                    break;

                case TCKind._tk_sequence:
                    {
                        // put the type of the sequence
                        lazy_content_type().write_value(_encap);

                        // put the bound on the length of the sequence
                        _encap.write_long(_length);
                    }
                    break;

                case TCKind._tk_array:
                    {
                        // put the type of the array
                        _contentType.write_value(_encap);

                        // put the length of the array
                        _encap.write_long(_length);
                    }
                    break;

                case TCKind._tk_alias:
                case TCKind._tk_value_box:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);

                        // put the type aliased
                        _contentType.write_value(_encap);
                    }
                    break;

                case TCKind._tk_struct:
                case TCKind._tk_except:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);

                        // put the number of members
                        _encap.write_long(_memberCount);

                        // marshal member names and types
                        for (int i=0; i < _memberCount; i++) {
                            _encap.write_string(_memberNames[i]);
                            //if (debug) System.out.println("TypeCode " + _name +
                            // " writing member " + _memberNames[i]);
                            _memberTypes[i].write_value(_encap);
                        }
                    }
                    break;

                case TCKind._tk_value:
                    {
                        // put the repository id
                        _encap.write_string(_id);

                        // put the name
                        _encap.write_string(_name);

                        // put the type modifier
                        _encap.write_short(_type_modifier);

                        // put the type aliased
                        if (_concrete_base == null) {
                            _orb.get_primitive_tc(TCKind._tk_null).write_value(_encap);
                        } else {
                            _concrete_base.write_value(_encap);
                        }

                        // put the number of members
                        _encap.write_long(_memberCount);

                        // marshal member names and types
                        for (int i=0; i < _memberCount; i++) {
                            _encap.write_string(_memberNames[i]);
                            //if (debug) System.out.println("TypeCode " + _name +
                            // " writing member " + _memberNames[i]);
                            _memberTypes[i].write_value(_encap);
                            _encap.write_short(_memberAccess[i]);
                        }
                    }
                    break;

                default:
                    throw wrapper.invalidTypecodeKindMarshal() ;
                }

                // marshal the encapsulation
                _encap.writeOctetSequenceTo(tcos);
                break;
            }
        }
    }

    /**
     * This is not a copy of the TypeCodeImpl objects, but instead it
     * copies the value this type code is representing.
     * See AnyImpl read_value and write_value for usage.
     * The state of this TypeCodeImpl instance isn't changed, only used
     * by the Any to do the correct copy.
     */
    protected void copy(org.omg.CORBA.portable.InputStream src,
        org.omg.CORBA.portable.OutputStream dst)
    {
        switch (_kind) {

        case TCKind._tk_null:
        case TCKind._tk_void:
        case TCKind._tk_native:
        case TCKind._tk_abstract_interface:
            break;

        case TCKind._tk_short:
        case TCKind._tk_ushort:
            dst.write_short(src.read_short());
            break;

        case TCKind._tk_long:
        case TCKind._tk_ulong:
            dst.write_long(src.read_long());
            break;

        case TCKind._tk_float:
            dst.write_float(src.read_float());
            break;

        case TCKind._tk_double:
            dst.write_double(src.read_double());
            break;

        case TCKind._tk_longlong:
        case TCKind._tk_ulonglong:
            dst.write_longlong(src.read_longlong());
            break;

        case TCKind._tk_longdouble:
            throw wrapper.tkLongDoubleNotSupported() ;

        case TCKind._tk_boolean:
            dst.write_boolean(src.read_boolean());
            break;

        case TCKind._tk_char:
            dst.write_char(src.read_char());
            break;

        case TCKind._tk_wchar:
            dst.write_wchar(src.read_wchar());
            break;

        case TCKind._tk_octet:
            dst.write_octet(src.read_octet());
            break;

        case TCKind._tk_string:
            {
                String s;
                s = src.read_string();
                // make sure length bound in typecode is not violated
                if ((_length != 0) && (s.length() > _length))
                    throw wrapper.badStringBounds( new Integer(s.length()),
                        new Integer(_length) ) ;
                dst.write_string(s);
            }
            break;

        case TCKind._tk_wstring:
            {
                String s;
                s = src.read_wstring();
                // make sure length bound in typecode is not violated
                if ((_length != 0) && (s.length() > _length))
                    throw wrapper.badStringBounds( new Integer(s.length()),
                        new Integer(_length) ) ;
                dst.write_wstring(s);
            }
            break;

        case TCKind._tk_fixed:
            {
                dst.write_ushort(src.read_ushort());
                dst.write_short(src.read_short());
            }
            break;

        case TCKind._tk_any:
            {
                //Any tmp = new AnyImpl(_orb);
                Any tmp =  ((CDRInputStream)src).orb().create_any();
                TypeCodeImpl t = new TypeCodeImpl((ORB)dst.orb());
                t.read_value((org.omg.CORBA_2_3.portable.InputStream)src);
                t.write_value((org.omg.CORBA_2_3.portable.OutputStream)dst);
                tmp.read_value(src, t);
                tmp.write_value(dst);
                break;
            }

        case TCKind._tk_TypeCode:
            {
                dst.write_TypeCode(src.read_TypeCode());
                break;
            }

        case TCKind._tk_Principal:
            {
                dst.write_Principal(src.read_Principal());
                break;
            }

        case TCKind._tk_objref:
            {
                dst.write_Object(src.read_Object());
                break;
            }

        case TCKind._tk_except:
            // Copy repositoryId
            dst.write_string(src.read_string());

            // Fall into ...
        // _REVISIT_ what about the inherited members of this values concrete base type?
        case TCKind._tk_value:
        case TCKind._tk_struct:
            {
                // copy each element, using the corresponding member type
                for (int i=0; i < _memberTypes.length; i++) {
                    _memberTypes[i].copy(src, dst);
                }
                break;
            }
        case TCKind._tk_union:
    /* _REVISIT_ More generic code?
            {
                Any discriminator = new AnyImpl(_orb);
                discriminator.read_value(src, _discriminator);
                discriminator.write_value(dst);
                int labelIndex = currentUnionMemberIndex(discriminator);
                if (labelIndex == -1) {
                    // check if label has not been found
                    if (_defaultIndex == -1)
                        // throw exception if default was not expected
                        throw new MARSHAL();
                    else
                        // must be of the default branch type
                        _memberTypes[_defaultIndex].copy(src, dst);
                } else {
                    _memberTypes[labelIndex].copy(src, dst);
                }
            }
    */
            {
                Any tagValue = new AnyImpl( (ORB)src.orb());

                switch  (realType(_discriminator).kind().value()) {
                case TCKind._tk_short:
                    {
                        short value = src.read_short();
                        tagValue.insert_short(value);
                        dst.write_short(value);
                        break;
                    }
                case TCKind._tk_long:
                    {
                        int value = src.read_long();
                        tagValue.insert_long(value);
                        dst.write_long(value);
                        break;
                    }
                case TCKind._tk_ushort:
                    {
                        short value = src.read_short();
                        tagValue.insert_ushort(value);
                        dst.write_short(value);
                        break;
                    }
                case TCKind._tk_ulong:
                    {
                        int value = src.read_long();
                        tagValue.insert_ulong(value);
                        dst.write_long(value);
                        break;
                    }
                case TCKind._tk_float:
                    {
                        float value = src.read_float();
                        tagValue.insert_float(value);
                        dst.write_float(value);
                        break;
                    }
                case TCKind._tk_double:
                    {
                        double value = src.read_double();
                        tagValue.insert_double(value);
                        dst.write_double(value);
                        break;
                    }
                case TCKind._tk_boolean:
                    {
                        boolean value = src.read_boolean();
                        tagValue.insert_boolean(value);
                        dst.write_boolean(value);
                        break;
                    }
                case TCKind._tk_char:
                    {
                        char value = src.read_char();
                        tagValue.insert_char(value);
                        dst.write_char(value);
                        break;
                    }
                case TCKind._tk_enum:
                    {
                        int value = src.read_long();
                        tagValue.type(_discriminator);
                        tagValue.insert_long(value);
                        dst.write_long(value);
                        break;
                    }
                case TCKind._tk_longlong:
                    {
                        long value = src.read_longlong();
                        tagValue.insert_longlong(value);
                        dst.write_longlong(value);
                        break;
                    }
                case TCKind._tk_ulonglong:
                    {
                        long value = src.read_longlong();
                        tagValue.insert_ulonglong(value);
                        dst.write_longlong(value);
                        break;
                    }
                    // _REVISIT_ figure out long double mapping
                    // case TCKind.tk_longdouble:
                    // {
                    // double value = src.read_double();
                    //  tagValue.insert_longdouble(value);
                    //  dst.putDouble(value);
                    //  break;
                    //}
                case TCKind._tk_wchar:
                    {
                        char value = src.read_wchar();
                        tagValue.insert_wchar(value);
                        dst.write_wchar(value);
                        break;
                    }
                default:
                    throw wrapper.illegalUnionDiscriminatorType() ;
                }

                // using the value of the tag, find out the type of the value
                // following.

                int labelIndex;
                for (labelIndex = 0; labelIndex < _unionLabels.length; labelIndex++) {
                    // use equality over anys
                    if (tagValue.equal(_unionLabels[labelIndex])) {
                        _memberTypes[labelIndex].copy(src, dst);
                        break;
                    }
                }

                if (labelIndex == _unionLabels.length) {
                    // check if label has not been found
                    if (_defaultIndex != -1)
                        // must be of the default branch type
                        _memberTypes[_defaultIndex].copy(src, dst);
                }
                break;
            }

        case TCKind._tk_enum:
            dst.write_long(src.read_long());
            break;

        case TCKind._tk_sequence:
            // get the length of the sequence
            int seqLength = src.read_long();

            // check for sequence bound violated
            if ((_length != 0) && (seqLength > _length))
                throw wrapper.badSequenceBounds( new Integer(seqLength),
                    new Integer(_length) ) ;

            // write the length of the sequence
            dst.write_long(seqLength);

            // copy each element of the seq using content type
            lazy_content_type(); // make sure it's resolved
            for (int i=0; i < seqLength; i++)
                _contentType.copy(src, dst);
            break;

        case TCKind._tk_array:
            // copy each element of the array using content type
            for (int i=0; i < _length; i++)
                _contentType.copy(src, dst);
            break;

        case TCKind._tk_alias:
        case TCKind._tk_value_box:
            // follow the alias
            _contentType.copy(src, dst);
            break;

        case tk_indirect:
            // need to follow offset, get unmarshal typecode from that
            // offset, and use that to do the copy
            // Don't need to read type code before using it to do the copy.
            // It should be fully usable.
            indirectType().copy(src, dst);
            break;

        default:
            throw wrapper.invalidTypecodeKindMarshal() ;
        }
    }


    static protected short digits(java.math.BigDecimal value) {
        if (value == null)
            return 0;
        short length = (short)value.unscaledValue().toString().length();
        if (value.signum() == -1)
            length--;
        return length;
    }

    static protected short scale(java.math.BigDecimal value) {
        if (value == null)
            return 0;
        return (short)value.scale();
    }

    // Utility methods

    // Only for union type. Returns the index of the union member
    // corresponding to the discriminator. If not found returns the
    // default index or -1 if there is no default index.
    int currentUnionMemberIndex(Any discriminatorValue) throws BadKind {
        if (_kind != TCKind._tk_union)
            throw new BadKind();

        try {
            for (int i=0; i<member_count(); i++) {
                if (member_label(i).equal(discriminatorValue)) {
                    return i;
                }
            }
            if (_defaultIndex != -1) {
                return _defaultIndex;
            }
        } catch (BadKind bad) {
        } catch (org.omg.CORBA.TypeCodePackage.Bounds bounds) {
        }
        return -1;
    }

    public String description() {
        return "TypeCodeImpl with kind " + _kind + " and id " + _id;
    }

    public String toString() {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream(1024);
        PrintStream printOut = new PrintStream(byteOut, true);
        printStream(printOut);
        return super.toString() + " =\n" + byteOut.toString();
    }

    public void printStream(PrintStream s) {
        printStream(s, 0);
    }

    private void printStream(PrintStream s, int level) {
        if (_kind == tk_indirect) {
            s.print("indirect " + _id);
            return;
        }

        switch (_kind) {
            case TCKind._tk_null:
            case TCKind._tk_void:
            case TCKind._tk_short:
            case TCKind._tk_long:
            case TCKind._tk_ushort:
            case TCKind._tk_ulong:
            case TCKind._tk_float:
            case TCKind._tk_double:
            case TCKind._tk_boolean:
            case TCKind._tk_char:
            case TCKind._tk_octet:
            case TCKind._tk_any:
            case TCKind._tk_TypeCode:
            case TCKind._tk_Principal:
            case TCKind._tk_objref:
            case TCKind._tk_longlong:
            case TCKind._tk_ulonglong:
            case TCKind._tk_longdouble:
            case TCKind._tk_wchar:
            case TCKind._tk_native:
                s.print(kindNames[_kind] + " " + _name);
                break;

            case TCKind._tk_struct:
            case TCKind._tk_except:
            case TCKind._tk_value:
                s.println(kindNames[_kind] + " " + _name + " = {");
                for(int i=0; i<_memberCount; i++) {
                    // memberName might differ from the name of the member.
                    s.print(indent(level + 1));
                    if (_memberTypes[i] != null)
                        _memberTypes[i].printStream(s, level + 1);
                    else
                        s.print("<unknown type>");
                    s.println(" " + _memberNames[i] + ";");
                }
                s.print(indent(level) + "}");
                break;

            case TCKind._tk_union:
                s.print("union " + _name + "...");
                break;

            case TCKind._tk_enum:
                s.print("enum " + _name + "...");
                break;

            case TCKind._tk_string:
                if (_length == 0)
                    s.print("unbounded string " + _name);
                else
                    s.print("bounded string(" + _length + ") " + _name);
                break;

            case TCKind._tk_sequence:
            case TCKind._tk_array:
                s.println(kindNames[_kind] + "[" + _length + "] " + _name + " = {");
                s.print(indent(level + 1));
                if (lazy_content_type() != null) {
                    lazy_content_type().printStream(s, level + 1);
                }
                s.println(indent(level) + "}");
                break;

            case TCKind._tk_alias:
                s.print("alias " + _name + " = " +
                    (_contentType != null ? _contentType._name : "<unresolved>"));
                break;

            case TCKind._tk_wstring:
                s.print("wstring[" + _length + "] " + _name);
                break;

            case TCKind._tk_fixed:
                s.print("fixed(" + _digits + ", " + _scale + ") " + _name);
                break;

            case TCKind._tk_value_box:
                s.print("valueBox " + _name + "...");
                break;

            case TCKind._tk_abstract_interface:
                s.print("abstractInterface " + _name + "...");
                break;

            default:
                s.print("<unknown type>");
                break;
        }
    }

    private String indent(int level) {
        String indent = "";
        for(int i=0; i<level; i++) {
            indent += "  ";
        }
        return indent;
    }

    protected void setCaching(boolean enableCaching) {
        cachingEnabled = enableCaching;
        if (enableCaching == false)
            outBuffer = null;
    }
}
