// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/CORBA.h>
#include <Types.h>
#include <Util.h>
#include <GenUtil.h>
#include <GenCPP.h>

// ----------------------------------------------------------------------
// Print header comment
// ----------------------------------------------------------------------

void
IdlCPPGenerator::comment(const char* s, IdlPrettyPrint& out)
{
    out.ignoreNextSep(false);
    out.sep();
    out << "\n//";
    out << "\n// " << s;
    out << "\n//";
    out.ignoreNextSep(true);
}

// ----------------------------------------------------------------------
// Precede C++ keywords by an underscore
// ----------------------------------------------------------------------

char*
IdlCPPGenerator::fixKwd(const char* abs)
{
    assert_nca(abs, OBNCANullString);

    if(*abs == '\0')
	return CORBA_string_dup("");

    static const char* kwds[] =
    {
	"and", "and_eq", "asm", "auto", "bitand",
	"bitor", "bool", "break", "case", "catch",
	"char", "class", "compl", "const", "const_cast",
	"continue", "default", "delete", "do", "double",
	"dynamic_cast", "else", "enum", "explicit", "export",
	"extern", "false", "float", "for", "friend", "goto",
	"if", "inline", "int", "long", "mutable",
	"namespace", "new", "not", "not_eq", "operator",
	"or", "or_eq", "private", "protected", "public",
	"register", "reinterpret_cast", "return", "short", "signed",
	"sizeof", "static", "static_cast", "struct", "switch",
	"template", "this", "throw", "true", "try",
	"typedef", "typeid", "typename", "union", "unsigned",
	"using", "virtual", "void", "volatile", "wchar_t",
	"while", "xor", "xor_eq",
	0
    };

    CORBA_String_var right = (const char*)"";
    const char* s = strchr(abs, ':');
    if(s)
    {
	assert(s[1] == ':');
	right = fixKwd(s + 2);
    }

    CORBA_String_var result;
    CORBA_ULong len = s ? s - abs : strlen(abs);

    CORBA_ULong i;
    for(i = 0 ; kwds[i] != 0 ; i++)
	if(strlen(kwds[i]) == len && strncmp(kwds[i], abs, len) == 0)
	{
	    result = CORBA_string_alloc(len + 1);
	    result[0] = '_';
	    strncpy(&result[1], abs, len);
	    result[len + 1] = '\0';
	    break;
	}
    
    if(kwds[i] == 0)
    {
	result = CORBA_string_alloc(len);
	strncpy(result.inout(), abs, len);
	result[len] = '\0';
    }

    if(s)
    {
	result += "::";
	result += right;
    }

    return result._retn();
}

// ----------------------------------------------------------------------
// Add anonymous type
// ----------------------------------------------------------------------

void
IdlCPPGenerator::addAnon(CORBA_TypeCode_ptr p)
{
    //
    // Check for C++ type equality
    //
    for(CORBA_ULong i = 0 ; i < anonTCSeq_.length() ; i++)
	if(isSameCPPType(anonTCSeq_[i], p))
	    return;
    
    //
    // New anonymous type, add to list
    //
    anonTCSeq_.append(CORBA_TypeCode::_duplicate(p));
}

// ----------------------------------------------------------------------
// Create type string
// ----------------------------------------------------------------------

char*
IdlCPPGenerator::getTypeString(const char* scope, CORBA_TypeCode_ptr type,
			       GetType gt, bool noAlias, const char* ident)
{
    //
    // Get original type
    //
    CORBA_TypeCode_var origType = OBGetOrigType(type);

    //
    // Check if original type is fixed-sized or variable-sized
    //
    bool variable = IdlIsVariable(origType);

    //
    // If alias is wanted, get alias name
    //
    CORBA_String_var alias;
    if(noAlias == false && type -> kind() == CORBA_tk_alias)
    {
	alias = getAbsolute(type -> id());
	IdlRemoveScope(scope, alias.inout());
    }
    else
	alias = CORBA_string_dup("");

    //
    // Get result
    //
    CORBA_String_var result = CORBA_string_dup("???");
    CORBA_TCKind kind = origType -> kind();
    switch(kind)
    {
    case CORBA_tk_void:
    case CORBA_tk_short:
    case CORBA_tk_long:
    case CORBA_tk_ushort:
    case CORBA_tk_ulong:
    case CORBA_tk_float:
    case CORBA_tk_double:
    case CORBA_tk_boolean:
    case CORBA_tk_char:
    case CORBA_tk_octet:
    {
	// **************************************************
	// Basic types (without string)
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	{
	    static const char* names[] =
	    {
		"void",
		"CORBA_Short", "CORBA_Long",
		"CORBA_UShort", "CORBA_ULong",
		"CORBA_Float", "CORBA_Double",
		"CORBA_Boolean", "CORBA_Char", "CORBA_Octet"
	    };

	    static const char* namesNoAlias[] =
	    {
		"void",
		"CORBA_Short", "CORBA_Long",
		"CORBA_UShort", "CORBA_ULong",
		"CORBA_Float", "CORBA_Double",
		"OBUnsignedChar", "OBUnsignedChar", "OBUnsignedChar"
	    };

	    if(noAlias)
		result = namesNoAlias[(int)kind - (int)CORBA_tk_void];
	    else
		result = names[(int)kind - (int)CORBA_tk_void];
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeStruct:
	case GetTypeSeq:
	case GetTypeArray:
	case GetTypeIn:
	case GetTypeReturn:
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeVar:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeOut:
	case GetTypeInOut:
	    result += '&';
	    break;

	case GetTypeConst:
	{
	    CORBA_String_var tmp = CORBA_string_dup("const ");
	    tmp += result;
	    result = tmp._retn();
	    break;
	}
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_any:
    {
	// **************************************************
	// The any type
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	    result = CORBA_string_dup("CORBA_Any");

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeStruct:
	case GetTypeSeq:
	case GetTypeArray:
	    break;

	case GetTypeIn:
	{
	    CORBA_String_var tmp = CORBA_string_dup("const ");
	    tmp += result;
	    result = tmp._retn();
	    result += '&';
	    break;
	}

	case GetTypeReturn:
	    result += '*';
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeVar:
	    result += "_var";
	    break;

	case GetTypeInOut:
	    result += '&';
	    break;

	case GetTypeOut:
	    result += "*&";
	    break;

	case GetTypeConst:
	    result = CORBA_string_dup("???");
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_TypeCode:
    case CORBA_tk_Principal:
    case CORBA_tk_objref:
    {
	// **************************************************
	// Object references (including pseudo object refereces)
        // **************************************************

	if(strlen(alias))
	    result = alias;
	else
	{
	    if(kind == CORBA_tk_TypeCode)
	    {
		result = CORBA_string_dup("CORBA_TypeCode");
	    }
	    else if(kind == CORBA_tk_Principal)
	    {
		result = CORBA_string_dup("CORBA_Principal");
	    }
	    else if(kind == CORBA_tk_objref)
	    {
		if(strcmp(origType -> id(),
			  "IDL:omg.org/CORBA/Object:1.0") == 0)
		{
		    result = CORBA_string_dup("CORBA_Object");
		}
		else
		{
		    CORBA_String_var s = getAbsolute(origType -> id());
		    IdlRemoveScope(scope, s.inout());
		    result = s;
		}
	    }
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeSeq:
	    break;

	case GetTypeConst:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypePtr:
	case GetTypeReturn:
	case GetTypeIn:
	    result += "_ptr";
	    break;

	case GetTypeRef:
	    result += "Ref";
	    break;

	case GetTypeVar:
	case GetTypeStruct:
	case GetTypeArray:
	    result += "_var";
	    break;

	case GetTypeOut:
	case GetTypeInOut:
	    result += "_ptr&";
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_struct:
    case CORBA_tk_union:
    {
	// **************************************************
	// Structs and unions
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	{
	    CORBA_String_var s = getAbsolute(origType -> id());
	    IdlRemoveScope(scope, s.inout());
	    result = s;
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeSeq:
	case GetTypeStruct:
	case GetTypeArray:
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeConst:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeVar:
	    result += "_var";
	    break;

	case GetTypeIn:
	{
	    CORBA_String_var tmp = CORBA_string_dup("const ");
	    tmp += result;
	    result = tmp._retn();
	    result += '&';
	    break;
	}

	case GetTypeInOut:
	    result += '&';
	    break;

	case GetTypeOut:
	    if(variable)
		result += "*&";
	    else
		result += '&';
	    break;

	case GetTypeReturn:
	    if(variable)
		result += '*';
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_except:
    {
	// **************************************************
	// Structs
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	{
	    CORBA_String_var s = getAbsolute(origType -> id());
	    IdlRemoveScope(scope, s.inout());
	    result = s;
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeSeq:
	case GetTypeStruct:
	case GetTypeArray:
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeVar:
	case GetTypeConst:
	case GetTypeSlice:
	case GetTypeForAny:
	case GetTypeIn:
	case GetTypeInOut:
	case GetTypeOut:
	case GetTypeReturn:
	    result = CORBA_string_dup("???");
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_enum:
    {
	// **************************************************
	// Enums
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	{
	    CORBA_String_var s = getAbsolute(origType -> id());
	    IdlRemoveScope(scope, s.inout());
	    result = s;
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeSeq:
	case GetTypeStruct:
	case GetTypeArray:
	case GetTypeIn:
	case GetTypeReturn:
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeVar:
	case GetTypeConst:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeOut:
	case GetTypeInOut:
	    result += '&';
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_string:
    {
	// **************************************************
	// Strings
	// **************************************************

	if(strlen(alias))
	    result = alias;
	else
	    result = CORBA_string_dup("char*");

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeReturn:
	    break;

	case GetTypeSeq:
	case GetTypePtr:
	case GetTypeRef:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeIn:
	case GetTypeConst:
	    result = CORBA_string_dup("const char*");
	    break;

	case GetTypeOut:
	case GetTypeInOut:
	    result += '&';
	    break;

	case GetTypeVar:
	case GetTypeStruct:
	case GetTypeArray:
	    if(strlen(alias))
		result += "_var";
	    else
		result = CORBA_string_dup("CORBA_String_var");
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_sequence:
    {
	// **************************************************
	// Sequences
	// **************************************************

	addAnon(origType);

	if(strlen(alias))
	    result = alias;
	else
	{
	    CORBA_TypeCode_var contentType = origType -> content_type();
	    CORBA_String_var contentTypeString =
		getTypeString(scope, contentType, GetTypeSeq, noAlias);
	    
	    CORBA_TypeCode_var origContentType = contentType;
	    origContentType = OBGetOrigType(contentType);

	    bool variableContent = IdlIsVariable(origContentType);

	    switch(origContentType -> kind())
	    {
	    case CORBA_tk_null:
	    case CORBA_tk_void:
	    case CORBA_tk_alias:
		assert(false);
		break;

	    case CORBA_tk_short:
	    case CORBA_tk_long:
	    case CORBA_tk_ushort:
	    case CORBA_tk_ulong:
	    case CORBA_tk_float:
	    case CORBA_tk_double:
	    case CORBA_tk_boolean:
	    case CORBA_tk_char:
	    case CORBA_tk_octet:
	    case CORBA_tk_any:
	    case CORBA_tk_struct:
	    case CORBA_tk_union:
	    case CORBA_tk_enum:
	    case CORBA_tk_sequence:
	    case CORBA_tk_except:

		if(variableContent)
		{
		    if(origType -> length())
			result = CORBA_string_dup("OBBndVarSeq< ");
		    else
			result = CORBA_string_dup("OBVarSeq< ");
		}
		else
		{
		    if(origType -> length())
			result = CORBA_string_dup("OBBndFixSeq< ");
		    else
			result = CORBA_string_dup("OBFixSeq< ");
		}
		
		result += contentTypeString;
		
		if(origType -> length())
		{
		    result += ", ";
		    result += origType -> length();
		}
		
		result += " >";

		break;

	    case CORBA_tk_array:
	    {
		if(variableContent)
		{
		    if(origType -> length())
			result = CORBA_string_dup("OBBndVarArraySeq< ");
		    else
			result = CORBA_string_dup("OBVarArraySeq< ");
		}
		else
		{
		    if(origType -> length())
			result = CORBA_string_dup("OBBndFixArraySeq< ");
		    else
			result = CORBA_string_dup("OBFixArraySeq< ");
		}
		
		CORBA_TypeCode_var contentContentType;
		CORBA_ULong length;
		getArrayType(origContentType, contentContentType.out(),
			     length);
		CORBA_String_var contCont
		    = getTypeString(scope, contentContentType,
				    GetTypeArray, true);

		result += contentTypeString;
		result += ", ";
		result += contCont;
		result += ", ";
		result += length;

		if(origType -> length())
		{
		    result += ", ";
		    result += origType -> length();
		}
		
		result += " >";

		break;
	    }

	    case CORBA_tk_TypeCode:
	    case CORBA_tk_Principal:
	    case CORBA_tk_objref:

		if(origType -> length())
		    result = CORBA_string_dup("OBBndObjSeq< ");
		else
		    result = CORBA_string_dup("OBObjSeq< ");

		result += contentTypeString;

		if(origType -> length())
		{
		    result += ", ";
		    result += origType -> length();
		}

		result += " >";

		break;

	    case CORBA_tk_string:

		if(origType -> length())
		{
		    result = CORBA_string_dup("OBBndStrSeq< ");
		    result += origType -> length();
		    result += " >";
		}
		else
		    result = CORBA_string_dup("OBStrSeq");

		break;
	    }
	}

	switch(gt)
	{
	case GetTypeNormal:
	case GetTypeSeq:
	case GetTypeStruct:
	case GetTypeArray:
	    break;

	case GetTypePtr:
	case GetTypeRef:
	case GetTypeConst:
	case GetTypeSlice:
	case GetTypeForAny:
	    result = CORBA_string_dup("???");
	    break;

	case GetTypeVar:
	{
	    if(strlen(alias))
	    {
		result += "_var";
	    }
	    else
	    {
		CORBA_String_var tmp = CORBA_string_dup("OBSeqVar< ");
		tmp += result;
		result = tmp._retn();
		result += " >";
	    }
	    break;
	}

	case GetTypeIn:
	{
	    CORBA_String_var tmp = CORBA_string_dup("const ");
	    tmp += result;
	    result = tmp._retn();
	    result += '&';
	    break;
	}

	case GetTypeOut:
	    result += "*&";
	    break;

	case GetTypeInOut:
	    result += '&';
	    break;

	case GetTypeReturn:
	    result += '*';
	    break;
	}

	if(ident && strlen(result))
	{
	    result += ' ';
	    result += ident;
	}

	break;
    }

    case CORBA_tk_array:
    {
	// **************************************************
	// Arrays
	// **************************************************

	addAnon(origType);

	if(strlen(alias))
	{
	    result = alias;

	    switch(gt)
	    {
	    case GetTypeNormal:
	    case GetTypeStruct:
	    case GetTypeArray:
	    case GetTypeInOut:
		break;
		
	    case GetTypePtr:
	    case GetTypeRef:
	    case GetTypeConst:
		result = CORBA_string_dup("???");
		break;

	    case GetTypeVar:
		result += "_var";
		break;

	    case GetTypeSlice:
	    case GetTypeSeq:
		result += "_slice";
		break;

	    case GetTypeForAny:
		result += "_forany";
		break;

	    case GetTypeIn:
	    {
		CORBA_String_var tmp = CORBA_string_dup("const ");
		tmp += result;
		result = tmp._retn();
		break;
	    }
	    
	    case GetTypeOut:
		if(variable)
		    result += "_slice*&";
		break;

	    case GetTypeReturn:
		result += "_slice*";
		break;
	    }

	    if(ident && strlen(result))
	    {
		result += ' ';
		result += ident;
	    }
	}
	else
	{
	    OBFixSeq<CORBA_ULong> sizes;
	    
	    CORBA_TypeCode_var contentType = origType;
	    
	    while(contentType -> kind() == CORBA_tk_array)
	    {
		sizes.append(contentType -> length());
		contentType = contentType -> content_type();
		
		if(noAlias)
		    contentType = OBGetOrigType(contentType);
	    }
	    
	    CORBA_String_var array = CORBA_string_dup("");
	    CORBA_String_var slice = CORBA_string_dup("");

	    CORBA_ULong length = 1;
	    for(CORBA_ULong i = 0 ; i < sizes.length() ; i++)
	    {
		length *= sizes[i];

		array += '[';
		array += sizes[i];
		array += ']';

		if(i > 0)
		{
		    slice += '[';
		    slice += sizes[i];
		    slice += ']';
		}
	    }

	    result = getTypeString(scope, contentType, GetTypeArray, noAlias);

	    switch(gt)
	    {
	    case GetTypeNormal:
	    case GetTypeStruct:
	    case GetTypeArray:
	    case GetTypeInOut:

		if(ident)
		{
		    result += ' ';
		    result += ident;
		}

		result += array;
		 
		break;
		
	    case GetTypePtr:
	    case GetTypeRef:
	    case GetTypeConst:
		result = CORBA_string_dup("???");
		break;
		
	    case GetTypeVar:
	    {
		CORBA_String_var cont = result;

		if(variable)
		{
		    CORBA_String_var tmp =
			CORBA_string_dup("OBVarArrayVar< ");
		    tmp += result;
		    result = tmp._retn();
		}
		else
		{
		    CORBA_String_var tmp =
			CORBA_string_dup("OBFixArrayVar< ");
		    tmp += result;
		    result = tmp._retn();
		}

		result += slice;
		result += ", ";
		result += cont;
		result += ", ";
		result += length;
		result += " >";

		if(ident)
		{
		    result += ' ';
		    result += ident;
		}

		break;
	    }

	    case GetTypeForAny:
	    {
		CORBA_String_var cont = result;

		if(variable)
		{
		    CORBA_String_var tmp =
			CORBA_string_dup("OBVarArrayForAny< ");
		    tmp += result;
		    result = tmp._retn();
		}
		else
		{
		    CORBA_String_var tmp =
			CORBA_string_dup("OBFixArrayForAny< ");
		    tmp += result;
		    result = tmp._retn();
		}

		result += slice;
		result += ", ";
		result += cont;
		result += ", ";
		result += length;
		result += " >";
		
		if(ident)
		{
		    result += ' ';
		    result += ident;
		}

		break;
	    }

	    case GetTypeSlice:
	    case GetTypeSeq:

		if(ident)
		{
		    result += ' ';
		    result += ident;
		}

		result += slice;
		 
		break;
		
	    case GetTypeIn:
	    {
		CORBA_String_var tmp = CORBA_string_dup("const ");
		tmp += result;
		result = tmp._retn();

		if(ident)
		{
		    result += ' ';
		    result += ident;
		}

		result += array;

		break;
	    }

	    case GetTypeOut:

		if(variable)
		{
		    if(strlen(slice) > 0)
			result += " (";

		    result += "*&";

		    if(ident)
		    {
			if(strlen(slice) > 0)
			    result += ' ';
			
			result += ident;
		    }

		    if(strlen(slice) > 0)
			result += ')';

		    result += slice;
		}
		else
		{
		    if(ident)
		    {
			result += ' ';
			result += ident;
		    }

		    result += array;
		}

		break;

	    case GetTypeReturn:

		if(strlen(slice) > 0)
		    result += " (";

		result += '*';
		
		if(ident)
		{
		    if(strlen(slice) == 0)
			result += ' ';

		    result += ident;
		}
		
		if(strlen(slice) > 0)
		    result += ')';

		result += slice;

		break;
	    }
	}
	
	break;
    }

    case CORBA_tk_alias:
    case CORBA_tk_null:
	assert(false);
	break;
    }

    return result._retn();
}

// ----------------------------------------------------------------------
// Get array type and total number of elements
// ----------------------------------------------------------------------

void
IdlCPPGenerator::getArrayType(CORBA_TypeCode_ptr type,
			      CORBA_TypeCode_ptr& cont,
			      CORBA_ULong& length)
{
    CORBA_TypeCode_var tc = OBGetOrigType(type);
    assert(tc -> kind() == CORBA_tk_array);
    
    length = 1;
    CORBA_TypeCode_var contentType = tc;
    while(contentType -> kind() == CORBA_tk_array)
    {
	length *= contentType -> length();
	contentType = contentType -> content_type();
	contentType = OBGetOrigType(contentType);
    }

    cont = contentType._retn();
}

// ----------------------------------------------------------------------
// Check for C++ type equality
// ----------------------------------------------------------------------

bool
IdlCPPGenerator::isSameCPPType(CORBA_TypeCode_ptr t1, CORBA_TypeCode_ptr t2)
{
    if(t1 -> equal(t2))
        return true;
    
    CORBA_TypeCode_var tc1 = OBGetOrigType(t1);
    CORBA_TCKind k1 = tc1 -> kind();
    CORBA_TypeCode_var tc2 = OBGetOrigType(t2);
    CORBA_TCKind k2 = tc2 -> kind();
    
    if((k1 == CORBA_tk_char ||
        k1 == CORBA_tk_boolean ||
        k1 == CORBA_tk_octet) &&
       (k2 == CORBA_tk_char ||
        k2 == CORBA_tk_boolean ||
        k2 == CORBA_tk_octet))
    {
        return true;
    }
    
    if(k1 == k2 && (k1 == CORBA_tk_sequence || k1 == CORBA_tk_array))
    {
        if(tc1 -> length() == tc2 -> length())
        {
            CORBA_TypeCode_var ctc1 = tc1 -> content_type();
            CORBA_TypeCode_var ctc2 = tc2 -> content_type();
            return isSameCPPType(ctc1, ctc2);
        }
    }

    return false;
}

// ----------------------------------------------------------------------
// Write marshal/unmarshal/marshalCount code
// ----------------------------------------------------------------------

void
IdlCPPGenerator::writeMarshalCode(CORBA_TypeCode_ptr type, bool conv,
				  const char* arg, const char* oct,
				  IdlPrettyPrint& out)
{
    CORBA_TypeCode_var tc = OBGetOrigType(type);

    out << '\n';
   
    if(tc -> kind() == CORBA_tk_array)
    {
	CORBA_ScopedName_var absolute =
	    getTypeString("", type, GetTypeNormal, true);
	CORBA_String_var anon = IdlGetAnonName(absolute);

	out << "OBMarshal_" << anon;
    }
    else
	out << "OBMarshal";

    out << '(' << arg;
    
    if(conv &&
       (tc -> kind() == CORBA_tk_string ||
	tc -> kind() == CORBA_tk_objref ||
	tc -> kind() == CORBA_tk_TypeCode ||
	tc -> kind() == CORBA_tk_Principal))       
    {
	out << ".in()";
    }
    
    out << ", " << oct << ");";
}
    
void
IdlCPPGenerator::writeMarshalCountCode(CORBA_TypeCode_ptr type, bool conv,
				       const char* arg, const char* count,
				       IdlPrettyPrint& out)
{
    CORBA_TypeCode_var tc = OBGetOrigType(type);

    out << '\n';
   
    if(tc -> kind() == CORBA_tk_array)
    {
	CORBA_ScopedName_var absolute =
	    getTypeString("", type, GetTypeNormal, true);
	CORBA_String_var anon = IdlGetAnonName(absolute);

	out << "OBMarshalCount_" << anon;
    }
    else
	out << "OBMarshalCount";

    out << '(' << arg;
    
    if(conv &&
       (tc -> kind() == CORBA_tk_string ||
	tc -> kind() == CORBA_tk_objref ||
	tc -> kind() == CORBA_tk_TypeCode ||
	tc -> kind() == CORBA_tk_Principal))       
    {
	out << ".in()";
    }
    
    out << ", " << count << ");";
}

void
IdlCPPGenerator::writeUnmarshalCode(CORBA_TypeCode_ptr type, bool conv,
				    const char* arg, const char* coct,
				    const char* swap, IdlPrettyPrint& out)
{
    CORBA_TypeCode_var tc = OBGetOrigType(type);

    out << '\n';
   
    if(tc -> kind() == CORBA_tk_array)
    {
	CORBA_ScopedName_var absolute =
	    getTypeString("", type, GetTypeNormal, true);
	CORBA_String_var anon = IdlGetAnonName(absolute);

	out << "OBUnmarshal_" << anon;
    }
    else
	out << "OBUnmarshal";

    out << '(' << arg;
    
    if(conv &&
       (tc -> kind() == CORBA_tk_string ||
	tc -> kind() == CORBA_tk_objref ||
	tc -> kind() == CORBA_tk_TypeCode ||
	tc -> kind() == CORBA_tk_Principal))       
    {
	out << ".inout()";
    }
    
    out << ", " << coct << ", " << swap << ");";
}
    
// ----------------------------------------------------------------------
// Write unmarshal code for out and inout values
// ----------------------------------------------------------------------

void
IdlCPPGenerator::writeReturnUnmarshalCode(const char* scope,
					  CORBA_TypeCode_ptr type,
					  IdlPrettyPrint& out)
{
    CORBA_String_var ret = getTypeString(scope, type, GetTypeReturn);

    CORBA_TypeCode_var origType = OBGetOrigType(type);
    
    if(origType -> kind() == CORBA_tk_array)
    {
	CORBA_ScopedName_var absolute =
	    getTypeString("", origType, GetTypeNormal, true);
	CORBA_String_var anon = IdlGetAnonName(absolute);
	
	out << '\n' << ret << " _ob_r = OBAlloc_" << anon << "();";
	out << "\nOBUnmarshal_" << anon << "(_ob_r, _ob_co, _ob_sw);";
    }
    else if(IdlIsVariable(origType))
    {
	if(origType -> kind() == CORBA_tk_objref ||
	   origType -> kind() == CORBA_tk_TypeCode ||
	   origType -> kind() == CORBA_tk_Principal)
	{
	    CORBA_String_var s = getTypeString(scope, type, GetTypeNormal);
	    out << '\n' << ret << " _ob_r = " << s << "::_nil();";
	    out << "\nOBUnmarshal(_ob_r, _ob_co, _ob_sw);";
	}
	else if(origType -> kind() == CORBA_tk_string)
	{
	    out << '\n' << ret << " _ob_r = 0;";
	    out << "\nOBUnmarshal(_ob_r, _ob_co, _ob_sw);";
	}
	else
	{
	    CORBA_String_var s = getTypeString(scope, type, GetTypeNormal);
	    out << '\n' << ret << " _ob_r = new " << s << ';';
	    out << "\nOBUnmarshal(*_ob_r, _ob_co, _ob_sw);";
	}
    }
    else if(origType -> kind() != CORBA_tk_void)
    {
	out << '\n' << ret << " _ob_r;";
	out << "\nOBUnmarshal(_ob_r, _ob_co, _ob_sw);";
    }
}

void
IdlCPPGenerator::writeOutUnmarshalCode(const char* scope,
				       CORBA_TypeCode_ptr type,
				       IdlPrettyPrint& out, CORBA_ULong j)
{
    CORBA_TypeCode_var origType = OBGetOrigType(type);
    
    if(origType -> kind() == CORBA_tk_array)
    {
	CORBA_ScopedName_var absolute =
	    getTypeString("", origType, GetTypeNormal, true);
	CORBA_String_var anon = IdlGetAnonName(absolute);

	if(IdlIsVariable(origType))
	    out << "\n_ob_a" << j << " = OBAlloc_" << anon << "();";

	out << "\nOBUnmarshal_" << anon << "(_ob_a" << j
	    << ", _ob_co, _ob_sw);";
    }
    else if(IdlIsVariable(origType))
    {
	if(origType -> kind() == CORBA_tk_objref ||
	   origType -> kind() == CORBA_tk_TypeCode ||
	   origType -> kind() == CORBA_tk_Principal)
	{
	    CORBA_String_var s = getTypeString(scope, type, GetTypeNormal);
	    out << "\n_ob_a" << j << " = " << s << "::_nil();";
	    out << "\nOBUnmarshal(_ob_a" << j << ", _ob_co, _ob_sw);";
	}
	else if(origType -> kind() == CORBA_tk_string)
	{
	    out << "\n_ob_a" << j << " = 0;";
	    out << "\nOBUnmarshal(_ob_a" << j << ", _ob_co, _ob_sw);";
	}
	else
	{
	    CORBA_String_var s = getTypeString(scope, type, GetTypeNormal);
	    out << "\n_ob_a" << j << " = new " << s << ';';
	    out << "\nOBUnmarshal(*_ob_a" << j << ", _ob_co, _ob_sw);";
	}
    }
    else if(origType -> kind() != CORBA_tk_void)
    {
	out << "\nOBUnmarshal(_ob_a" << j << ", _ob_co, _ob_sw);";
    }
}

// ----------------------------------------------------------------------
// Check if the implementation and declaration of a type is already
// in the ORBacus library
// ----------------------------------------------------------------------

bool
IdlCPPGenerator::isInLibrary(CORBA_TypeCode_ptr tc)
{
    CORBA_TypeCode_var type = OBGetOrigType(tc);
    
    if(type -> kind() == CORBA_tk_sequence)
    {
	CORBA_TypeCode_var contentType = type -> content_type();
	contentType = OBGetOrigType(contentType);
	
	switch(contentType -> kind())
	{
	case CORBA_tk_null:
	case CORBA_tk_void:
	case CORBA_tk_alias:
	case CORBA_tk_except:
	    
	    assert(false);
	    
	    break;
	    
	case CORBA_tk_short:
	case CORBA_tk_long:
	case CORBA_tk_ushort:
	case CORBA_tk_ulong:
	case CORBA_tk_float:
	case CORBA_tk_double:
	case CORBA_tk_boolean:
	case CORBA_tk_char:
	case CORBA_tk_octet:
	case CORBA_tk_any:
	case CORBA_tk_TypeCode:
	case CORBA_tk_Principal:
	case CORBA_tk_string:
	    
	    //
	    // Unbounded sequences are already in the ORBacus library
	    //
	    if(type -> length() == 0)
		return true;
	    
	    break;
	    
	case CORBA_tk_struct:
	case CORBA_tk_union:
	case CORBA_tk_enum:
	case CORBA_tk_array:

	    break;
	
	case CORBA_tk_sequence:
	{
	    CORBA_TypeCode_var contentContentType =
		contentType -> content_type();
	    contentContentType = OBGetOrigType(contentContentType);

	    switch(contentContentType -> kind())
	    {
	    case CORBA_tk_null:
	    case CORBA_tk_void:
	    case CORBA_tk_alias:
	    case CORBA_tk_except:
		
		assert(false);
		
		break;
		
	    case CORBA_tk_boolean:
	    case CORBA_tk_char:
	    case CORBA_tk_octet:
		
		//
		// Unbounded sequences of unbounded sequences for
		// char, octet and boolean are already in the ORBacus
		// library
		//
		if(contentType -> length() == 0)
		    return true;
		
		break;
		
	    case CORBA_tk_short:
	    case CORBA_tk_long:
	    case CORBA_tk_ushort:
	    case CORBA_tk_ulong:
	    case CORBA_tk_float:
	    case CORBA_tk_double:
	    case CORBA_tk_any:
	    case CORBA_tk_TypeCode:
	    case CORBA_tk_Principal:
	    case CORBA_tk_string:
	    case CORBA_tk_struct:
	    case CORBA_tk_union:
	    case CORBA_tk_enum:
	    case CORBA_tk_array:
	    case CORBA_tk_sequence:
	    case CORBA_tk_objref:
		
		break;
	    }
	    
	    break;
	}
	
	case CORBA_tk_objref:
	    
	    //
	    // Unbounded object sequence is already
	    // in the ORBacus library
	    //
	    if(type -> length() == 0)
	    {
		CORBA_RepositoryId_var id = contentType -> id();
		if(strcmp(id,"IDL:omg.org/CORBA/Object:1.0") == 0)
		    return true;
	    }
	    
	    break;
	}
    }

    return false;
}

// ----------------------------------------------------------------------
// Check if code needs to be generated for a specific repository id
// ----------------------------------------------------------------------

bool
IdlCPPGenerator::check(const char* id)
{
    if(!partial_)
	return true;

    for(CORBA_ULong i = 0 ; i < idSeq_.length() ; i++)
	if(strcmp(id, idSeq_[i]) == 0)
	    return true;

    return false;
}

// ----------------------------------------------------------------------
// Get absolute name
// ----------------------------------------------------------------------

char*
IdlCPPGenerator::getAbsolute(CORBA_Contained_ptr contained)
{
    CORBA_Identifier_var name = contained -> name();
    name = fixKwd(name);

    CORBA_Container_var container = contained -> defined_in();
    CORBA_Contained_var contained2 = CORBA_Contained::_narrow(container);

    if(CORBA_is_nil(contained2))
    {
	return CORBA_string_dup(name);
    }
    else
    {
	CORBA_ScopedName_var s = getAbsolute(contained2);
	
	if(contained2 -> def_kind() == CORBA_dk_Module)
	{
	    char* result = CORBA_string_alloc(strlen(s) + 1 + strlen(name));
	    strcpy(result, s);
	    strcat(result, "_");
	    strcat(result, name);
	    return result;
	}
	else
	{
	    char* result = CORBA_string_alloc(strlen(s) + 2 + strlen(name));
	    strcpy(result, s);
	    strcat(result, "::");
	    strcat(result, name);
	    return result;
	}
    }
}

char*
IdlCPPGenerator::getAbsolute(const char* id)
{
    for(CORBA_ULong i = 0 ; i < absSeq_.length() ; i += 2)
    {
	if(strcmp(absSeq_[i], id) == 0)
	    return CORBA_string_dup(absSeq_[i + 1]);
    }
    
    CORBA_Contained_var contained = repository_ -> lookup_id(id);
    assert(!CORBA_is_nil(contained));
    
    CORBA_ULong len = absSeq_.length();
    absSeq_.length(len + 2);
    absSeq_[len] = id;
    absSeq_[len + 1] = getAbsolute(contained);
    return CORBA_string_dup(absSeq_[len + 1]);
}

// ----------------------------------------------------------------------
// Get scope and prefix
// ----------------------------------------------------------------------

void
IdlCPPGenerator::getScopeAndPrefix(CORBA_Container_ptr container,
				   char*& scope, char*& prefix)
{
    CORBA_Contained_var contained = CORBA_Contained::_narrow(container);
    if(CORBA_is_nil(contained))
    {
	scope = CORBA_string_dup("");
	prefix = CORBA_string_dup("");
    }
    else
    {
	CORBA_RepositoryId_var id = contained -> id();
	CORBA_ScopedName_var s = getAbsolute(id);

	if(contained -> def_kind() == CORBA_dk_Module)
	{
	    const char* p = strrchr(s, ':');
	    
	    if(p)
	    {
		assert(p > s.in());
		assert(*(p - 1) == ':');
		p++;
	    }
	    else
	    {
		p = s;
	    }

	    prefix = CORBA_string_alloc(strlen(p) + 1);
	    strcpy(prefix, p);
	    strcat(prefix, "_");

	    scope = CORBA_string_alloc(p - (const char*)s);
	    strncpy(scope, s, p - (const char*)s);
	    scope[p - (const char*)s] = '\0';
	}
	else
	{
	    prefix = CORBA_string_dup("");

	    scope = CORBA_string_alloc(strlen(s) + 2);
	    strcpy(scope, s);
	    strcat(scope, "::");
	}
    }
}

// ----------------------------------------------------------------------
// Print union labels
// ----------------------------------------------------------------------

void
IdlCPPGenerator::printUnionLabels(const IdlUnionMemberInfo& info,
				  IdlPrettyPrint& out,
				  const char* cast)
{
    out.dec();

    if(CORBA_is_nil(info.type))
    {
	assert(info.isDefault);
	out << "\ndefault:";
    }
    else
    {
	if(info.isDefault)
	    out << "\ndefault:";
	else
	{
	    CORBA_ULong i;
	    
	    for(i = 0 ; i < info.pLabels.length() ; i++)
		out << "\ncase " << cast << info.pLabels[i] << ':';
	    
	    for(i = 0 ; i < info.nLabels.length() ; i++)
		out << "\ncase " << cast << '-' << info.nLabels[i] << ':';
	}
    }

    out.inc();
}
