// -*- C++ -*-

#include "IORManipulation.h"

#include "tao/MProfile.h"
#include "tao/Profile.h"
#include "tao/Stub.h"
#include "tao/ORB_Core.h"

#include "ace/Auto_Ptr.h"

ACE_RCSID (tao,
           IORManipulation,
           "IORManipulation.cpp,v 1.11 2002/03/08 05:33:09 ossama Exp")


TAO_IOR_Manipulation_impl::TAO_IOR_Manipulation_impl (void)
{
}

TAO_IOR_Manipulation_impl::~TAO_IOR_Manipulation_impl (void)
{
}

CORBA::Object_ptr
TAO_IOR_Manipulation_impl::merge_iors (
    const TAO_IOP::TAO_IOR_Manipulation::IORList & iors
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::EmptyProfileList,
                   TAO_IOP::Duplicate,
                   TAO_IOP::Invalid_IOR))
{
  // we need to create a new CORBA::Object which has the union of the
  // two profile lists.  However, if any profiles are duplicates (i.e. in
  // bott lisis) then an exception is raised.

  // Deterinine how many profiles we have
  // Get an estimate of the size - pfile count could change since we
  // neither lock nor get a copy in this loop.
  CORBA::ULong i, count=0;
  for (i = 0; i < iors.length (); i++)
    {
      count += iors[i]->_stubobj ()->base_profiles ().profile_count ();
    }

  // make sure we have some profiles
  if (count == 0)
    ACE_THROW_RETURN (TAO_IOP::EmptyProfileList (),
                      CORBA::Object::_nil ());

  // initialize with estimated pfile count.
  TAO_MProfile Merged_Profiles (count);

  // get the profile lists, start by initialize the composite reference
  // by using the first Object.  Then for each subsequent Object verify
  // they are the same type and they do not have duplicate profiles.
  auto_ptr<TAO_MProfile> tmp_pfiles (iors[0]->_stubobj ()->make_profiles ());
  if (Merged_Profiles.add_profiles (tmp_pfiles.get ())< 0)
    ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                      CORBA::Object::_nil ());
  CORBA::String_var id =
    CORBA::string_dup (iors[0]->_stubobj ()->type_id.in ());

  for (i = 1; i < iors.length () ; i++)
    {
      // this gets a copy of the MProfile, hense the auto_ptr;

      ACE_AUTO_PTR_RESET (tmp_pfiles,
                          iors[i]->_stubobj ()->make_profiles (),
                          TAO_MProfile);

      // check to see if any of the profile in tmp_pfiles are already
      // in Merged_Profiles.  If so raise exception.
      if (Merged_Profiles.is_equivalent (tmp_pfiles.get ()))
        ACE_THROW_RETURN (TAO_IOP::Duplicate (),
                          CORBA::Object::_nil ());

      // If the object type_id's differ then raise an exception.
      if (id.in () && iors[i]->_stubobj ()->type_id.in () &&
          ACE_OS::strcmp (id.in (), iors[i]->_stubobj ()->type_id.in ()))
        ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                          CORBA::Object::_nil ());

      // append profiles
      if (Merged_Profiles.add_profiles (tmp_pfiles.get ()) < 0)
        ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                          CORBA::Object::_nil ());

    }

  // MS C++ knows nothing about reset!
  // tmp_pfiles.reset (0); // get rid of last MProfile

  TAO_ORB_Core *orb_core = TAO_ORB_Core_instance ();

  TAO_Stub *stub = orb_core->create_stub (id._retn (), // give the id string
                                          Merged_Profiles
                                          ACE_ENV_ARG_PARAMETER);
  ACE_CHECK_RETURN (CORBA::Object::_nil ());

  // Make the stub memory allocation exception safe for the duration
  // of this method.
  TAO_Stub_Auto_Ptr safe_stub (stub);

  // Create the CORBA level proxy
  CORBA::Object_ptr temp_obj = CORBA::Object::_nil ();
  ACE_NEW_THROW_EX (temp_obj,
                    CORBA::Object (safe_stub.get ()),
                    CORBA::NO_MEMORY ());

  CORBA::Object_var new_obj = temp_obj;

  ACE_CHECK_RETURN (CORBA::Object::_nil ());

  // Clean up in case of errors.
  if (CORBA::is_nil (new_obj.in ()))
    {
      ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                        CORBA::Object::_nil ());
    }

  // Release ownership of the pointers protected by the auto_ptrs since they
  // no longer need to be protected by this point.
  stub = safe_stub.release ();

  return new_obj._retn ();
}

CORBA::Object_ptr
TAO_IOR_Manipulation_impl::add_profiles (
    CORBA::Object_ptr ior1,
    CORBA::Object_ptr ior2
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::EmptyProfileList,
                   TAO_IOP::Duplicate,
                   TAO_IOP::Invalid_IOR))
{

  // Get an estimate of the number of profiles
  CORBA::Object_ptr buffer [2];
  buffer [0] = ior1;
  buffer [1] = ior2;
  TAO_IOP::TAO_IOR_Manipulation::IORList iors (2, 2, buffer, 0);
  return this->merge_iors (iors ACE_ENV_ARG_PARAMETER);
}

CORBA::Object_ptr
TAO_IOR_Manipulation_impl::remove_profiles (
    CORBA::Object_ptr ior1,
    CORBA::Object_ptr ior2
    ACE_ENV_ARG_DECL)
      ACE_THROW_SPEC ((
        CORBA::SystemException,
        TAO_IOP::Invalid_IOR,
        TAO_IOP::EmptyProfileList,
        TAO_IOP::NotFound
      ))
{
  // First verify they are the same type!
  CORBA::String_var id =
    CORBA::string_dup (ior1->_stubobj ()->type_id.in ());
  if (id.in () && ior2->_stubobj ()->type_id.in () &&
      ACE_OS::strcmp (id.in (), ior2->_stubobj ()->type_id.in ()))
    ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                      CORBA::Object::_nil ());

  // Since we are removing from ior1 ...
  CORBA::ULong count = ior1->_stubobj ()->base_profiles ().profile_count ();

  // make sure we have some profiles
  if (count == 0 ||
      ior2->_stubobj ()->base_profiles ().profile_count () == 0)
    ACE_THROW_RETURN (TAO_IOP::EmptyProfileList (),
                      CORBA::Object::_nil ());

  // initialize with estimated pfile count.
  TAO_MProfile Diff_Profiles (count);

  auto_ptr<TAO_MProfile> tmp_pfiles (ior1->_stubobj ()->make_profiles ());
  if (Diff_Profiles.add_profiles (tmp_pfiles.get ()) < 0)
    ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                      CORBA::Object::_nil ());

  ACE_AUTO_PTR_RESET (tmp_pfiles,
                      ior2->_stubobj ()->make_profiles (),
                      TAO_MProfile);

  if (Diff_Profiles.remove_profiles (tmp_pfiles.get ()) < 0)
    ACE_THROW_RETURN (TAO_IOP::NotFound (),
                      CORBA::Object::_nil ());

  // MS C++ knows nothing about reset!
  // tmp_pfiles.reset (0); // get rid of last MProfile

  TAO_ORB_Core *orb_core = TAO_ORB_Core_instance ();

  TAO_Stub *stub = orb_core->create_stub (id._retn (), // give the id string
                                          Diff_Profiles
                                          ACE_ENV_ARG_PARAMETER);
  ACE_CHECK_RETURN (CORBA::Object::_nil ());

  // Make the stub memory allocation exception safe for the duration
  // of this method.
  TAO_Stub_Auto_Ptr safe_stub (stub);

  // Create the CORBA level proxy
  CORBA::Object_ptr temp_obj = CORBA::Object::_nil ();
  ACE_NEW_THROW_EX (temp_obj,
                    CORBA::Object (safe_stub.get ()),
                    CORBA::NO_MEMORY ());

  CORBA::Object_var new_obj = temp_obj;

  ACE_CHECK_RETURN (CORBA::Object::_nil ());

  // Clean up in case of errors.
  if (CORBA::is_nil (new_obj.in ()))
    {
      ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                        CORBA::Object::_nil ());
    }

  // Exception safety is no longer an issue by this point so release
  // the TAO_Stub from the TAO_Stub_Auto_Ptr.
  stub = safe_stub.release ();

  return new_obj._retn ();
}

CORBA::Boolean
TAO_IOR_Manipulation_impl::set_property (
    TAO_IOP::TAO_IOR_Property_ptr prop,
    CORBA::Object_ptr ior
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::Invalid_IOR,
                   TAO_IOP::Duplicate))
{
  // make sure we have some profiles
  if (ior->_stubobj ()->base_profiles ().profile_count () == 0)
    ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                      0);

  // Call the implementation object to
  return prop->set_property (ior
                             ACE_ENV_ARG_PARAMETER);
}

CORBA::Boolean
TAO_IOR_Manipulation_impl::set_primary (
    TAO_IOP::TAO_IOR_Property_ptr prop,
    CORBA::Object_ptr ior1,
    CORBA::Object_ptr ior2
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::Invalid_IOR,
                   TAO_IOP::Duplicate,
                   TAO_IOP::MultiProfileList))
{
  // make sure we have some profiles in IOR2
  if (ior2->_stubobj ()->base_profiles ().profile_count () == 0)
    ACE_THROW_RETURN (TAO_IOP::Invalid_IOR (),
                      0);

  // Make sure we have only one profile in IOR1
  // @@ Will fail if the object has been
  /*if (ior1->_stubobj ()->base_profiles ().profile_count () > 1)
    ACE_THROW_RETURN (TAO_IOP::MultiProfileList (),
    0);*/

  // Call the callback object to do the rest of the processing.
  return prop->set_primary (ior1,
                            ior2
                            ACE_ENV_ARG_PARAMETER);
}

CORBA::Object_ptr
TAO_IOR_Manipulation_impl::get_primary (
      TAO_IOP::TAO_IOR_Property_ptr prop,
      CORBA::Object_ptr ior
      ACE_ENV_ARG_DECL
    )
    ACE_THROW_SPEC ((
      CORBA::SystemException,
      TAO_IOP::NotFound
    ))
{
  // make sure we have some profiles in IOR
  if (ior->_stubobj ()->base_profiles ().profile_count () == 0)
    ACE_THROW_RETURN (TAO_IOP::NotFound (), 0);
  // @@ Bala: this was throwing TAO_IOP::Invalid_IOR, but it was not
  // in the throw spec, that will result in a CORBA::UNKNOWN at
  // run-time (if it does not crash).  Any idea about what is going on
  // here?

  return prop->get_primary (ior
                            ACE_ENV_ARG_PARAMETER);
}

CORBA::Boolean
TAO_IOR_Manipulation_impl::is_primary_set (
    TAO_IOP::TAO_IOR_Property_ptr prop,
    CORBA::Object_ptr ior
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  return prop->is_primary_set (ior ACE_ENV_ARG_PARAMETER);
}

CORBA::ULong
TAO_IOR_Manipulation_impl::is_in_ior (
    CORBA::Object_ptr ior1,
    CORBA::Object_ptr ior2
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::NotFound))
{
  CORBA::ULong count = 0;
  TAO_Profile *pfile1, *pfile2;
  auto_ptr<TAO_MProfile> tmp_pfiles1 (ior1->_stubobj ()->make_profiles ());
  auto_ptr<TAO_MProfile> tmp_pfiles2 (ior2->_stubobj ()->make_profiles ());

  tmp_pfiles1->rewind ();
  while ((pfile1 = tmp_pfiles1->get_next ()) > 0)
    {
      tmp_pfiles2->rewind ();
      while ((pfile2 = tmp_pfiles2->get_next ()) > 0)
        {
          if (pfile1->is_equivalent (pfile2))
            count++;
        }
    }

  if (count == 0)
    ACE_THROW_RETURN (TAO_IOP::NotFound (),
                      0);

  return count;
}

CORBA::ULong
TAO_IOR_Manipulation_impl::get_profile_count (
    CORBA::Object_ptr ior
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException,
                   TAO_IOP::EmptyProfileList))
{
  CORBA::ULong count;
  count = ior->_stubobj ()->base_profiles ().profile_count ();

  if (count == 0)
    ACE_THROW_RETURN (TAO_IOP::EmptyProfileList (),
                      0);

  return count;
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)

template class auto_ptr<TAO_MProfile>;
template class ACE_Auto_Basic_Ptr<TAO_MProfile>;

#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)

#pragma instantiate auto_ptr<TAO_MProfile>
#pragma instantiate ACE_Auto_Basic_Ptr<TAO_MProfile>

#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */
