package jacorb.orb;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-98  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.6 2000/03/11 13:09:32 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

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

    // 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;
    }

	

}



