/*
 * orbit-imodule-utils.c:
 *
 * Copyright (C) 2002 Sun Microsystems, Inc.
 *                    Red Hat, Inc.
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors:
 *     Mark McLoughlin <mark@skynet.ie>
 *     Elliot Lee <sopwith@redhat.com
 */

#include <string.h>

#include <orbit/orbit.h>

#include "orbit-imodule-utils.h"
#include "orbit-imodule-libidl-utils.h"

static CORBA_StructMemberSeq *
ORBit_imodule_get_struct_members (GHashTable        *typecodes,
				  IDL_tree           tree,
				  CORBA_Environment *ev)
{
	CORBA_StructMemberSeq *members;
	IDL_tree               l;
	int                    num_members = 0;
	int                    i;

	g_return_val_if_fail (IDL_NODE_TYPE (tree) == IDLN_TYPE_STRUCT ||
			      IDL_NODE_TYPE (tree) == IDLN_EXCEPT_DCL, NULL);

	for (l = IDL_TYPE_STRUCT (tree).member_list; l; l = IDL_LIST (l).next)
		num_members += IDL_list_length (IDL_MEMBER (IDL_LIST (l).data).dcls);

	members = CORBA_StructMemberSeq__alloc ();

	members->_length  = members->_maximum = num_members;
	members->_buffer  = CORBA_StructMemberSeq_allocbuf (members->_length);
	members->_release = CORBA_TRUE;

	for (i = 0, l = IDL_TYPE_STRUCT (tree).member_list; l; l = IDL_LIST (l).next) {
		CORBA_TypeCode subtc;
		IDL_tree       dcl;

		subtc = ORBit_imodule_get_typecode (
				typecodes, IDL_MEMBER (IDL_LIST (l).data).type_spec);

		for (dcl = IDL_MEMBER (IDL_LIST (l).data).dcls; dcl;
		     dcl = IDL_LIST (dcl).next, i++) {
			CORBA_StructMember *member = &members->_buffer [i];
			CORBA_string        name;

			if (IDL_NODE_TYPE (dcl) == IDLN_IDENT)
				name = IDL_IDENT (dcl).str;
			else /* IDLN_TYPE_ARRAY */
				name = IDL_IDENT (IDL_TYPE_ARRAY (dcl).ident).str;

			member->name     = CORBA_string_dup (name);
			member->type     = (CORBA_TypeCode)
						CORBA_Object_duplicate ((CORBA_Object) subtc, ev);
			member->type_def = CORBA_OBJECT_NIL; /* Not used? */
		}

		CORBA_Object_release ((CORBA_Object) subtc, ev);
	}

	g_assert (i == num_members);

	return members;
}

static void
ORBit_imodule_jam_int (IDL_tree src, CORBA_TypeCode tc, gpointer dest)
{
	CORBA_long val = 0;

	switch (IDL_NODE_TYPE (src)) {
	case IDLN_BOOLEAN:
		val = IDL_BOOLEAN (src).value ? 1 : 0;
		break;
	case IDLN_CHAR:
		val = IDL_CHAR (src).value [0];
		break;
	case IDLN_INTEGER:
		val = IDL_INTEGER (src).value;
		break;
	default:
		g_assert_not_reached ();
		break;
	}

	switch (tc->kind) {
	case CORBA_tk_boolean:
	case CORBA_tk_char:
	case CORBA_tk_octet:
		*(CORBA_boolean *) dest = val;
		break;
	case CORBA_tk_short:
	case CORBA_tk_ushort:
		*(CORBA_short *) dest = val;
		break;
	case CORBA_tk_long:
	case CORBA_tk_ulong:
		*(CORBA_long *) dest = val;
		break;
	default:
		g_assert_not_reached ();
		break;
	}
}

static void
ORBit_imodule_setup_label_any (CORBA_TypeCode  tc,
			       IDL_tree        node,
			       CORBA_any      *label)
{
	if (!node) { /* default case */
		label->_type = TC_CORBA_octet;
		label->_value = ORBit_small_alloc (TC_CORBA_octet);
		*(CORBA_octet *) label->_value = -1;
		return;
	}

	label->_type = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) tc, NULL);
	label->_value = ORBit_small_alloc (tc);

	switch (IDL_NODE_TYPE (node)) {
	case IDLN_BOOLEAN:
	case IDLN_CHAR:
	case IDLN_INTEGER:
		ORBit_imodule_jam_int (node, tc, label->_value);
		break;

	case IDLN_FLOAT:
		g_assert (tc->kind == CORBA_tk_float);
		*(CORBA_float *) label->_value = IDL_FLOAT (node).value;
		break;
	case IDLN_BINOP:  /* drop through */
	case IDLN_UNARYOP: {
		IDL_tree val;

		if (IDL_NODE_TYPE (node) == IDLN_BINOP)
			val = _IDL_binop_eval (IDL_BINOP (node).op,
					       IDL_BINOP (node).left,
					       IDL_BINOP (node).right);
		else
			val = _IDL_unaryop_eval (IDL_BINOP (node).op,
					       IDL_UNARYOP (node).operand);

		ORBit_imodule_jam_int (val, tc, label->_value);
		IDL_tree_free (val);
		break;
	}
	case IDLN_IDENT: {
		CORBA_long val;

		g_assert (label->_type->kind == CORBA_tk_enum);
		for (val = 0; val < label->_type->sub_parts; val++)
			if (!strcmp (IDL_IDENT (node).str, label->_type->subnames [val]))
				break;
		g_assert (val < label->_type->sub_parts);
		*(CORBA_long *) label->_value = val;
		break;
	}
	default:
		g_assert_not_reached ();
		break;
	}
}

static CORBA_UnionMemberSeq *
ORBit_imodule_get_union_members (GHashTable        *typecodes,
				 IDL_tree           tree,
				 CORBA_TypeCode     switchtc,
				 CORBA_Environment *ev)
{
	CORBA_UnionMemberSeq *members;
	IDL_tree              l;
	int                   num_members = 0;
	int                   i;

	g_return_val_if_fail (IDL_NODE_TYPE (tree) == IDLN_TYPE_UNION, NULL);

	for (l = IDL_TYPE_UNION (tree).switch_body; l; l = IDL_LIST (l).next)
		num_members += IDL_list_length (IDL_CASE_STMT (IDL_LIST (l).data).labels);

	members = CORBA_UnionMemberSeq__alloc ();

	members->_length  = members->_maximum = num_members;
	members->_buffer  = CORBA_UnionMemberSeq_allocbuf (members->_length);
	members->_release = CORBA_TRUE;

	for (i = 0, l = IDL_TYPE_UNION (tree).switch_body; l; l = IDL_LIST (l).next) {
		CORBA_TypeCode subtc;
		IDL_tree       member, label, dcl;

		member = IDL_CASE_STMT (IDL_LIST (l).data).element_spec;
		g_assert (IDL_NODE_TYPE (member) == IDLN_MEMBER);

		subtc = ORBit_imodule_get_typecode (
				typecodes, IDL_MEMBER (member).type_spec);
		dcl = IDL_LIST (IDL_MEMBER (member).dcls).data;

		for (label = IDL_CASE_STMT (IDL_LIST (l).data).labels; label;
		     label = IDL_LIST (label).next, i++) {
			CORBA_UnionMember *umember = &members->_buffer [i];

			ORBit_imodule_setup_label_any (
				switchtc, IDL_LIST (label).data, &umember->label);

			umember->label._release = CORBA_TRUE;

			umember->name     = CORBA_string_dup (IDL_IDENT (dcl).str);
			umember->type     = (CORBA_TypeCode)
						CORBA_Object_duplicate ((CORBA_Object) subtc, ev);
			umember->type_def = CORBA_OBJECT_NIL; /* Not used? */
		}

		CORBA_Object_release ((CORBA_Object) subtc, ev);
	}

	g_assert (i == num_members);

	return members;
}

static CORBA_EnumMemberSeq *
ORBit_imodule_get_enum_members (IDL_tree           tree,
				CORBA_Environment *ev)
{
	CORBA_EnumMemberSeq *members;
	IDL_tree             l;
	int                  num_members = 0;
	int                  i;

	g_return_val_if_fail (IDL_NODE_TYPE (tree) == IDLN_TYPE_ENUM, NULL);

	num_members = IDL_list_length (IDL_TYPE_ENUM (tree).enumerator_list);

	members = CORBA_EnumMemberSeq__alloc ();

	members->_length  = members->_maximum = num_members;
	members->_buffer  = CORBA_EnumMemberSeq_allocbuf (members->_length);
	members->_release = CORBA_TRUE;

	for (i = 0, l = IDL_TYPE_ENUM (tree).enumerator_list; l; i++, l = IDL_LIST (l).next)
		members->_buffer [i] = CORBA_string_dup (IDL_IDENT (IDL_LIST (l).data).str);

	g_assert (i == num_members);

	return members;
}

static CORBA_TypeCode
ORBit_imodule_lookup_typecode (GHashTable *typecodes,
			       const char *repo_id)
{
	if (!typecodes)
		return CORBA_OBJECT_NIL;

	return (CORBA_TypeCode) CORBA_Object_duplicate (
					g_hash_table_lookup (typecodes, repo_id),
					NULL);
}

static void
ORBit_imodule_register_typecode (GHashTable     *typecodes,
				 const char     *repo_id,
				 CORBA_TypeCode  tc)
{
	g_return_if_fail (g_hash_table_lookup (typecodes, repo_id) == NULL);

	g_hash_table_insert (
		typecodes, g_strdup (repo_id),
		CORBA_Object_duplicate ((CORBA_Object) tc, NULL));
}

GHashTable *
ORBit_imodule_new_typecodes (void)
{
	return g_hash_table_new_full (
			g_str_hash, g_str_equal,
			g_free,
			(GDestroyNotify) CORBA_Object_release);
}

void
ORBit_imodule_free_typecodes (GHashTable *typecodes)
{
	g_hash_table_destroy (typecodes);
}

typedef struct {
	CORBA_sequence_CORBA_TypeCode *sequence;
	int                            iter;
} TypecodesHashIter;

static void
typecodes_hash_foreach (const char        *repo_id,
			CORBA_TypeCode     tc,
			TypecodesHashIter *iter)
{
	g_assert (iter->iter < iter->sequence->_length);

	iter->sequence->_buffer [iter->iter++] =
		(CORBA_TypeCode) CORBA_Object_duplicate ((CORBA_Object) tc, NULL);
}

CORBA_sequence_CORBA_TypeCode *
ORBit_imodule_get_typecodes_seq (GHashTable *typecodes)
{
	CORBA_sequence_CORBA_TypeCode *retval;
	TypecodesHashIter              iter;

	retval           = CORBA_sequence_CORBA_TypeCode__alloc ();
	retval->_length  = retval->_maximum = g_hash_table_size (typecodes);
	retval->_release = TRUE;
	retval->_buffer  = CORBA_sequence_CORBA_TypeCode_allocbuf (retval->_length);

	iter.sequence = retval;
	iter.iter     = 0;

	g_hash_table_foreach (typecodes, (GHFunc) typecodes_hash_foreach, &iter);

	g_assert (iter.iter == retval->_length);

	return retval;
}

IDL_tree
ORBit_imodule_get_typespec (IDL_tree tree)
{
	IDL_tree retval = NULL;

	if (!tree)
		return NULL;

	switch (IDL_NODE_TYPE (tree)) {
	case IDLN_TYPE_INTEGER:
	case IDLN_TYPE_FLOAT:
	case IDLN_TYPE_FIXED:
	case IDLN_TYPE_CHAR:
	case IDLN_TYPE_WIDE_CHAR:
	case IDLN_TYPE_STRING:
	case IDLN_TYPE_WIDE_STRING:
	case IDLN_TYPE_BOOLEAN:
	case IDLN_TYPE_OCTET:
	case IDLN_TYPE_ANY:
	case IDLN_TYPE_OBJECT:
	case IDLN_TYPE_ENUM:
	case IDLN_TYPE_SEQUENCE:
	case IDLN_TYPE_ARRAY:
	case IDLN_TYPE_STRUCT:
	case IDLN_TYPE_UNION:
	case IDLN_EXCEPT_DCL:
	case IDLN_FORWARD_DCL:
	case IDLN_INTERFACE:
	case IDLN_NATIVE:
 	case IDLN_TYPE_TYPECODE:
		retval = tree;
		break;
	case IDLN_TYPE_DCL:
		retval = ORBit_imodule_get_typespec (
				IDL_TYPE_DCL (tree).type_spec);
		break;
	case IDLN_PARAM_DCL:
		retval = ORBit_imodule_get_typespec (
				IDL_PARAM_DCL (tree).param_type_spec);
		break;
	case IDLN_MEMBER:
		retval = ORBit_imodule_get_typespec (
				IDL_MEMBER (tree).type_spec);
		break;
	case IDLN_LIST:
	case IDLN_IDENT:
		retval = ORBit_imodule_get_typespec (
				IDL_get_parent_node (tree, IDLN_ANY, NULL));
		break;
	default:
		g_error ("Cannot get typespec for %s",
				IDL_tree_type_names [IDL_NODE_TYPE (tree)]);
		break;
	}

	return retval;
}

static int
ORBit_imodule_find_c_align (IDL_tree node)
{
	int c_align = 1;

	node = ORBit_imodule_get_typespec (node);	

	switch (IDL_NODE_TYPE (node)) {
	case IDLN_TYPE_INTEGER:
		switch (IDL_TYPE_INTEGER (node).f_type) {
		case IDL_INTEGER_TYPE_SHORT:
			c_align = ORBIT_ALIGNOF_CORBA_SHORT;
			break;
		case IDL_INTEGER_TYPE_LONG:
			c_align = ORBIT_ALIGNOF_CORBA_LONG;
			break;
		case IDL_INTEGER_TYPE_LONGLONG:
			c_align = ORBIT_ALIGNOF_CORBA_LONG_LONG;
			break;
		}
		break;
	case IDLN_TYPE_FLOAT:
		switch (IDL_TYPE_FLOAT (node).f_type) {
		case IDL_FLOAT_TYPE_FLOAT:
			c_align = ORBIT_ALIGNOF_CORBA_FLOAT;
			break;
		case IDL_FLOAT_TYPE_DOUBLE:
			c_align = ORBIT_ALIGNOF_CORBA_DOUBLE;
			break;
		case IDL_FLOAT_TYPE_LONGDOUBLE:
			c_align = ORBIT_ALIGNOF_CORBA_LONG_DOUBLE;
			break;
		}
		break;
	case IDLN_TYPE_ENUM:
		c_align = ORBIT_ALIGNOF_CORBA_LONG;
		break;
	case IDLN_TYPE_CHAR: /* drop through */
	case IDLN_TYPE_BOOLEAN:
	case IDLN_TYPE_OCTET:
		c_align = ORBIT_ALIGNOF_CORBA_CHAR;
		break;
	case IDLN_TYPE_WIDE_CHAR:
		c_align = ORBIT_ALIGNOF_CORBA_SHORT;
		break;
	case IDLN_TYPE_UNION: {
		IDL_tree l = IDL_TYPE_UNION (node).switch_body;

		c_align = ORBIT_ALIGNOF_CORBA_STRUCT;

		for (; l; l = IDL_LIST (l).next) {
			IDL_tree subtype = IDL_MEMBER (IDL_CASE_STMT (
					IDL_LIST (l).data).element_spec).type_spec;

			c_align = MAX (c_align, ORBit_imodule_find_c_align (subtype));
		}
		}
		break;
	case IDLN_EXCEPT_DCL: /* drop through */
	case IDLN_TYPE_STRUCT: {
		IDL_tree l = IDL_TYPE_STRUCT (node).member_list;
					
		for (; l; l = IDL_LIST (l).next) {
			IDL_tree member = IDL_MEMBER (IDL_LIST (l).data).type_spec;

			c_align = MAX (c_align, ORBit_imodule_find_c_align (member));
		}
		}
		break;
	case IDLN_TYPE_STRING: /* drop through */
	case IDLN_TYPE_WIDE_STRING:
	case IDLN_TYPE_OBJECT:
	case IDLN_TYPE_TYPECODE:
	case IDLN_FORWARD_DCL:
	case IDLN_INTERFACE:
		c_align = ORBIT_ALIGNOF_CORBA_POINTER;
		break;
	case IDLN_TYPE_ARRAY: {
		IDL_tree subtype;

		subtype = IDL_TYPE_DCL (
				IDL_get_parent_node (
					node, IDLN_TYPE_DCL, NULL)).type_spec;

		c_align = ORBit_imodule_find_c_align (subtype);
		}
		break;
	case IDLN_TYPE_SEQUENCE:
		c_align = MAX (MAX (ORBIT_ALIGNOF_CORBA_STRUCT,
				    ORBIT_ALIGNOF_CORBA_LONG),
				    ORBIT_ALIGNOF_CORBA_POINTER);
		break;
	case IDLN_TYPE_ANY:
		c_align =  MAX (ORBIT_ALIGNOF_CORBA_STRUCT,
			        ORBIT_ALIGNOF_CORBA_POINTER);
		break;
	default:
		g_error ("Can't find alignment %s\n", 
			 IDL_tree_type_names [IDL_NODE_TYPE (node)]);
		break;
	}

	return c_align;
}

CORBA_TypeCode
ORBit_imodule_get_typecode (GHashTable *typecodes,
			    IDL_tree    tree)
{
	CORBA_Environment env;
	CORBA_TypeCode    retval = CORBA_OBJECT_NIL;

	if (!tree)
		return CORBA_OBJECT_NIL;

	CORBA_exception_init (&env);

	switch (IDL_NODE_TYPE (tree)) {
	case IDLN_MEMBER:
		retval = ORBit_imodule_get_typecode (
				typecodes, IDL_MEMBER (tree).type_spec);
		break;
	case IDLN_TYPE_ANY:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_any, NULL);
		break;
	case IDLN_TYPE_FLOAT:
		switch (IDL_TYPE_FLOAT (tree).f_type) {
		case IDL_FLOAT_TYPE_FLOAT:
			retval = TC_CORBA_float;
			break;
		case IDL_FLOAT_TYPE_DOUBLE:
			retval = TC_CORBA_double;
			break;
		case IDL_FLOAT_TYPE_LONGDOUBLE:
			retval = TC_CORBA_long_double;
			break;
		}
		break;
	case IDLN_TYPE_FIXED:
		retval = CORBA_ORB_create_fixed_tc (NULL, 
				IDL_INTEGER (IDL_TYPE_FIXED (tree).positive_int_const).value,
				IDL_INTEGER (IDL_TYPE_FIXED (tree).integer_lit).value, &env);
		break;
	case IDLN_TYPE_INTEGER:
		if (!IDL_TYPE_INTEGER (tree).f_signed)
			switch (IDL_TYPE_INTEGER (tree).f_type) {
			case IDL_INTEGER_TYPE_SHORT:
				retval = TC_CORBA_unsigned_short;
				break;
			case IDL_INTEGER_TYPE_LONGLONG:
				retval = TC_CORBA_unsigned_long_long;
				break;
			case IDL_INTEGER_TYPE_LONG:
				retval = TC_CORBA_unsigned_long;
				break;
			default:
				g_assert_not_reached ();
			}
		else
			switch (IDL_TYPE_INTEGER (tree).f_type) {
			case IDL_INTEGER_TYPE_SHORT:
				retval = TC_CORBA_short;
				break;
			case IDL_INTEGER_TYPE_LONGLONG:
				retval = TC_CORBA_long_long;
				break;
			case IDL_INTEGER_TYPE_LONG:
				retval = TC_CORBA_long;
				break;
			default:
				g_assert_not_reached ();
			}
		break;
	case IDLN_TYPE_STRING:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_string, NULL);
		break;
	case IDLN_TYPE_WIDE_STRING:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_wstring, NULL);
		break;
	case IDLN_TYPE_OCTET:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_octet, NULL);
		break;
	case IDLN_TYPE_CHAR:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_char, NULL);
		break;
	case IDLN_TYPE_WIDE_CHAR:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_wchar, NULL);
		break;
	case IDLN_TYPE_BOOLEAN:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_boolean, NULL);
		break;
  	case IDLN_TYPE_STRUCT:
		retval = ORBit_imodule_lookup_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_STRUCT (tree).ident).repo_id);

		if (!retval) {
			CORBA_StructMemberSeq *members;

			members = ORBit_imodule_get_struct_members (
						typecodes, tree, &env);

			retval = CORBA_ORB_create_struct_tc (NULL,
					IDL_IDENT (IDL_TYPE_STRUCT (tree).ident).repo_id,
					IDL_IDENT (IDL_TYPE_STRUCT (tree).ident).str,
					members, &env);

			ORBit_imodule_register_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_STRUCT (tree).ident).repo_id, retval);

			CORBA_free (members);
		}
		break;
	case IDLN_EXCEPT_DCL:
		retval = ORBit_imodule_lookup_typecode (
				typecodes,
				IDL_IDENT (IDL_EXCEPT_DCL (tree).ident).repo_id);

		if (!retval) {
			CORBA_StructMemberSeq *members;

			members = ORBit_imodule_get_struct_members (
						typecodes, tree, &env);

			retval = CORBA_ORB_create_exception_tc (NULL,
					IDL_IDENT (IDL_EXCEPT_DCL (tree).ident).repo_id,
					IDL_IDENT (IDL_EXCEPT_DCL (tree).ident).str,
					members, &env);

			ORBit_imodule_register_typecode (
				typecodes,
				IDL_IDENT (IDL_EXCEPT_DCL (tree).ident).repo_id, retval);

			CORBA_free (members);
		}
		break;
	case IDLN_TYPE_ARRAY: {
		CORBA_TypeCode subtc;
		IDL_tree       sizer;
		IDL_tree       type_dcl;

		sizer = IDL_list_nth (IDL_TYPE_ARRAY (tree).size_list,
				      IDL_list_length (IDL_TYPE_ARRAY (tree).size_list) - 1);
		g_assert (IDL_NODE_TYPE (IDL_LIST (sizer).data) == IDLN_INTEGER);

		type_dcl = IDL_NODE_UP (IDL_NODE_UP (tree));
		g_assert (IDL_NODE_TYPE (type_dcl) == IDLN_TYPE_DCL);

		subtc = ORBit_imodule_get_typecode (
				typecodes, IDL_TYPE_DCL (type_dcl).type_spec),
		retval = CORBA_ORB_create_array_tc (NULL,
				IDL_INTEGER (IDL_LIST (sizer).data).value,
				subtc, &env);
		retval->c_align = subtc->c_align;
		CORBA_Object_release ((CORBA_Object) subtc, NULL);

		for (sizer = IDL_LIST (sizer).prev; sizer; sizer = IDL_LIST (sizer).prev) {
			subtc = retval;
			retval = CORBA_ORB_create_array_tc (NULL,
					IDL_INTEGER (IDL_LIST (sizer).data).value,
					subtc, &env);
			retval->c_align = subtc->c_align;
			CORBA_Object_release ((CORBA_Object) subtc, NULL);
		}

		subtc = retval;
		retval = ORBit_imodule_create_alias_typecode (
				typecodes, IDL_TYPE_ARRAY (tree).ident, subtc);
		CORBA_Object_release ((CORBA_Object) subtc, NULL);
		}
		break;
	case IDLN_TYPE_UNION:
		retval = ORBit_imodule_lookup_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_UNION (tree).ident).repo_id);
		
		if (!retval) {
			CORBA_UnionMemberSeq *members;
			CORBA_TypeCode        switchtc;

			switchtc = ORBit_imodule_get_typecode (
					typecodes, IDL_TYPE_UNION (tree).switch_type_spec);

			members = ORBit_imodule_get_union_members (
						typecodes, tree, switchtc, &env);
			retval = CORBA_ORB_create_union_tc (NULL,
					IDL_IDENT (IDL_TYPE_UNION (tree).ident).repo_id,
					IDL_IDENT (IDL_TYPE_UNION (tree).ident).str,
					switchtc, members, &env);

			CORBA_Object_release ((CORBA_Object) switchtc, NULL);

			ORBit_imodule_register_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_UNION (tree).ident).repo_id, retval);

			CORBA_free (members);
		}
		break;
	case IDLN_TYPE_ENUM:
		retval = ORBit_imodule_lookup_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id);
		if (!retval) {
			CORBA_EnumMemberSeq *members;

			members = ORBit_imodule_get_enum_members (tree, &env);

			retval = CORBA_ORB_create_enum_tc (NULL,
					IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id,
					IDL_IDENT (IDL_TYPE_ENUM (tree).ident).str,
					members, &env);

			ORBit_imodule_register_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id, retval);

			CORBA_free (members);
		}
		break;
	case IDLN_IDENT:
		retval = ORBit_imodule_lookup_typecode (
				typecodes, IDL_IDENT (tree).repo_id);
		g_assert (retval != NULL);
		break;
	case IDLN_TYPE_SEQUENCE: {
		CORBA_TypeCode subtc;
		int            bound = 0;

		if (IDL_TYPE_SEQUENCE (tree).positive_int_const)
			bound = IDL_INTEGER (IDL_TYPE_SEQUENCE (tree).positive_int_const).value;

		subtc = ORBit_imodule_get_typecode (
				typecodes, IDL_TYPE_SEQUENCE (tree).simple_type_spec),
		retval = CORBA_ORB_create_sequence_tc (NULL,
				bound, subtc, &env);
		CORBA_Object_release ((CORBA_Object) subtc, NULL);
		/*
		 * FIXME: and what about recursive sequences?
		 */
		}
		break;
	case IDLN_FORWARD_DCL:
	case IDLN_INTERFACE:
		retval = ORBit_imodule_lookup_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id);
		if (!retval) {
			retval = CORBA_ORB_create_interface_tc (NULL,
					IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id,
					IDL_IDENT (IDL_TYPE_ENUM (tree).ident).str,
					&env);

			ORBit_imodule_register_typecode (
				typecodes,
				IDL_IDENT (IDL_TYPE_ENUM (tree).ident).repo_id, retval);
		}
		break;
	case IDLN_TYPE_OBJECT:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_Object, NULL);
		break;
	case IDLN_TYPE_TYPECODE:
		retval = (CORBA_TypeCode)
				CORBA_Object_duplicate (
					(CORBA_Object) TC_CORBA_TypeCode, NULL);
		break;
	default:
		g_error ("We were asked to get a typecode for a %s",
			 IDL_tree_type_names [IDL_NODE_TYPE (tree)]);
		break;
	}

	if (retval && retval->c_align == 0)
		retval->c_align = ORBit_imodule_find_c_align (tree);

	if (env._major != CORBA_NO_EXCEPTION)
		g_warning ("ORBit_imodule_get_typecode: exception %s", env._id);

	CORBA_exception_free (&env);

	return retval;
}

CORBA_TypeCode
ORBit_imodule_create_alias_typecode (GHashTable    *typecodes,
				     IDL_tree       tree,
				     CORBA_TypeCode original_type)
{
	CORBA_Environment env;
	CORBA_TypeCode    retval;

	CORBA_exception_init (&env);

	g_return_val_if_fail (IDL_NODE_TYPE (tree) == IDLN_IDENT, NULL);
	g_return_val_if_fail (g_hash_table_lookup (typecodes,
						   IDL_IDENT (tree).repo_id) == NULL, NULL);

	retval = CORBA_ORB_create_alias_tc (NULL,
			IDL_IDENT (tree).repo_id,
			IDL_IDENT (tree).str,
			original_type, &env);

	ORBit_imodule_register_typecode (
			typecodes, IDL_IDENT (tree).repo_id, retval);

	if (env._major != CORBA_NO_EXCEPTION)
		g_warning ("ORBit_imodule_create_alias_typecode: exception %s", env._id);

	CORBA_exception_free (&env);

	return retval;
}

gboolean
ORBit_imodule_type_is_fixed_length (IDL_tree tree)
{
	gboolean is_fixed = TRUE;
	IDL_tree iter;
	IDL_tree typespec;

	typespec = ORBit_imodule_get_typespec (tree);

	switch (IDL_NODE_TYPE (typespec)) {
	case IDLN_TYPE_FLOAT:
	case IDLN_TYPE_INTEGER:
	case IDLN_TYPE_ENUM:
	case IDLN_TYPE_CHAR:
	case IDLN_TYPE_WIDE_CHAR:
	case IDLN_TYPE_OCTET:
	case IDLN_TYPE_BOOLEAN:
		is_fixed = TRUE;
		break;
	case IDLN_TYPE_SEQUENCE:
	case IDLN_TYPE_STRING:
	case IDLN_TYPE_WIDE_STRING:
	case IDLN_TYPE_OBJECT:
	case IDLN_FORWARD_DCL:
	case IDLN_INTERFACE:
	case IDLN_TYPE_ANY:
	case IDLN_NATIVE:
	case IDLN_TYPE_TYPECODE:
		is_fixed = FALSE;
		break;
	case IDLN_TYPE_UNION:
		for (iter = IDL_TYPE_UNION (typespec).switch_body;
		     iter; iter = IDL_LIST (iter).next)
			is_fixed &= ORBit_imodule_type_is_fixed_length (
					IDL_LIST (IDL_CASE_STMT (
						IDL_LIST (iter).data).element_spec).data);
		break;
	case IDLN_EXCEPT_DCL:
	case IDLN_TYPE_STRUCT:
		for (iter = IDL_TYPE_STRUCT (typespec).member_list;
		     iter; iter = IDL_LIST (iter).next)
			is_fixed &= ORBit_imodule_type_is_fixed_length (IDL_LIST (iter).data);
		break;
	case IDLN_TYPE_ARRAY:
		is_fixed = ORBit_imodule_type_is_fixed_length (
				IDL_TYPE_DCL (IDL_get_parent_node (
						typespec, IDLN_TYPE_DCL, NULL)).type_spec);
		break;
	case IDLN_TYPE_DCL:
		is_fixed = ORBit_imodule_type_is_fixed_length (
				IDL_TYPE_DCL (typespec).type_spec);
		break;
	case IDLN_IDENT:
	case IDLN_LIST:
		is_fixed = ORBit_imodule_type_is_fixed_length (IDL_NODE_UP (typespec));
		break;
	case IDLN_MEMBER:
		is_fixed = ORBit_imodule_type_is_fixed_length (IDL_MEMBER (typespec).type_spec);
		break;
	default:
		g_error ("Cannot determine if type %s is fixed-length",
				IDL_tree_type_names [IDL_NODE_TYPE (typespec)]);
		break;
	}

	return is_fixed;
}

static void
ORBit_imodule_traverse_helper (IDL_tree    tree,
			       GFunc       callback,
			       gpointer    user_data,
			       GHashTable *visited_nodes)
{
	IDL_tree curitem;

	if (g_hash_table_lookup (visited_nodes, tree))
		return;

	g_hash_table_insert (visited_nodes, tree, GINT_TO_POINTER (1));

	for (curitem = IDL_INTERFACE (tree).inheritance_spec; curitem;
	     curitem = IDL_LIST (curitem).next)
		ORBit_imodule_traverse_helper (
				IDL_get_parent_node (IDL_LIST (curitem).data, IDLN_INTERFACE, NULL),
				callback, user_data, visited_nodes);

	callback (tree, user_data);
}

void
ORBit_imodule_traverse_parents (IDL_tree tree,
				GFunc    callback,
				gpointer user_data)
{
	GHashTable *visited_nodes = g_hash_table_new (NULL, g_direct_equal);

	g_return_if_fail (tree != NULL);
	g_return_if_fail (callback != NULL);

	if (IDL_NODE_TYPE (tree) != IDLN_INTERFACE)
		tree = IDL_get_parent_node (tree, IDLN_INTERFACE, NULL);

	if (!tree)
		return;

	ORBit_imodule_traverse_helper (tree, callback, user_data, visited_nodes);

	g_hash_table_destroy (visited_nodes);
}


syntax highlighted by Code2HTML, v. 0.9.1