// **********************************************************************
//
// This file contains a set of tests about CORBA::DynAny API.
//
// Currently, it is written for and works on ORBacus ORB.
// For other ORBs,
//     update the include ORB file and adapt the CORBA2 macro.
//
// 
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// Author: Philippe Merle
// from the Laboratoire d'Informatique Fondamentale de Lille in France 
// (it is a computer science laboratory of the Lille french university)
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/CORBA.h>

#include <stdlib.h>
#include <stdio.h>

#ifdef HAVE_FSTREAM
#   include <fstream>
#else
#   include <fstream.h>
#endif

// ----------------------------------------------------------------------
// CORBA2 macro
// ----------------------------------------------------------------------

// this macro is used to access CORBA module definitions
#define CORBA2(IDENT) CORBA_##IDENT

// must be changed if the ORB uses nested classes or C++ scopes
// ie. CORBA2(IDENT) CORBA::IDENT

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
//
// a class to display CORBA::TypeCode and CORBA::DynAny instances
//
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

class CORBA_OUT
{
protected: // internal state

   int level_;
   ostream& ostr_;
   CORBA2(ORB_ptr) orb_;

public: // constructor and destructor

   CORBA_OUT (ostream& ostr)
     : level_(0), ostr_ (ostr), orb_(0) {};
   ~CORBA_OUT () {};

public: // manage level

   void set_orb (CORBA2(ORB_ptr) orb) { orb_ = orb; };

   void down () { level_ ++; };
   void up () { level_ --; };

   ostream& skip () {
       for(int i=0; i<level_; i++) ostr_ << "   ";
       return ostr_;
   };

public:

   void display_IDL_TypeCode (CORBA2(TypeCode_ptr));
   void show_name_TypeCode (CORBA2(TypeCode_ptr));

   void display_DynAny (CORBA2(DynAny_ptr));
   void show_DynAny_without_type (CORBA2(DynAny_ptr));
   void show_DynAny (CORBA2(DynAny_ptr));

   void display_Any (const CORBA2(Any)&);
   void show_Any (const CORBA2(Any)&);

   void display_AnySeq (CORBA2(AnySeq)* any_seq);
};

// ----------------------------------------------------------------------
// Get original type, i.e. remove aliases
// ----------------------------------------------------------------------

CORBA2(TypeCode_ptr)
GetOrigType(CORBA2(TypeCode_ptr) p)
{
    CORBA2(TypeCode_var) tc = CORBA2(TypeCode)::_duplicate(p);

    while(tc -> kind() == CORBA2(tk_alias))
	tc = tc -> content_type();

    return tc._retn();
}

// ----------------------------------------------------------------------
// display CORBA::TypeCode
// ----------------------------------------------------------------------

static const char* NameTypeIDL [] = { "null", "void", "short", "long",
  "unsigned short", "unsigned long", "float", "double", "boolean", "char",
  "octet", "any", "TypeCode", "Principal", "objref", "struct", "union",
  "enum", "string", "sequence", "array", "alias", "exception"
};

void CORBA_OUT::display_IDL_TypeCode (CORBA2(TypeCode_ptr) tc)
{
    CORBA2(ULong) i;
    CORBA2(TCKind) kind = tc->kind();

    switch ( kind ) {
    case CORBA2(tk_objref):
        skip() << "interface " << tc->name() << ';';
        break;

    case CORBA2(tk_struct):
    case CORBA2(tk_except): {
        skip() << NameTypeIDL[kind] << ' ' << tc->name() << " {" << endl;

        down ();
        for (i=0; i<tc->member_count(); i++) {
            skip();
            CORBA2(TypeCode_var) member_type = tc->member_type(i);
            show_name_TypeCode (member_type);
            ostr_ << ' ' << tc->member_name(i) << ';' << endl;
        }
        up ();
        skip() << "};";
      } break;
 
    case CORBA2(tk_union): {
        skip () << "union " << tc->name() << " switch(";
        CORBA2(TypeCode_var) discriminator_type = tc->discriminator_type();
        show_name_TypeCode (discriminator_type);
        ostr_ << ") {" << endl;

        down ();
        for (i=0 ; i<tc->member_count(); i++) {
            CORBA2(Any_var) any_member_label = tc->member_label(i);
            CORBA2(TypeCode_var) type_label = any_member_label->type();
            if ( type_label->kind() == CORBA2(tk_octet) ) {
	        skip() << "default";
            } else {
	        skip() << "case ";
                CORBA2(DynAny_var) dyn_any_member_label =
                                  orb_->create_dyn_any (any_member_label);
                show_DynAny_without_type (dyn_any_member_label);
            }
            ostr_ << ": ";
            CORBA2(TypeCode_var) member_type = tc->member_type(i);
            show_name_TypeCode (member_type);
            ostr_ << ' ' << tc->member_name(i) << ';' << endl;
        }
        up();
        skip() << "};"; 
      } break;

    case CORBA2(tk_enum):
        skip()  << "enum " << tc->name() << " {";
        for (i=0; i<tc->member_count(); i++) {
            if ( i != 0 )
 	       ostr_ << ", ";
            ostr_ << tc->member_name(i);
        }
        ostr_ << "};";
        break;

    case CORBA2(tk_string): {
        skip() << "string";
        CORBA2(ULong) length = tc->length();
        if (length != 0)
	  ostr_ << '<' << length << '>';
      } break;

    case CORBA2(tk_sequence): {
        skip() << "sequence<";
        CORBA2(TypeCode_var) content_type = tc->content_type();
        show_name_TypeCode (content_type);
        CORBA2(ULong) length = tc->length();
        if (length != 0)
          ostr_ << ", " << length;
        ostr_ << '>';
      } break;

    case CORBA2(tk_array): {
        skip();
        CORBA2(TypeCode_var) content_type = tc->content_type();
        show_name_TypeCode (content_type);
        ostr_ << '[' << tc->length() << ']';
      } break;

    case CORBA2(tk_alias): {
        skip() << "typedef ";
        CORBA2(TypeCode_var) content_type = tc->content_type();
        show_name_TypeCode (content_type);
        ostr_ << ' ' << tc->name() << ';';
      } break;

    default:
        skip() << NameTypeIDL [kind];
            break;
    }
    ostr_ << endl;
}

void CORBA_OUT::show_name_TypeCode (CORBA2(TypeCode_ptr) tc)
{
    CORBA2(TCKind) kind = tc->kind();

    switch ( kind ) {
    case CORBA2(tk_objref):
    case CORBA2(tk_enum):
    case CORBA2(tk_union):
    case CORBA2(tk_struct):
    case CORBA2(tk_except):
    case CORBA2(tk_alias):
        ostr_ << tc->name();
        break;

    case CORBA2(tk_string): {
        ostr_ << "string";
        CORBA2(ULong) length = tc->length();
        if (length != 0)
	  ostr_ << '<' << length << '>';
      } break;

    case CORBA2(tk_sequence): {
        ostr_ << "sequence<";
        CORBA2(TypeCode_var) content_type = tc->content_type();
        show_name_TypeCode (content_type);
        CORBA2(ULong) length = tc->length();
        if (length != 0)
          ostr_ << ", " << length;
        ostr_ << '>';
      } break;

    case CORBA2(tk_array): {
        CORBA2(TypeCode_var) content_type = tc->content_type();
        show_name_TypeCode (content_type);
        ostr_ << '[' << tc->length() << ']';
      } break;

    default:
        ostr_ << NameTypeIDL [kind];
        break;
    }
}

// ----------------------------------------------------------------------
// show CORBA::DynAny
// ----------------------------------------------------------------------

void CORBA_OUT::display_DynAny (CORBA2(DynAny_ptr) dyn_any)
{
    skip();
    show_DynAny(dyn_any);
    ostr_ << endl;
}

void CORBA_OUT::show_DynAny (CORBA2(DynAny_ptr) dyn_any)
{
    CORBA2(TypeCode_var) tc = dyn_any->type();

    show_name_TypeCode (tc);
    ostr_ << '(';
    show_DynAny_without_type(dyn_any);
    ostr_ << ')';
}

void CORBA_OUT::show_DynAny_without_type (CORBA2(DynAny_ptr) dyn_any)
{
    CORBA2(ULong) i;

    dyn_any->rewind ();

    CORBA2(TypeCode_var) tc = dyn_any->type();
    tc = GetOrigType(tc);

    CORBA2(TCKind) kind = tc->kind();

    switch ( kind ) {
    case CORBA2(tk_objref): {
        ostr_ << tc -> id();
        CORBA2(Object_ptr) v_obj = dyn_any->get_reference();
        if(CORBA_is_nil(v_obj))
            ostr_ << "<NULL>";
    } break;
    
    case CORBA2(tk_short): {
        CORBA2(Short) v_short = dyn_any->get_short();
        ostr_ << v_short;
      } break;

    case CORBA2(tk_ushort): {
        CORBA2(UShort) v_ushort = dyn_any->get_ushort();
        ostr_ << v_ushort;
      } break;

    case CORBA2(tk_long): {
        CORBA2(Long) v_long = dyn_any->get_long();
        ostr_ << v_long;
      } break;

    case CORBA2(tk_ulong): {
        CORBA2(ULong) v_ulong = dyn_any->get_ulong();
        ostr_ << v_ulong;
      } break;

    case CORBA2(tk_float): {
        CORBA2(Float) v_float = dyn_any->get_float();
        ostr_ << v_float;
      } break;

    case CORBA2(tk_double): {
        CORBA2(Double) v_double = dyn_any->get_double();
        ostr_ << v_double;
      } break;

    case CORBA2(tk_boolean): {
        CORBA2(Boolean) v_boolean = dyn_any->get_boolean();
        if ( v_boolean == CORBA2(TRUE) ) ostr_ << "TRUE";
        else if ( v_boolean == CORBA2(FALSE) ) ostr_ << "FALSE";
        else ostr_ << "UNKNOWN_BOOLEAN";
      } break;

    case CORBA2(tk_octet): {
        CORBA2(Octet) v_octet = dyn_any->get_octet();
        ostr_ << int(v_octet);
      } break;

    case CORBA2(tk_char): {
        CORBA2(Char) v_char = dyn_any->get_char();
        ostr_ << "'" << (char)v_char << "'";
      } break;

    case CORBA2(tk_string): {
        CORBA2(String_var) v_string = dyn_any->get_string();
        ostr_ << '"' << v_string << '"';
      } break;

    case CORBA2(tk_TypeCode): {
        CORBA2(TypeCode_var) v_tc = dyn_any->get_typecode();
        show_name_TypeCode (v_tc);
      } break;

    case CORBA2(tk_any): {
        CORBA2(Any_var) v_any = dyn_any->get_any();
        show_Any(v_any);
      } break; 

    case CORBA2(tk_array): {
        CORBA2(DynArray_var) dyn_array = CORBA2(DynArray)::_narrow (dyn_any);
        for (i=0; i<tc->length(); i++) {
            if ( i != 0 ) ostr_ << ", ";
            CORBA2(DynAny_var) component = dyn_array->current_component();
            show_DynAny (component);
            dyn_array->next ();
	}
      } break;   

    case CORBA2(tk_sequence): {
        CORBA2(DynSequence_var) dyn_sequence =
	    CORBA2(DynSequence)::_narrow (dyn_any);
        for (i=0; i<dyn_sequence->length(); i++) {
            if ( i != 0 ) ostr_ << ", ";
            CORBA2(DynAny_var) component = dyn_sequence->current_component();
            show_DynAny (component);
            dyn_sequence->next ();
	}
      } break;   

    case CORBA2(tk_enum): {
         CORBA2(DynEnum_var) dyn_enum = CORBA2(DynEnum)::_narrow (dyn_any);
         CORBA2(String_var) v_string = dyn_enum->value_as_string();
         ostr_ << v_string;
       } break;   

   case CORBA2(tk_struct): {
        CORBA2(DynStruct_var) dyn_struct =
	    CORBA2(DynStruct)::_narrow (dyn_any);

        ostr_ << endl;
        down ();

        CORBA2(ULong) member_count = tc->member_count();
        for (i=0; i<member_count; i++) {
            CORBA2(String_var) member_name = dyn_struct->current_member_name();
            skip() << member_name << " = ";
            CORBA2(DynAny_var) component = dyn_struct->current_component();
            show_DynAny (component);
            ostr_ << endl;
            dyn_struct->next();  
        }

        up ();
        skip();
      } break;

   case CORBA2(tk_union): {
        CORBA2(DynUnion_var) dyn_union = CORBA2(DynUnion)::_narrow(dyn_any);

        ostr_ << endl;
        down ();

        CORBA2(DynAny_var) component = dyn_union->current_component();

        skip() << "discriminator = ";
        show_DynAny (component);
        ostr_ << endl;

        dyn_union->next();

        component = dyn_union->current_component();

        CORBA2(String_var) member_name = dyn_union->member_name ();
        skip() << member_name << " = ";

        if ( !CORBA2(is_nil) (component) )
            show_DynAny (component);
        else
            ostr_ << "not initialised" << endl;

        ostr_ << endl;

        up ();
        skip();
      } break;

    default:
        assert (false);
        break;
    }
    dyn_any->rewind();
}

// ----------------------------------------------------------------------
// show CORBA::Any
// ----------------------------------------------------------------------

void CORBA_OUT::display_Any (const CORBA2(Any)& any)
{
    CORBA2(DynAny_var) dyn_any = orb_->create_dyn_any (any);
    display_DynAny (dyn_any);
}

void CORBA_OUT::show_Any (const CORBA2(Any)& any)
{
    CORBA2(DynAny_var) dyn_any = orb_->create_dyn_any (any);
    show_DynAny (dyn_any);
}

// ----------------------------------------------------------------------
// show CORBA::AnySeq
// ----------------------------------------------------------------------

void
CORBA_OUT::display_AnySeq (CORBA2(AnySeq)* any_seq)
{
  for (CORBA2(ULong) i=0; i<any_seq->length(); i++) {
      ostr_ << "  ";
      show_Any((*any_seq)[i]);
  }
  ostr_ << endl;
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
//
// global variable
//
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

CORBA2(ORB_var) orb;

CORBA_OUT out (cout);

void
TraceError (const char* expression,
            const char* reason,
            const char* file, int line)
{
  cerr << "There is a problem encounted in the test of the DynAny API" << endl;
  cerr << "  Expression = " << expression << endl;
  cerr << "  Reason     = " << reason << endl;
  cerr << "  File       = " << file << endl;
  cerr << "  Line       = " << line << endl;
  exit (1);
}

// ----------------------------------------------------------------------

int main (int argc, char** argv)
{
    // Initialisation
    orb = CORBA2(ORB_init) (argc,argv);
    out.set_orb (orb);
    out.down ();

    if(argc < 2)
    {
	cerr << "Usage: " << argv[0] << " database" << endl;
	return 1;
    }

    ifstream in; // Must use open(name), not ifstream in(name) (VC++ bug)
    in.open(argv[1]);
    if(in.fail())
    {
	cerr << argv[0] << ": open failed " << argv[1] << endl;
	return 1;
    }

    int rec = 0;

    while(!in.eof())
    {
	CORBA_String_var cmd = CORBA_string_dup("");

	do
	{
	    char str[8192];
	    str[0] = 0; // At least required for HP/UX
	    in.get(str, 8192);
	    cmd += str;
	} while(!in.eof() && in.peek() != '\n');
	if(!in.eof())
	    in.get();

	if(!strlen(cmd.in()))
	    break;

	//
	// Convert the octet character string to an octet sequence.
	//
	CORBA_Octet* const buf = OBAsciiToOctets(cmd);
	const CORBA_Octet* oct = buf;

	//
	// Unmarshal the endianness, and the any.
	//
	CORBA_Boolean endian;
	OBUnmarshal(endian, oct, false);
	CORBA_Any any;
	OBUnmarshal(any, oct, endian != OBEndian);
	
	delete[] buf;

	cout << "Record: " << rec++ << endl;
	out.display_Any(any);
    }

    in.close();

    return 0;
}

// ----------------------------------------------------------------------
// end of file
// ----------------------------------------------------------------------
