package jacorb.orb;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2000  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import org.omg.CORBA.TCKind;

/**
 * @author Gerald Brose, FU Berlin
 * @version $Id: TypeCode.java,v 1.9 2000/09/11 15:25:59 brose Exp $    
 */
 
public class TypeCode 
    extends org.omg.CORBA.TypeCode
{
    private static int recursion = 0;
    private int kind = -1;
    private String id = null;
    private String name = null;
    private int member_count = 0;
    private String [] member_name = null;
    private TypeCode [] member_type = null;
    private Any [] member_label = null;
    private TypeCode discriminator_type = null;
    private int default_index = -1;
    private int length = -1;
    private TypeCode content_type = null;
    private short scale;
    private short digits;

    /* if this TC is a recursive sequence.. */
    private int recursive_offset = 0;
    private TypeCode recursive_ref;

    private static boolean class_init = false;
    private static TypeCode[] primitive_tcs = new TypeCode[33];

    static
    {
        /** statically create primitive TypeCodes for fast lookup */
        for(int i = 0; i < 14; i++)
        {
            primitive_tcs[i]=new TypeCode(i);
        }   
        for(int i = 23; i < 29; i++)
        {
            primitive_tcs[i]=new TypeCode(i);
        }
    }

    /**
     * @returns TypeCode with integer kind "kind", null if not a primitive TypeCode
     */

    static TypeCode get_primitive_tc( int _kind )
    {
        return primitive_tcs[_kind];
    }

    public boolean is_primitive()
    {
        return ( primitive_tcs[kind] != null );
    }


    /* TypeCode constructors for every conceivable type follow: */


    public TypeCode( int _kind )
    {
        kind = _kind;
    }


    // tk_struct, tk_except

    public TypeCode (int _kind, java.lang.String _id, 
                     java.lang.String _name, 
                     org.omg.CORBA.StructMember[] _members) 
    {
        kind = _kind;
        id =  _id;
        name = _name.replace('.','_'); // for orbixWeb Interop
        member_count = _members.length;
        member_name = new String[member_count];
        member_type = new TypeCode[member_count];
        for( int i = 0; i < member_count; i++ )
        {
            member_name[i] = _members[i].name;
            member_type[i] = (TypeCode)_members[i].type;
        }
        
    }

    // tk_union

    public TypeCode (int _kind, java.lang.String _id, java.lang.String _name, 
                     org.omg.CORBA.TypeCode _discriminator_type, 
                     org.omg.CORBA.UnionMember[] _members) 
    {
        kind = _kind;
        id =  _id ;
        name = _name.replace('.','_'); // for orbixWeb Interop
        discriminator_type = (TypeCode)_discriminator_type;
        member_count = _members.length;
        member_name = new String[member_count];
        member_label = new Any[member_count];
        member_type = new TypeCode[member_count];
        for( int i = 0; i < member_count; i++ )
        {
            member_name[i] = _members[i].name;
            member_label[i] = (Any)_members[i].label;
            if( member_label[i].kind().equals( TCKind.tk_octet ) &&
                ((Byte)member_label[i].value()).byteValue() == (byte)0 )
                default_index = i;
            member_type[i] = (TypeCode)_members[i].type;
        }
    }

    // tk_enum

    public TypeCode (int _kind, java.lang.String _id, java.lang.String _name, 
                     java.lang.String[] _members) 
    {
        kind = _kind;
        id = _id;
        name = _name.replace('.','_'); // for orbixWeb Interop
        member_count = _members.length;
        member_name = new String[member_count];
        for( int i = 0; i < member_count; i++ ) 
        {
            member_name[i] = _members[i];
        }
    }

    // tk_alias

    public TypeCode (int _kind, java.lang.String _id, java.lang.String _name, 
                     org.omg.CORBA.TypeCode _original_type)
    { 
        id = _id;
        kind = _kind;
        if( _name != null )
            name = _name.replace('.','_'); // for orbixWeb Interop
        else
            name = null;
        content_type = (TypeCode)_original_type;
    }

    // tk_objref

    public TypeCode (int _kind,java.lang.String _id, java.lang.String _name)
    { 
        kind = _kind;
        id = _id;
        name = _name.replace('.','_'); // for orbixWeb Interop
    }

    // tk_string

    public TypeCode (int _kind, int _bound) 
    {
        kind = _kind;
        length = _bound;
    }

    // tk_sequence, tk_array

    public TypeCode (int _kind,int _bound, 
                     org.omg.CORBA.TypeCode _element_type) 
    {
        kind = _kind;
        length = _bound;
        content_type = (TypeCode)_element_type;
    }

    // recursive sequence

    public TypeCode (int _kind, int _bound, int offset) 
    {
        if( _kind != TCKind._tk_sequence )
            throw new RuntimeException("Not a sequence");
        kind = _kind;
        length = _bound;
        recursive_offset = offset;
        content_type = new TypeCode( 0xffffffff );
        recursion++;
    }

    // fixed

    public TypeCode (int _kind, short _digits, short _scale) 
    {
        if( _kind != TCKind._tk_fixed )
            throw new RuntimeException("Not a fixed");
        kind = _kind;
        digits = _digits;
        scale = _scale;
    }

    public void resolve_recursion(int offset, TypeCode tc)
    {
        if( recursion == 0 )
            return;
        try
        {
            switch ( _kind() )
            {
            case TCKind._tk_sequence:
            case TCKind._tk_array:
                if( offset == recursive_offset )
                {
                    recursive_ref = tc;
                    recursion--;
                    return;
                } 
                else
                    if( recursion > 0 )
                        ((jacorb.orb.TypeCode)content_type()).resolve_recursion(offset, tc);
                break;
            case TCKind._tk_struct:
            case TCKind._tk_except:
            case TCKind._tk_union:
                for( int i = 0; i < member_count(); i++ )
                    ((jacorb.orb.TypeCode)member_type(i)).resolve_recursion(offset+1, tc);
                break;
            default:
                return;
            }
        }
        catch( org.omg.CORBA.TypeCodePackage.BadKind bk )
        {
            bk.printStackTrace();
        }
        catch( org.omg.CORBA.TypeCodePackage.Bounds b )
        {
            b.printStackTrace();
        }
    }


    /**
     * check TypeCodes for structural equality
     */

    public boolean equal( org.omg.CORBA.TypeCode tc)
    {
        jacorb.util.Debug.output( 4, "Comparing this " + kind().value() + 
                                  " with tc " + tc.kind().value());

        if( kind().value() != tc.kind().value())
            return false;

        /** primitive type codes, only their kinds need be equal */

        if( kind().value() < 14 ||
            ( kind().value() > 22 && kind().value() < 29 ))
        {
            return true;
        }

        /* compare repository ids */

        try 
        {
            if( kind ==  TCKind._tk_array )
            {
                return ( length() == tc.length() && 
                         content_type().equal( tc.content_type()));
            }

            if( kind == TCKind._tk_sequence )
            {
                TypeCode this_tc = (jacorb.orb.TypeCode)content_type();
                TypeCode other_tc = (jacorb.orb.TypeCode)tc.content_type();
                
                if( this_tc.is_recursive() )
                {
                    this_tc = (jacorb.orb.TypeCode)get_recursive_reference();
                }

                if( other_tc.is_recursive())
                {
                    other_tc = (jacorb.orb.TypeCode)((jacorb.orb.TypeCode)tc).get_recursive_reference();
                }

                return ( length() == tc.length() && 
                         this_tc.equal( other_tc ));
            }

            if( kind == TCKind._tk_objref  || kind == TCKind._tk_struct || 
                kind == TCKind._tk_union || kind == TCKind._tk_enum || 
                kind == TCKind._tk_alias  || kind ==  TCKind._tk_except)
            {
                if( ! id().equals( tc.id()) )
                    return false;
            }
            
            if( kind == TCKind._tk_union )
            {
                if( !discriminator_type().equal( tc.discriminator_type()))
                    return false;
        
                if( default_index() != tc.default_index())
                    return false;
                
                if( member_count() != tc.member_count())
                    return false;

                for( int i = 0; i < member_count(); i++ )
                {
                    if( ! member_type(i).equal(tc.member_type(i)))
                        return false;

                    if( ! member_label(i).equals( tc.member_label(i)))
                        return false;
                }
            }

        }
        catch( org.omg.CORBA.TypeCodePackage.Bounds bs)
        {
            bs.printStackTrace();
            return false;
        }
        catch( org.omg.CORBA.TypeCodePackage.BadKind bk)
        {
            bk.printStackTrace();
            return false;
        }
        return true;
    }


    public org.omg.CORBA.TCKind kind()
    {
        jacorb.util.Debug.output(4,"Type.kind: " + kind  );
        return org.omg.CORBA.TCKind.from_int(kind);
    }


    public int _kind()
    {
        return kind;
    }

    public java.lang.String id() 
        throws org.omg.CORBA.TypeCodePackage.BadKind 
    {
        switch( kind )
        {
        case   TCKind._tk_objref:
        case   TCKind._tk_struct:
        case   TCKind._tk_union:
        case   TCKind._tk_enum:
        case   TCKind._tk_alias:
        case   TCKind._tk_except : return id;
        default:  throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }
    }

    public java.lang.String name() 
        throws org.omg.CORBA.TypeCodePackage.BadKind 
    {
        switch( kind )
        {
        case   TCKind._tk_objref:
        case   TCKind._tk_struct:
        case   TCKind._tk_union:
        case   TCKind._tk_enum:
        case   TCKind._tk_alias:
        case   TCKind._tk_except : return name;
        default:  throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }
    }

    public int member_count() 
        throws org.omg.CORBA.TypeCodePackage.BadKind 
    {
        switch( kind )
        {
        case   TCKind._tk_struct:
        case   TCKind._tk_union:
        case   TCKind._tk_enum : return member_count;
        default:  throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }
    }

    public java.lang.String member_name(int index) 
        throws org.omg.CORBA.TypeCodePackage.BadKind,
        org.omg.CORBA.TypeCodePackage.Bounds 
    {
        switch( kind )
        {
        case   TCKind._tk_struct:
        case  TCKind._tk_union:
        case  TCKind._tk_enum : 
            if( index <= member_count )
                return member_name[index];
            else
                throw new  org.omg.CORBA.TypeCodePackage.Bounds();
        default:  throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }

    }

    public org.omg.CORBA.TypeCode member_type(int index) 
        throws org.omg.CORBA.TypeCodePackage.BadKind,
        org.omg.CORBA.TypeCodePackage.Bounds
    {
        if( kind != TCKind._tk_struct && kind != TCKind._tk_union )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        if( index > member_count )
            throw new  org.omg.CORBA.TypeCodePackage.Bounds();
        return member_type[index];
    }

    public org.omg.CORBA.Any member_label(int index) 
        throws org.omg.CORBA.TypeCodePackage.BadKind,  
        org.omg.CORBA.TypeCodePackage.Bounds
    {
        if( kind != TCKind._tk_struct && kind != TCKind._tk_union )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        if( index > member_count )
            throw new  org.omg.CORBA.TypeCodePackage.Bounds();
        return member_label[index];
    }

    public org.omg.CORBA.TypeCode discriminator_type() 
        throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        if( kind != TCKind._tk_struct && kind != TCKind._tk_union )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        return discriminator_type;
    }


    public int default_index() 
        throws org.omg.CORBA.TypeCodePackage.BadKind 
    {
        if( kind != TCKind._tk_struct && kind != TCKind._tk_union )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        return default_index;
    }


    public int length() 
        throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        switch( kind )
        {
        case   TCKind._tk_string:
        case   TCKind._tk_sequence:
        case   TCKind._tk_array : return length;
        default: throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }
    }

    public org.omg.CORBA.TypeCode content_type() 
        throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        switch( kind )
        {
        case   TCKind._tk_array :
        case   TCKind._tk_sequence :
        case   TCKind._tk_alias : 
            return content_type;
        default: throw new org.omg.CORBA.TypeCodePackage.BadKind();
        }
    }

    public  short fixed_digits() throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        if( kind != TCKind._tk_fixed )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        return digits;
    }

    public  short fixed_scale() throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        if( kind != TCKind._tk_fixed )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();    
        return scale;
    }


    public boolean equivalent(org.omg.CORBA.TypeCode tc)
    {
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    public org.omg.CORBA.TypeCode get_compact_typecode()
    {
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    // useful additional functionality

    public void toSequence()
    {
        kind = TCKind._tk_sequence;
    }

    public String toString()
    {
        StringBuffer sb = new StringBuffer();
        sb.append("kind : " + kind + "\n");
        try
        {
            sb.append("name : " + name + "\n");
        } 
        catch ( Exception e ){}

        try
        {
            sb.append("id : " + id + "\n");
        } 
        catch ( Exception e ){}

        return sb.toString();
    }

    public int recursive_offset()
    {
        return recursive_offset;
    }

    /** used by the receiving side as a hash index */

    public void set_recursive_offset(int off)
    {
        recursive_offset = off;
    }

    public org.omg.CORBA.TypeCode get_recursive_reference()
    {
        return recursive_ref;
    }

    public boolean is_recursive()
    {
        return kind == 0xffffffff;
    }

    public void unrecurse(org.omg.CORBA.TypeCode _tc)
        throws org.omg.CORBA.TypeCodePackage.BadKind
    {
        if( kind != TCKind._tk_sequence && !content_type.is_recursive() )
            throw new org.omg.CORBA.TypeCodePackage.BadKind();
        content_type = (jacorb.orb.TypeCode)_tc;
    }

        

}

