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

#include <OB/CORBA.h>
#include <OB/Util.h>
#include <OB/Narrow_impl.h>
#include <OB/Hashtable.h>
#include <OB/HashtableI.h>
#include <OB/Properties.h>
#include <OB/Hashers.h>
#include <OB/INS.h>

#include <OB/OBNaming_skel.h>

#include <Types.h>

#include <OBNaming_impl.h>

#include <stdio.h>
#include <NamingDatabase.h>

#include <time.h> // For time...
#include <ctype.h> // For isdigit, isalpha, etc.

OB_MAKE_NARROW_IMPL_1(CosNaming_OBNamingContext_impl,
                      CosNaming_OBNamingContext_skel)

//
// Running count for generating object-keys for naming contexts.
//
int CosNaming_OBNamingContext_impl::count_ = 0;
#ifdef HAVE_JTC
JTCMutex CosNaming_OBNamingContext_impl::countMut_;
#endif

// ----------------------------------------------------------------------
// Hashtable methods
// ----------------------------------------------------------------------

CORBA_ULong
OBHash(const CosNaming_NameComponent& n)
{
    return OBHash(n.id);
}

bool
OBHashCompare(const CosNaming_NameComponent& k1,
	      const CosNaming_NameComponent& k2)
{
    return (strcmp(k1.id, k2.id) == 0) && (strcmp(k1.kind, k2.kind) == 0);
}

// ----------------------------------------------------------------------
// NcOrObjBinding
// ----------------------------------------------------------------------

class NcOrObjBinding : public OBRefCount OB_COMMA_MUTEX
{
    CosNaming_OBNamingContext::NcOrObj ncOrObj_;
    CosNaming_NameComponent comp_;
    long timestamp_;

public:

    NcOrObjBinding(const CosNaming_NameComponent& nc,
                   CosNaming_OBNamingContext::NcOrObj ncOrObj,
                   long timestamp)
        : ncOrObj_(ncOrObj),
          comp_(nc),
          timestamp_(timestamp)
    {
    }
        
    static inline NcOrObjBinding_ptr _duplicate(NcOrObjBinding_ptr p)
    {
	if(p)
	    p -> _OB_incRef();
	return p;
    }

    static inline NcOrObjBinding_ptr _nil()
    {
	return 0;
    }

    CosNaming_Binding
    toBinding() const
    {
        OB_SYNCHRONIZED

        CosNaming_Binding b;

        b.binding_name.length(1);
        b.binding_name[0].id = comp_.id;
        b.binding_name[0].kind = comp_.kind;
        b.binding_type = ncOrObj_._d();

        return b;
    }

    CosNaming_OBNamingContext::ExtendedBinding
    toExtBinding() const
    {
        OB_SYNCHRONIZED

        CosNaming_OBNamingContext::ExtendedBinding b;

        b.binding_name.length(1);
        b.binding_name[0].id = comp_.id;
        b.binding_name[0].kind = comp_.kind;
        b.ncOrObj = ncOrObj_;
        b.timestamp = timestamp_;

        return b;
    }

    void
    rename(const CosNaming_NameComponent& nc)
    {
        OB_SYNCHRONIZED

        comp_.id = nc.id;
        comp_.kind = nc.kind;
    }

    CosNaming_OBNamingContext::NcOrObj
    ncOrObj() const
    {
        OB_SYNCHRONIZED
        return ncOrObj_;
    }

    void
    rebind(const CosNaming_Name& n,
           CosNaming_OBNamingContext::NcOrObj ncOrObj,
           long timestamp)
    {
        OB_SYNCHRONIZED

        //
        // Contexts must be rebound to contexts, objects to
        // objects.
        //
        if(ncOrObj._d() != ncOrObj_._d())
        {
            //
            // INS (98-10-11) 4-12:
            //
            // If rebind or rebind_context raise a NotFound exception
            // because an already existing binding is of the wrong
            // type, the rest_of_name member of the exception has a
            // sequence length of 1.
            //
            if(ncOrObj_._d() == CosNaming_ncontext)
                throw CosNaming_NamingContext::NotFound(
                    CosNaming_NamingContext::not_object, n);
            else
                throw CosNaming_NamingContext::NotFound(
                    CosNaming_NamingContext::not_context, n);
        }
        
        //
        // Update the binding information.
        //
        comp_ = n[0];
        ncOrObj_ = ncOrObj;
        timestamp_ = timestamp;
    }

    bool
    isContext() const
    {
        OB_SYNCHRONIZED
        return ncOrObj_._d() == CosNaming_ncontext;
    }

    CORBA_Object_ptr 
    object() const
    {
        OB_SYNCHRONIZED

        CORBA_Object_ptr obj;
        if(ncOrObj_._d() == CosNaming_ncontext)
            obj = ncOrObj_.nc();
        else
            obj = ncOrObj_.obj();

        return CORBA_Object::_duplicate(obj);
    }

    CosNaming_NamingContext_ptr
    context(const CosNaming_Name& n) const
    {
        OB_SYNCHRONIZED

        //
        // Binding must belong to a context.
        //
        if(ncOrObj_._d() != CosNaming_ncontext)
            throw CosNaming_NamingContext::NotFound(
                CosNaming_NamingContext::not_context, n);
        return CosNaming_NamingContext::_duplicate(ncOrObj_.nc());
    }
};

// ----------------------------------------------------------------------
// External, non-inline duplicate/release for templates
// ----------------------------------------------------------------------

void
OBDuplicate(NcOrObjBinding_ptr p)
{
    if(p)
	p -> _OB_incRef();
}

void
OBRelease(NcOrObjBinding_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

inline void
CORBA_release(NcOrObjBinding_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

inline CORBA_Boolean
CORBA_is_nil(NcOrObjBinding_ptr p)
{
    return p == 0;
}

// ----------------------------------------------------------------------
// Template instantiations
// ----------------------------------------------------------------------

#ifndef HAVE_NO_EXPLICIT_TEMPLATES
template class OBObjVar< NcOrObjBinding >;
template class OBHashtable<CosNaming_NameComponent, NcOrObjBinding_var,
                           OB_LOCK_STRATEGY_SYNCHRONIZED>;
template class OBHashtable<CORBA_String_var, CosNaming_NamingContext_ptr,
                           OB_LOCK_STRATEGY_SYNCHRONIZED>;
#else
#ifdef HAVE_PRAGMA_DEFINE
#pragma define(OBObjVar< NcOrObjBinding >)
#pragma define(OBHashtable<CosNaming_NameComponent, NcOrObjBinding_var,
	                   OB_LOCK_STRATEGY_SYNCHRONIZED>>)
#pragma define(OBHashtable<CORBA_String_var, CosNaming_NamingContext_ptr,
	                   OB_LOCK_STRATEGY_SYNCHRONIZED>)
#endif
#endif

// ----------------------------------------------------------------------
// CosNaming_OBNamingContext_impl private member implementation
// ----------------------------------------------------------------------

void
CosNaming_OBNamingContext_impl::cleanup()
{
    //
    // Remove ourself from the NamingContextSet. This will stop the
    // naming database from dumping this context.
    //
    if(ncs_)
	ncs_ -> remove(key_);

    if(store_)
        store_ -> destroy(myRef_);

    //
    // Disconnect the servant from the ORB.
    //
    orb_ -> disconnect(myRef_);
}

void
CosNaming_OBNamingContext_impl::doBind(
    const CosNaming_Name& n,
    const CosNaming_OBNamingContext::NcOrObj& ncOrObj,
    bool rebind, long timestamp)
{
    //
    // Check if name is valid.
    //
    if(n.length() == 0)
	throw InvalidName();
    if(*n[0].id == '\0' && *n[0].kind == '\0')
	throw InvalidName();

    if(n.length() == 1)
    {
        {
#ifdef HAVE_JTC
            JTCSynchronized sync(*bindings_);
#endif
            NcOrObjBinding_var binding;
            bindings_ -> get(n[0], binding);

            if(!CORBA_is_nil(binding))
            {
                //
                // Name is already bound.
                //
                if(!rebind)
                    throw AlreadyBound();
                
                //
                // Update the binding information.
                //
                binding -> rebind(n, ncOrObj, timestamp);
            }
            else
            {
                //
                // Bind the name and the object.
                //
                // We need to populate the binding information before
                // we insert it in the binding list. Otherwise, it
                // would be possible to resolve an incomplete binding.
                //
                bindings_ -> put(n[0],
                                 new NcOrObjBinding(n[0], ncOrObj, timestamp));
            }
        }

        if(store_)
            store_ -> bind(myRef_, n, ncOrObj, timestamp, rebind);
        return;
    }

    NcOrObjBinding_var binding;
    if(!bindings_ -> get(n[0], binding))
        //
        // Missing node if binding was not found.
        //
        throw NotFound(missing_node, n);

    //
    // The context in which to bind.
    //
    CosNaming_NamingContext_var context = binding -> context(n);
    
    //
    // Create the rest of the name.
    //
    CosNaming_Name rest;
    rest.length(n.length() - 1);
    for(CORBA_ULong i = 1 ; i < n.length() ; i++)
        rest[i - 1] = n[i];
    
    //
    // Let the next context handle the binding.
    //
    if(ncOrObj._d() == CosNaming_ncontext)
    {
        if(rebind)
            context -> rebind_context(rest, ncOrObj.nc());
        else
            context -> bind_context(rest, ncOrObj.nc());
    }
    else
    {
        if(rebind)
            context -> rebind(rest, ncOrObj.obj());
        else
            context -> bind(rest, ncOrObj.obj());
    }
}

//
// Notify all listeners that a binding has been added.
//
void
CosNaming_OBNamingContext_impl::bindingAdded(const NcOrObj& ncOrObj,
					     const CosNaming_Name& n)
{
    //
    // Check whether change occured in this context.
    //
    if(n.length() != 1)
	return;

    if(!noUpdates_)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(listenersMutex_);
#endif
	bool isContext = (ncOrObj._d() == CosNaming_ncontext);
    
#ifdef OB_TRACE
	OBMessageViewer::instance() -> trace(4, "Binding added");
#endif

	for(CORBA_ULong i = 0 ; i < listeners_.length() ; i++)
	{
	    try
	    {
		if(isContext)
		    listeners_[i] -> context_added(myRef_, ncOrObj.nc(), n[0]);
		else
		    listeners_[i] -> object_added(myRef_, ncOrObj.obj(), n[0]);
	    }
	    catch(const CORBA_SystemException&)
	    {
#ifdef OB_TRACE
		OBMessageViewer::instance() ->
		    trace(3, "SystemException: Removing listener");
#endif
		listeners_.remove(i--);
	    }
	}
    }
}

//
// Notify all listeners that a binding has been removed.
//
void
CosNaming_OBNamingContext_impl::bindingRemoved(const CosNaming_Name& n,
					       bool isContext)
{
    //
    // Check whether change occured in this context.
    //
    if(n.length() != 1)
	return;

    if(!noUpdates_)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(listenersMutex_);
#endif
#ifdef OB_TRACE
	OBMessageViewer::instance() -> trace(4, "Binding removed");
#endif
	for(CORBA_ULong i = 0 ; i < listeners_.length() ; i++)
	{
	    try
	    {
		if(isContext)
		    listeners_[i] -> context_removed(myRef_, n[0]);
		else
		    listeners_[i] -> object_removed(myRef_, n[0]);
	    }
	    catch(const CORBA_SystemException&)
	    {
#ifdef OB_TRACE
		OBMessageViewer::instance() ->
		    trace(3, "SystemException: Removing listener");
#endif
		listeners_.remove(i--);
	    }
	}
    }
}

//
// Notify all listeners that a binding has been changed.
//
void
CosNaming_OBNamingContext_impl::bindingChanged(const NcOrObj& ncOrObj,
					       const CosNaming_Name& oldName,
					       const CosNaming_Name& newName)
{
    //
    // Check whether change occured in this context.
    //
    if(oldName.length() != 1)
	return;
    
    if(!noUpdates_)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(listenersMutex_);
#endif
	bool isContext = (ncOrObj._d() == CosNaming_ncontext);

#ifdef OB_TRACE
	OBMessageViewer::instance() -> trace(4, "Binding changed");
#endif
	for(CORBA_ULong i = 0 ; i < listeners_.length() ; i++)
	{
	    try
	    {
		if(isContext)
		    listeners_[i] -> context_changed(myRef_, ncOrObj.nc(),
						     oldName[0], newName[0]);
		else
		    listeners_[i] -> object_changed(myRef_, ncOrObj.obj(),
						    oldName[0], newName[0]);
	    }
	    catch(const CORBA_SystemException&)
	    {
#ifdef OB_TRACE
		OBMessageViewer::instance() ->
		    trace(3, "SystemException: Removing listener");
#endif
		listeners_.remove(i--);
	    }
	}
    }
}

//
// Add stringified-name escapes. The characters '.', '/' and '\\' must
// be escaped with a '\\'.
//
char*
CosNaming_OBNamingContext_impl::addEscapes(const char* s)
{
    CORBA_String_var result = CORBA_string_alloc(::strlen(s));
    *result.inout() = '\0';

    for(; *s != '\0'; s++)
    {
	switch(*s)
	{
	case '.':
	    result += "\\.";
	    break;

	case '/':
	    result += "\\/";
	    break;

	case '\\':
	    result += "\\\\";
	    break;

	default:
	    result += *s;
            break;
	}
    }

    return result._retn();
}

//
// Encode an integer as a hex character.
//
static inline char
EncodeHex(int i)
{
    assert(i >= 0 && i < 16);
    if(i >= 0 && i <= 9)
	return i + '0';
    return (i - 10) + 'A';
}

//
// Escape characters according to URL encoding scheme except for the
// characters listed in 4.6.3
//
char*
CosNaming_OBNamingContext_impl::addURLEscapes(const char* s)
{
    CORBA_String_var result = CORBA_string_alloc(::strlen(s));
    *result.inout() = '\0';

    //
    // As per RFC 2396.
    //
    for(; *s != '\0'; ++s)
    {
	if(isalpha(*s) || isdigit(*s))
	    result += *s;
	else
	{
	    switch(*s)
	    {
	    case ';': case '/': case '?': case ':':
	    case '@': case '&': case '=': case  '+':
	    case '$': case ',': case '-': case '_':
	    case '.': case '!': case '~': case '*':
	    case '\'':case '(': case ')':
		result += *s;
		break;

	    default:
		result += '%';
		result += EncodeHex((*s >> 4) & 0xF);
		result += EncodeHex(*s & 0xF);
	    }
	}
    }

    return result._retn();
}

CosNaming_OBNamingContext_impl*
CosNaming_OBNamingContext_impl::createNewContext()
{
    //
    // Create new unique key. The key is composed of the current time
    // and a running count.
    //
    CORBA_String_var name = CORBA_string_dup("");
    name += ::time(0);
    name += "-";

    {
#ifdef HAVE_JTC
	JTCSynchronized sync(countMut_);
#endif
	name += count_++;
    }

    return new CosNaming_OBNamingContext_impl(orb_, store_, ncs_, name,
					      noUpdates_);
}

// ----------------------------------------------------------------------
// CosNaming_OBNamingContext_impl constructor and destructor
// ----------------------------------------------------------------------

CosNaming_OBNamingContext_impl::CosNaming_OBNamingContext_impl(
    CORBA_ORB_ptr orb,
    NamingDatabase* store,
    NamingContextSet* ncs,
    const char* key,
    bool noUpdates)

    : orb_(CORBA_ORB::_duplicate(orb)),
      key_(CORBA_string_dup(key)),
      bindings_(new CosNaming_OBNamingContext_impl::BindingsHash(1023)),
      ncs_(ncs),
      store_(store),
      noUpdates_(noUpdates),
      destroyed_(false)
{
    bool isRoot = (strcmp(key, "NameService") == 0);

    orb_ -> connect(this, key_);

    myRef_ = _this();
    ncs_ -> put(key_, myRef_.in());

    if(store_)
    {
	if(isRoot)
	    store_ -> info();
        store_ -> create(myRef_, ::time(0));
    }

    if(isRoot)
    {
	const char* prop = OBProperties::instance() ->
			getProperty("ooc.naming.display_root_ior");

	if(prop != 0 && strcmp(prop, "true") == 0)
	{
	    CORBA_String_var s = orb_ -> object_to_string(myRef_);
	    cout << s << endl;
	}
    }
}

CosNaming_OBNamingContext_impl::~CosNaming_OBNamingContext_impl()
{
    delete bindings_;
}

// ----------------------------------------------------------------------
// CosNaming_OBNamingContext_impl public member implementation
// ----------------------------------------------------------------------

//
// Implementation of CosNaming::NamingContext.
//

void
CosNaming_OBNamingContext_impl::bind(const CosNaming_Name& n,
				     CORBA_Object_ptr obj)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    CosNaming_OBNamingContext::NcOrObj ncOrObj;
    ncOrObj.obj(obj);

    doBind(n, ncOrObj, false, ::time(0));

    bindingAdded(ncOrObj, n);
}

void
CosNaming_OBNamingContext_impl::rebind(const CosNaming_Name& n,
				       CORBA_Object_ptr obj)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();
    
    CosNaming_OBNamingContext::NcOrObj ncOrObj;
    ncOrObj.obj(obj);

    try
    {
	doBind(n, ncOrObj, true, ::time(0));
    }
    catch(const AlreadyBound&)
    {
	//
	// AlreadyBound must not be thrown here, so convert it.  This
	// shouldn't happen -- therefore an application error.
	//
	assert(false);
	throw CannotProceed(this, n);
    }
 
    bindingChanged(ncOrObj, n, n);
}

void
CosNaming_OBNamingContext_impl::bind_context(const CosNaming_Name& n,
					     CosNaming_NamingContext_ptr nc)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    if(CORBA_is_nil(nc))
	throw CORBA_BAD_PARAM();
    
    CosNaming_OBNamingContext::NcOrObj ncOrObj;
    ncOrObj.nc(nc);

    doBind(n, ncOrObj, false, ::time(0));

    bindingAdded(ncOrObj, n);
}

void
CosNaming_OBNamingContext_impl::rebind_context(const CosNaming_Name& n,
					       CosNaming_NamingContext_ptr nc)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    if(CORBA_is_nil(nc))
	throw CORBA_BAD_PARAM();

    CosNaming_OBNamingContext::NcOrObj ncOrObj;
    ncOrObj.nc(nc);

    try
    {
	doBind(n, ncOrObj, true, ::time(0));
    }
    catch(const AlreadyBound&)
    {
	//
	// AlreadyBound must not be thrown here, so convert it.  This
	// shouldn't happen -- therefore an application error.
	//
	assert(false);
	throw CannotProceed(this, n);
    }

    bindingChanged(ncOrObj, n, n);
}

CORBA_Object_ptr
CosNaming_OBNamingContext_impl::resolve(const CosNaming_Name& n)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    //
    // Check if name is valid.
    //
    if(n.length() == 0)
	throw InvalidName();
    if(*n[0].id == '\0' && *n[0].kind == '\0')
	throw InvalidName();

    NcOrObjBinding_var binding;
    if(!bindings_ -> get(n[0], binding))
        //
        // Missing node if binding was not found.
        //
        throw NotFound(missing_node, n);

    if(n.length() == 1)
        //
        // Object or context found, return it.
        //
        return binding -> object();

    //
    // Context in which to resolve.
    //
    CosNaming_NamingContext_var context = binding -> context(n);

    //
    // Create the rest of the name.
    //
    CosNaming_Name rest;
    rest.length(n.length() - 1);
    for(CORBA_ULong i = 1 ; i < n.length() ; i++)
	rest[i - 1] = n[i];
    
    //
    // Have the context handle the resolving of the name.
    //
    return context -> resolve(rest);
}

void
CosNaming_OBNamingContext_impl::unbind(const CosNaming_Name& n)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    //
    // Check if name is valid.
    //
    if(n.length() == 0)
	throw InvalidName();
    if(*n[0].id == '\0' && *n[0].kind == '\0')
	throw InvalidName();

    NcOrObjBinding_var binding;

    if(n.length() == 1)
    {
        {
#ifdef HAVE_JTC
            JTCSynchronized sync(*bindings_);
#endif

            if(!bindings_ -> get(n[0], binding))
                //
                // Missing node if binding was not found.
                //
                throw NotFound(missing_node, n);

            //
            // Do unbind.
            //
            bindings_ -> remove(n[0]);
        }

        if(store_)
            store_ -> unbind(myRef_, n);

	bindingRemoved(n, binding -> isContext());
        return;
    }

    if(!bindings_ -> get(n[0], binding))
        //
        // Missing node if binding was not found.
        //
        throw NotFound(missing_node, n);

    //
    // The context in which to unbind.
    //
    CosNaming_NamingContext_var context = binding -> context(n);

    //
    // Create the rest of the name.
    //
    CosNaming_Name rest;
    rest.length(n.length() - 1);
    for(CORBA_ULong i = 1 ; i < n.length() ; i++)
        rest[i - 1] = n[i];
    
    //
    // Let the next context handle the resolving of the name.
    //
    context -> unbind(rest);
}

CosNaming_NamingContext_ptr
CosNaming_OBNamingContext_impl::new_context()
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    CosNaming_OBNamingContext_impl* nc = createNewContext();
    return nc -> _this();
}

CosNaming_NamingContext_ptr
CosNaming_OBNamingContext_impl::bind_new_context(const CosNaming_Name& n)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    CosNaming_OBNamingContext_impl* newContext = createNewContext();
    CosNaming_NamingContext_var nc = newContext -> _this();

    try
    {
	bind_context(n, nc);
    }
    catch(const CORBA_SystemException& ex)
    {
	if(ex.completed() == CORBA_COMPLETED_NO)
	{
	    newContext -> cleanup();
	    newContext -> _OB_releaseInternal();
	    CORBA_release(newContext);
	}

	ex._raise();
    }
    catch(const CORBA_UserException& ex)
    {
	newContext -> cleanup();
	newContext -> _OB_releaseInternal();
	CORBA_release(newContext);

	ex._raise();
    }

    return nc._retn();
}

void
CosNaming_OBNamingContext_impl::destroy()
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    CosNaming_OBNamingContext_impl::BindingsHash::Enumerator keys =
	bindings_ -> keys();
    if(keys.length() != 0)
	throw NotEmpty();

    destroyed_ = true;
    
    cleanup();

    //
    // Release the reference that we hold to ourself.
    //
    CORBA_release(this);

    //
    // Release the object-reference to ourself.
    //
    myRef_ = CosNaming_NamingContext::_nil();
}

void
CosNaming_OBNamingContext_impl::list(CORBA_ULong how_many,
				     CosNaming_BindingList*& bl,
				     CosNaming_BindingIterator_ptr& bi)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    //
    // Note that we have to be careful in this method. Since we don't
    // synchronize the set of bindings_ it's possible that the set of
    // bindings is altered during processing.
    //
    CosNaming_OBNamingContext_impl::BindingsHash::Enumerator keys =
	bindings_ -> keys();

    CORBA_ULong len = keys.length();

    //
    // We want to allocate the smaller of len and how_many.
    //
    CORBA_ULong num = len < how_many ? len : how_many;
    bl = new CosNaming_BindingList(num);

    //
    // Copy the first how_many bindings into bl, taking into account
    // that some bindings may not be present. We may not use the
    // calculated `num' above since some bindings may no longer be
    // present.
    //
    CORBA_ULong i;
    for(i = 0 ; i < len && bl -> length() < how_many ; i++)
    {
	NcOrObjBinding_var binding;
	if(bindings_ -> get(keys[i], binding))
            bl -> append(binding -> toBinding());
    }

    //
    // If we have more bindings then create a BindingIterator with the
    // remainder of the existing bindings, otherwise we return a nil
    // BindingIterator.
    //
    CosNaming_BindingList rest(len - i);
    for(; i < len ; i++)
    {
        NcOrObjBinding_var binding;
        if(bindings_ -> get(keys[i], binding))
            rest.append(binding -> toBinding());
    }
    
    if(rest.length() > 0)
    {
        CosNaming_BindingIterator_impl* biImpl =
	  new CosNaming_BindingIterator_impl(orb_, rest);
        bi = biImpl -> _this();
    }
    else
    {
        bi = CosNaming_BindingIterator::_nil();
    }
}

//
// Implementation of CosNaming::NamingContextExt
//
CosNaming_NamingContextExt::StringName
CosNaming_OBNamingContext_impl::to_string(const CosNaming_Name& name)
{
    CosNaming_NamingContextExt::StringName_var result = CORBA_string_dup("");

    for(CORBA_ULong i = 0 ; i < name.length() ; i++)
    {
	if(i != 0)
	    result += '/';

	CORBA_String_var s = addEscapes(name[i].id);
	result += s;
	if(*name[i].kind != '\0')
	{
	    result += '.';
	    s = addEscapes(name[i].kind);
	    result += s;
	}
    }

    return result._retn();
}

CosNaming_Name*
CosNaming_OBNamingContext_impl::to_name(const char* s)
{
    if(*s == '\0')
	throw CosNaming_NamingContext::InvalidName();

    OBIIOPNameParser parser(s);
    if(!parser.isValid())
	throw InvalidName();

    const OBStrSeq& contents = parser.getContents();

    CosNaming_Name_var name = new CosNaming_Name();
    for(CORBA_ULong i = 0; i < contents.length(); i += 2)
    {
	CosNaming_NameComponent nc;
	nc.id = contents[i];
	nc.kind = contents[i + 1];
	name -> append(nc);
    }
	
    return name._retn();
}

CosNaming_NamingContextExt::URLString
CosNaming_OBNamingContext_impl::to_url(const char* addr, const char* n)
{
    CosNaming_NamingContextExt::URLString_var result =
	CORBA_string_dup("iiopname://");

    result += addr;
    result += '/';

    CORBA_String_var s = addURLEscapes(n);
    result += s;

    return result._retn();
}

CORBA_Object_ptr
CosNaming_OBNamingContext_impl::resolve_str(const char* n)
{
    CosNaming_Name_var name = to_name(n);
    return resolve(name);
}

//
// Implementation of CosNaming::OBNamingContext
//
CosNaming_OBNamingContext::ExtendedBindingList*
CosNaming_OBNamingContext_impl::list_extended()
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    CosNaming_OBNamingContext_impl::BindingsHash::Enumerator keys =
	bindings_ -> keys();
    CORBA_ULong num = keys.length();

    CosNaming_OBNamingContext::ExtendedBindingList_var eb =
	new CosNaming_OBNamingContext::ExtendedBindingList(num);

    for(CORBA_ULong i = 0 ; i < num ; i++)
    {
	NcOrObjBinding_var binding;
	if(bindings_ -> get(keys[i], binding))
            eb -> append(binding -> toExtBinding());
    }

    return eb._retn();
}

#ifdef HAVE_JTC
//
// This helper class is needed to lock and unlock the datastore if it
// exists.
//
class OBDatabaseLockHelper
{
    NamingDatabase* db_;

public:

    OBDatabaseLockHelper(NamingDatabase* db)
	: db_(db)
    {
	if(db_)
	    db_ -> lock();
    }

    ~OBDatabaseLockHelper()
    {
	if(db_)
	    db_ -> unlock();
    }
};
#endif

void
CosNaming_OBNamingContext_impl::rename(const CosNaming_Name& oldName,
				       const CosNaming_Name& newName)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    //
    // Are the names invalid? (Why not pass NameComponents?)
    //
    if(oldName.length() != 1 || newName.length() != 1)
	throw InvalidName();

    if(*newName[0].id == '\0' && *newName[0].kind == '\0')
	throw InvalidName();

    //
    // If the names are the same then we have nothing to do.
    //
    if(strcmp(oldName[0].id, newName[0].id) == 0 &&
       strcmp(oldName[0].kind, newName[0].kind) == 0)
	return;

    CosNaming_OBNamingContext::NcOrObj ncOrObj;

    //
    // We have to synchronize on the bindings and the store_ here
    // since we don't want the set of bindings changed in the middle
    // of the operation. Note that we have to acquire the lock on the
    // store_ before locking the binding set to avoid a deadlock.
    //
    {
#ifdef HAVE_JTC
	OBDatabaseLockHelper dbsync(store_);
	JTCSynchronized sync(*bindings_);
#endif

        //
        // Locking the binding itself is not necessary below since the
        // bindings_ set is locked for the entire time that we have
        // the binding, and it's not possible to acquire a reference
        // to a binding without locking the bindings.
        //
        NcOrObjBinding_var binding;

	//
	// Get the current binding.
 	//
	if(!bindings_ -> get(oldName[0], binding))
	    throw NotFound(missing_node, oldName);
	
	//
	// Check if newName already exists.
	//
	if(bindings_ -> contains(newName[0]))
	    throw AlreadyBound();
	
	//
	// Remove the current, add the new.
	//
	bindings_ -> remove(oldName[0]);

        binding -> rename(newName[0]);
	
	bindings_ -> put(newName[0], binding);

        ncOrObj = binding -> ncOrObj();
    
	if(store_)
	{
	    store_ -> unbind(myRef_, oldName);
	    store_ -> bind(myRef_, newName, ncOrObj, ::time(0), false);
	}
    }

    bindingChanged(ncOrObj, oldName, newName);
}

CORBA_Boolean
CosNaming_OBNamingContext_impl::add_listener(
    CosNaming_BindingListener_ptr listener)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    if(!noUpdates_)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(listenersMutex_);
#endif
	//
	// Check if listener is already registered.
	//
	for(CORBA_ULong i = 0 ; i < listeners_.length() ; i++)
	    if(listener -> _is_equivalent(listeners_[i]))
		return true;

#ifdef OB_TRACE
	OBMessageViewer::instance() -> trace(3, "Adding listener");
#endif
	listeners_.append(CosNaming_BindingListener::_duplicate(listener));
	return true;
    }

    return false;
}

CORBA_Boolean
CosNaming_OBNamingContext_impl::remove_listener(
    CosNaming_BindingListener_ptr listener)
{
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();

    if(!noUpdates_)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(listenersMutex_);
#endif
	for(CORBA_ULong i = 0 ; i < listeners_.length() ; i++)
	{
	    if(listener -> _is_equivalent(listeners_[i]))
	    {
		
#ifdef OB_TRACE
		OBMessageViewer::instance() -> trace(3, "Removing listener");
#endif
		listeners_.remove(i);
		return true;
	    }
	}
    }

    return false;
}

CORBA_Boolean
CosNaming_OBNamingContext_impl::callbacks_active()
{
    return !noUpdates_;
}

//
// Internal methods.
//

const char*
CosNaming_OBNamingContext_impl::_OB_getKey() const
{
    return key_;
}

void
CosNaming_OBNamingContext_impl::_OB_bindWithTimestamp(
    const CosNaming_Name& n,
    const CosNaming_OBNamingContext::NcOrObj& ncOrObj,
    long timestamp)
{
    doBind(n, ncOrObj, false, timestamp);
}

void
CosNaming_OBNamingContext_impl::_OB_releaseInternal()
{
    myRef_ = CosNaming_NamingContext::_nil();
}

// ----------------------------------------------------------------------
// CosNaming_BindingIterator_impl constructor and destructor
// ----------------------------------------------------------------------

CosNaming_BindingIterator_impl::
CosNaming_BindingIterator_impl(CORBA_ORB_ptr orb,
			       const CosNaming_BindingList& bl)
    : orb_(CORBA_ORB::_duplicate(orb)), idx_(0), bl_(bl)
{
}

// ----------------------------------------------------------------------
// CosNaming_BindingIterator public member implementation
// ----------------------------------------------------------------------

CORBA_Boolean
CosNaming_BindingIterator_impl::next_one(CosNaming_Binding*& b)
{
    OB_SYNCHRONIZED

    if(idx_ < bl_.length())
    {
	b = new CosNaming_Binding(bl_[idx_++]);
	return true;
    }

    b = new CosNaming_Binding;
    b -> binding_type = CosNaming_nobject;
    return false;
}

CORBA_Boolean
CosNaming_BindingIterator_impl::next_n(CORBA_ULong how_many,
				       CosNaming_BindingList*& bl)
{
    OB_SYNCHRONIZED

    if(how_many == 0)
	throw CORBA_BAD_PARAM();

    CORBA_ULong len = bl_.length();
    CORBA_ULong num = len - idx_ < how_many ? len - idx_ : how_many;
    
    bl = new CosNaming_BindingList(num);
    bl -> length(num);
    
    for(CORBA_ULong i = 0 ; i < num ; i++)
	(*bl)[i] = bl_[idx_++];

    return num != 0;
}

void
CosNaming_BindingIterator_impl::destroy()
{
    OB_SYNCHRONIZED

    orb_ -> disconnect(this);

    //
    // Release our internal reference.
    //
    CORBA_release(this);
}
