// -*- C++ -*-
// $Id: Policy_Set.cpp 91628 2010-09-07 11:11:12Z johnnyw $

#include "tao/Policy_Set.h"
#include "tao/SystemException.h"
#include "tao/debug.h"
#include "ace/CORBA_macros.h"

#if !defined (__ACE_INLINE__)
# include "tao/Policy_Set.inl"
#endif /* ! __ACE_INLINE__ */

TAO_BEGIN_VERSIONED_NAMESPACE_DECL

TAO_Policy_Set::TAO_Policy_Set (TAO_Policy_Scope scope)
  : scope_ (scope)
{
  for (unsigned int i = 0; i < TAO_CACHED_POLICY_MAX_CACHED; ++i)
    this->cached_policies_[i] = 0;
}

TAO_Policy_Set::~TAO_Policy_Set (void)
{
  try
    {
      this->cleanup_i ();
    }
  catch (const ::CORBA::Exception&)
    {
      // Ignore exceptions...
    }
}

TAO_Policy_Set::TAO_Policy_Set (const TAO_Policy_Set &rhs)
  : scope_ (rhs.scope_)
{
  // Initialize the cache.
  for (int i = 0; i < TAO_CACHED_POLICY_MAX_CACHED; ++i)
    {
      this->cached_policies_[i] = 0;
    }

  // Copy over the policy list.
  this->policy_list_.length (rhs.policy_list_.length ());

  try
    {
      for (CORBA::ULong i = 0; i < rhs.policy_list_.length (); ++i)
        {
          CORBA::Policy_ptr policy = rhs.policy_list_[i];

          if (CORBA::is_nil (policy))
            {
              continue;
            }

          CORBA::Policy_var copy = policy->copy ();

          TAO_Cached_Policy_Type const cached_type = copy->_tao_cached_type ();

          // Add the "cacheable" policies into the cache.
          if (cached_type != TAO_CACHED_POLICY_UNCACHED && cached_type >= 0)
            {
              this->cached_policies_[cached_type] = copy.ptr ();
            }

          this->policy_list_[i] = copy._retn ();
        }
    }
  catch (const ::CORBA::Exception& ex)
    {
      if (TAO_debug_level > 4)
        ex._tao_print_exception ("TAO_Policy_Set::TAO_Policy_Set");

      // "Try" to make this recoverable as we must have run out of memory.
      this->policy_list_.length (0);
    }
}

void
TAO_Policy_Set::copy_from (TAO_Policy_Set *source)
{
  if (source == 0)
    {
      return;
    }

  this->cleanup_i ();

  for (CORBA::ULong i = 0; i < source->policy_list_.length (); ++i)
    {
      CORBA::Policy_ptr policy = source->policy_list_[i];

      if (CORBA::is_nil (policy))
        {
          continue;
        }

      if (! this->compatible_scope (policy->_tao_scope()))
        {
          throw ::CORBA::NO_PERMISSION ();
        }

      CORBA::Policy_var copy = policy->copy ();

      CORBA::ULong const length = this->policy_list_.length ();
      this->policy_list_.length (length + 1);

      TAO_Cached_Policy_Type const cached_type =
        copy->_tao_cached_type ();

      // Add the "cacheable" policies into the cache.
      if (cached_type != TAO_CACHED_POLICY_UNCACHED
          && cached_type >= 0)
        {
          this->cached_policies_[cached_type] = copy.ptr ();
        }

      this->policy_list_[length] = copy._retn ();
    }
}

void
TAO_Policy_Set::cleanup_i (void)
{
  CORBA::ULong const len = this->policy_list_.length ();
  // Cleanup the policy list.
  for (CORBA::ULong i = 0; i < len; ++i)
    {
      this->policy_list_[i]->destroy ();
      this->policy_list_[i] = CORBA::Policy::_nil ();
    }

  this->policy_list_.length (0);

  // Cleanup the cache.
  for (CORBA::ULong j = 0; j < TAO_CACHED_POLICY_MAX_CACHED; ++j)
    {
      this->cached_policies_[j] = 0;
    }
}

  // @@ !!! Add comments regarding Policy lifetimes, etc.
void
TAO_Policy_Set::set_policy_overrides (const CORBA::PolicyList &policies,
                                      CORBA::SetOverrideType set_add)
{
  // @@ The spec does not say what to do on this case.
  if (set_add != CORBA::SET_OVERRIDE && set_add != CORBA::ADD_OVERRIDE)
    {
      throw ::CORBA::BAD_PARAM ();
    }

  if (set_add == CORBA::SET_OVERRIDE)
    {
      this->cleanup_i ();
    }

  // Flag, indicating whether we have already overridden
  // RTCORBA::ServerProtocolPolicy during this call.
  bool server_protocol_set = false;

  CORBA::ULong const plen = policies.length ();

  for (CORBA::ULong i = 0; i < plen; ++i)
    {
      CORBA::Policy_ptr policy = policies[i];

      if (CORBA::is_nil (policy))
        {
          continue;
        }

      CORBA::PolicyType const policy_type = policy->policy_type ();

      if (policy_type == TAO_RT_SERVER_PROTOCOL_POLICY_TYPE)
        {
          // Only one ServerProtocolPolicy should be included in a
          // given PolicyList (section 4.15.2 of RTCORBA 1.0, i.e.,
          // ptc/99-05-03).
          // User-caused exceptional conditions can leave the Policy
          // Manager in an inconsistent state.  It is the
          // responsibility of the user to return it to consistent state.
          if (server_protocol_set)
            {
              throw ::CORBA::INV_POLICY ();
            }

          server_protocol_set = true;
        }

      this->set_policy (policy);
    }
}

void
TAO_Policy_Set::set_policy (const CORBA::Policy_ptr policy)
{
  if (! this->compatible_scope (policy->_tao_scope()))
    {
      throw ::CORBA::NO_PERMISSION ();
    }

  CORBA::PolicyType const policy_type = policy->policy_type ();

  CORBA::Policy_var copy = policy->copy ();

  CORBA::ULong j = 0;
  CORBA::ULong const length = this->policy_list_.length ();

  while (j != length)
    {
      CORBA::ULong const current =
        this->policy_list_[j]->policy_type ();

      if (current == policy_type)
        {
          this->policy_list_[j]->destroy ();

          this->policy_list_[j] = copy.ptr ();
          break;
        }

      ++j;
    }

  if (j == length)
    {
      this->policy_list_.length (length + 1);
      this->policy_list_[j] = copy.ptr ();
    }

  // If this is a policy that gets accessed on the critical path,
  // save a pointer to it in the cache.
  TAO_Cached_Policy_Type const cached_policy_type =
    policy->_tao_cached_type ();

  if (cached_policy_type != TAO_CACHED_POLICY_UNCACHED
      && cached_policy_type >= 0)
    {
      this->cached_policies_[cached_policy_type] = copy.ptr ();
    }

  // Transfer ownership to the policy list.
  (void) copy._retn ();
}

CORBA::PolicyList *
TAO_Policy_Set::get_policy_overrides (const CORBA::PolicyTypeSeq &types)
{
  CORBA::ULong const slots = types.length ();
  CORBA::PolicyList *policy_list_ptr = 0;

  if (slots == 0)
    {
      // Copy our own policy list.
      ACE_NEW_THROW_EX (policy_list_ptr,
                        CORBA::PolicyList (this->policy_list_),
                        CORBA::NO_MEMORY ());

      return policy_list_ptr;
    }

  ACE_NEW_THROW_EX (policy_list_ptr,
                    CORBA::PolicyList (slots),
                    CORBA::NO_MEMORY ());

  CORBA::PolicyList_var policy_list (policy_list_ptr);
  policy_list->length (slots);
  CORBA::ULong n = 0;

  for (CORBA::ULong j = 0; j < slots; ++j)
    {
      CORBA::ULong const slot = types[j];
      CORBA::ULong const length = this->policy_list_.length ();

      for (CORBA::ULong i = 0; i < length; ++i)
        {
          CORBA::ULong const current =
            this->policy_list_[i]->policy_type ();

          if (current != slot)
            {
              continue;
            }

          policy_list[n++] =
            CORBA::Policy::_duplicate (this->policy_list_[i]);
          break;
        }
    }

  policy_list->length (n);  // Truncate buffer if necessary.

  return policy_list._retn ();
}

CORBA::Policy_ptr
TAO_Policy_Set::get_policy (CORBA::PolicyType type)
{
  CORBA::ULong const length = this->policy_list_.length ();

  for (CORBA::ULong i = 0; i < length; ++i)
    {
      CORBA::PolicyType const current = this->policy_list_[i]->policy_type ();

      if (current == type)
        {
          return CORBA::Policy::_duplicate (this->policy_list_[i]);
        }
    }

  return CORBA::Policy::_nil ();
}

CORBA::Policy_ptr
TAO_Policy_Set::get_cached_const_policy (TAO_Cached_Policy_Type type) const
{
  if (type != TAO_CACHED_POLICY_UNCACHED && type < TAO_CACHED_POLICY_MAX_CACHED)
    {
      return this->cached_policies_[type];
    }

  return CORBA::Policy::_nil ();
}

CORBA::Policy_ptr
TAO_Policy_Set::get_cached_policy (TAO_Cached_Policy_Type type)
{
  if (type != TAO_CACHED_POLICY_UNCACHED && type < TAO_CACHED_POLICY_MAX_CACHED)
    {
      return CORBA::Policy::_duplicate (this->cached_policies_[type]);
    }

  return CORBA::Policy::_nil ();
}

TAO_END_VERSIONED_NAMESPACE_DECL
