src/core/names.cc
author Craig Dowell <craigdo@ee.washington.edu>
Thu, 26 Mar 2009 17:39:20 -0700
changeset 4298 cc2db3e6bcae
parent 4161 a4747d84fd6a
child 4481 9250a25ee683
permissions -rw-r--r--
Make Names API return void not bool, rescan for Python Bindings

/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <map>
#include "object.h"
#include "log.h"
#include "assert.h"
#include "abort.h"
#include "names.h"
#include "ns3/simulator.h"

namespace ns3 {

NS_LOG_COMPONENT_DEFINE ("Names");

class NameNode
{
public:
  NameNode ();
  NameNode (const NameNode &nameNode);
  NameNode (NameNode *parent, std::string name, Ptr<Object> object);
  NameNode &operator = (const NameNode &rhs);

 ~NameNode ();

  NameNode *m_parent;
  std::string m_name;
  Ptr<Object> m_object;

  std::map<std::string, NameNode *> m_nameMap;
};

NameNode::NameNode ()
  : m_parent (0), m_name (""), m_object (0)
{
}

NameNode::NameNode (const NameNode &nameNode)
{
  m_parent = nameNode.m_parent;
  m_name = nameNode.m_name;
  m_object = nameNode.m_object;
  m_nameMap = nameNode.m_nameMap;
}

NameNode &
NameNode::operator = (const NameNode &rhs)
{
  m_parent = rhs.m_parent;
  m_name = rhs.m_name;
  m_object = rhs.m_object;
  m_nameMap = rhs.m_nameMap;
  return *this;
}

NameNode::NameNode (NameNode *parent, std::string name, Ptr<Object> object)
  : m_parent (parent), m_name (name), m_object (object)
{
}

NameNode::~NameNode ()
{
}

class NamesPriv 
{
public:
  NamesPriv ();
  ~NamesPriv ();

  bool Add (std::string name, Ptr<Object> obj);
  bool Add (std::string path, std::string name, Ptr<Object> object);
  bool Add (Ptr<Object> context, std::string name, Ptr<Object> object);

  bool Rename (std::string oldpath, std::string newname);
  bool Rename (std::string path, std::string oldname, std::string newname);
  bool Rename (Ptr<Object> context, std::string oldname, std::string newname);

  std::string FindName (Ptr<Object> object);
  std::string FindPath (Ptr<Object> object);

  Ptr<Object> Find (std::string name);
  Ptr<Object> Find (std::string path, std::string name);
  Ptr<Object> Find (Ptr<Object> context, std::string name);

  static NamesPriv *Get (void);
  static void Delete (void);
private:
  static NamesPriv **DoGet (bool doCreate);

  NameNode *IsNamed (Ptr<Object>);
  bool IsDuplicateName (NameNode *node, std::string name);

  NameNode m_root;
  std::map<Ptr<Object>, NameNode *> m_objectMap;
};

NamesPriv *
NamesPriv::Get (void)
{
  return *(DoGet (true));
}

NamesPriv **
NamesPriv::DoGet (bool doCreate)
{
  static NamesPriv *ptr = 0;

  if (ptr == 0 && doCreate)
    {
      ptr = new NamesPriv;
      Simulator::ScheduleDestroy (&NamesPriv::Delete);
    }

  return &ptr;
}

void 
NamesPriv::Delete (void)
{
  NS_LOG_FUNCTION_NOARGS ();

  NamesPriv **ptr = DoGet (false);
  delete *ptr;
  *ptr = 0;
}

NamesPriv::NamesPriv ()
{
  NS_LOG_FUNCTION_NOARGS ();

  m_root.m_parent = 0;
  m_root.m_name = "Names";
  m_root.m_object = 0;
}

NamesPriv::~NamesPriv ()
{
  NS_LOG_FUNCTION_NOARGS ();

  //
  // Every name is associated with an object in the object map, so freeing the
  // NameNodes in this map will free all of the memory allocated for the NameNodes
  //
  for (std::map<Ptr<Object>, NameNode *>::iterator i = m_objectMap.begin (); i != m_objectMap.end (); ++i)
    {
      delete i->second;
      i->second = 0;
    }

  m_root.m_parent = 0;
  m_root.m_name = "";
  m_root.m_object = 0;
}

bool
NamesPriv::Add (std::string name, Ptr<Object> object)
{
  NS_LOG_FUNCTION (name << object);
  //
  // This is the simple, easy to use version of Add, so we want it to be flexible.
  // We don't want to force a user to always type the fully qualified namespace 
  // name, so we allow the namespace name to be omitted.  For example, calling
  // Add ("Client/ath0", obj) should result in exactly the same behavior as
  // Add ("/Names/Client/ath0", obj).  Calling Add ("Client", obj) should have
  // the same effect as Add ("Names/Client", obj)
  //
  // The first thing to do, then, is to "canonicalize" the input string to always
  // be a fully qualified name.
  //
  // If we are given a name that begins with "/Names/" we assume that this is a
  // fully qualified path name to the object we want to create.  We split the name
  // into a path string and and a final segment (name) and then call the "Real" Add.
  //
  std::string namespaceName = "/Names";
  std::string::size_type offset = name.find (namespaceName);
  if (offset != 0)
    {
      //
      // This must be a name that has the "/Names" namespace prefix omitted.  
      // Do some reasonableness checking on the rest of the name.
      //
      offset = name.find ("/");
      if (offset == 0)
        {
          NS_ASSERT_MSG (false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
          return false;
        }

      name = "/Names/" + name;
    }
  
  //
  // There must now be a fully qualified path in the string.  All fully 
  // qualified names begin with "/Names".  We have to split off the final 
  // segment which will become the name of the object.  A '/' that
  // separates the path from the final segment had better be there since
  // we just made sure that at least the namespace name was there.
  //
  std::string::size_type i = name.rfind ("/");
  NS_ASSERT_MSG (i != std::string::npos, "NamesPriv::Add(): Internal error.  Can't find '/' in name");

  //
  // The slash we found cannot be the slash at the start of the namespaceName.
  // This would indicate there is no name in the path at all.  It can be
  // any other index.
  //
  NS_ASSERT_MSG (i != 0, "NamesPriv::Add(): Can't find a name in the path string");

  //
  // We now know where the path string starts and ends, and where the
  // name starts and ends.  All we have to do is to call our available
  // function for adding a name under a path string.
  //
  return Add (name.substr (0, i), name.substr (i + 1), object);
}

bool
NamesPriv::Add (std::string path, std::string name, Ptr<Object> object)
{
  if (path == "/Names")
    {
      return Add (Ptr<Object> (0, false), name, object);
    }
  return Add (Find (path), name, object);
}

bool
NamesPriv::Add (Ptr<Object> context, std::string name, Ptr<Object> object)
{
  NS_LOG_FUNCTION (context << name << object);

  if (IsNamed (object))
    {
      NS_LOG_LOGIC ("Object is already named");
      return false;
    }

  NameNode *node = 0;
  if (context)
    {
      node = IsNamed (context);
      NS_ASSERT_MSG (node, "NamesPriv::Name(): context must point to a previously named node");
    }
  else
    {
      node = &m_root;
    }

  if (IsDuplicateName (node, name))
    {
      NS_LOG_LOGIC ("Name is already taken");
      return false;
    }

  NameNode *newNode = new NameNode(node, name, object);
  node->m_nameMap[name] = newNode;
  m_objectMap[object] = newNode;

  return true;
}

bool
NamesPriv::Rename (std::string oldpath, std::string newname)
{
  NS_LOG_FUNCTION (oldpath << newname);
  //
  // This is the simple, easy to use version of Rename, so we want it to be 
  // flexible.   We don't want to force a user to always type the fully 
  // qualified namespace name, so we allow the namespace name to be omitted.
  // For example, calling Rename ("Client/ath0", "eth0") should result in 
  // exactly the same behavior as Rename ("/Names/Client/ath0", "eth0").
  // Calling Rename ("Client", "Router") should have the same effect as 
  // Rename ("Names/Client", "Router")
  //
  // The first thing to do, then, is to "canonicalize" the input string to always
  // be a fully qualified path.
  //
  // If we are given a name that begins with "/Names/" we assume that this is a
  // fully qualified path to the object we want to change.  We split the path into 
  // path string (cf directory) and and a final segment (cf filename) and then call
  // the "Real" Rename.
  //
  std::string namespaceName = "/Names";
  std::string::size_type offset = oldpath.find (namespaceName);
  if (offset != 0)
    {
      //
      // This must be a name that has the "/Names" namespace prefix omitted.  
      // Do some reasonableness checking on the rest of the name.
      //
      offset = oldpath.find ("/");
      if (offset == 0)
        {
          NS_ASSERT_MSG (false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
          return false;
        }

      oldpath = "/Names/" + oldpath;
    }
  
  //
  // There must now be a fully qualified path in the oldpath string.  All 
  // fully qualified names begin with "/Names".  We have to split off the final 
  // segment which will become the name we want to rename.  A '/' that
  // separates the path from the final segment (name) had better be there since
  // we just made sure that at least the namespace name was there.
  //
  std::string::size_type i = oldpath.rfind ("/");
  NS_ASSERT_MSG (i != std::string::npos, "NamesPriv::Add(): Internal error.  Can't find '/' in name");

  //
  // The slash we found cannot be the slash at the start of the namespaceName.
  // This would indicate there is no name in the path at all.  It can be
  // any other index.
  //
  NS_ASSERT_MSG (i != 0, "NamesPriv::Add(): Can't find a name in the path string");

  //
  // We now know where the path part of the string starts and ends, and where the
  // name part starts and ends.  All we have to do is to call our available
  // function for creating adding a name under a path string.
  //
  return Rename (oldpath.substr (0, i), oldpath.substr (i + 1), newname);
}

bool
NamesPriv::Rename (std::string path, std::string oldname, std::string newname)
{
  if (path == "/Names")
    {
      return Rename (Ptr<Object> (0, false), oldname, newname);
    }
  return Rename (Find (path), oldname, newname);
}

bool
NamesPriv::Rename (Ptr<Object> context, std::string oldname, std::string newname)
{
  NS_LOG_FUNCTION (context << oldname << newname);

  NameNode *node = 0;
  if (context)
    {
      node = IsNamed (context);
      NS_ASSERT_MSG (node, "NamesPriv::Name(): context must point to a previously named node");
    }
  else
    {
      node = &m_root;
    }

  if (IsDuplicateName (node, newname))
    {
      NS_LOG_LOGIC ("New name is already taken");
      return false;
    }

  std::map<std::string, NameNode *>::iterator i = node->m_nameMap.find (oldname);
  if (i == node->m_nameMap.end ())
    {
      NS_LOG_LOGIC ("Old name does not exist in name map");
      return false;
    }
  else
    {
      NS_LOG_LOGIC ("Old name exists in name map");

      //
      // The rename process consists of:
      // 1.  Geting the pointer to the name node from the map and remembering it;
      // 2.  Removing the map entry corresponding to oldname from the map;
      // 3.  Changing the name string in the name node;
      // 4.  Adding the name node back in the map under the newname.
      //
      NameNode *changeNode = i->second;
      node->m_nameMap.erase (i);
      changeNode->m_name = newname;
      node->m_nameMap[newname] = changeNode;
      return true;
    }
}

std::string
NamesPriv::FindName (Ptr<Object> object)
{
  NS_LOG_FUNCTION (object);

  std::map<Ptr<Object>, NameNode *>::iterator i = m_objectMap.find (object);
  if (i == m_objectMap.end ())
    {
      NS_LOG_LOGIC ("Object does not exist in object map");
      return "";
    }
  else
    {
      NS_LOG_LOGIC ("Object exists in object map");
      return i->second->m_name;
    }
}

std::string
NamesPriv::FindPath (Ptr<Object> object)
{
  NS_LOG_FUNCTION (object);

  std::map<Ptr<Object>, NameNode *>::iterator i = m_objectMap.find (object);
  if (i == m_objectMap.end ())
    {
      NS_LOG_LOGIC ("Object does not exist in object map");
      return "";
    }

  NameNode *p = i->second;
  NS_ASSERT_MSG (p, "NamesPriv::FindFullName(): Internal error: Invalid NameNode pointer from map");

  std::string path;

  do
    {
      path = "/" + p->m_name + path;
      NS_LOG_LOGIC ("path is " << path);
    }
  while ((p = p->m_parent) != 0);

  return path;
}


Ptr<Object>
NamesPriv::Find (std::string path)
{
  //
  // This is hooked in from simple, easy to use version of Find, so we want it
  // to be flexible.
  //
  // If we are provided a path that doesn't begin with "/Names", we assume 
  // that the caller has simply given us a path starting with a name that
  // is in the root namespace.  This allows peole to omit the "/Names" prefix.
  // and simply do a Find ("Client/eth0") instead of having to always do a
  // Find ("/Names/Client/eth0");
  //
  // So, if we are given a name that begins with "/Names/" the upshot is that we
  // just remove that prefix and treat the rest of the string as starting with a 
  // name in the root namespace.
  //
  std::string namespaceName = "/Names/";
  std::string remaining;

  std::string::size_type offset = path.find (namespaceName);
  if (offset == 0)
    {
      NS_LOG_LOGIC (path << " is a fully qualified name");
      remaining = path.substr (namespaceName.size ());
    }
  else
    {
      NS_LOG_LOGIC (path << " begins with a relative name");
      remaining = path;
    }

  NameNode *node = &m_root;

  //
  // The string <remaining> is now composed entirely of path segments in the
  // /Names name space and we have eaten the leading slash. e.g., 
  // remaining = "ClientNode/eth0"
  //
  // The start of the search is always at the root of the name space.
  //
  for (;;)
    {
      NS_LOG_LOGIC ("Looking for the object of name " << remaining);
      offset = remaining.find ("/");
      if (offset == std::string::npos)
        {
          //
          // There are no remaining slashes so this is the last segment of the 
          // specified name.  We're done when we find it
          //
          std::map<std::string, NameNode *>::iterator i = node->m_nameMap.find (remaining);
          if (i == node->m_nameMap.end ())
            {
              NS_LOG_LOGIC ("Name does not exist in name map");
              return 0;
            }
          else
            {
              NS_LOG_LOGIC ("Name parsed, found object");
              return i->second->m_object;
            }
        }
      else
        {
          //
          // There are more slashes so this is an intermediate segment of the 
          // specified name.  We need to "recurse" when we find this segment.
          //
          offset = remaining.find ("/");
          std::string segment = remaining.substr(0, offset);

          std::map<std::string, NameNode *>::iterator i = node->m_nameMap.find (segment);
          if (i == node->m_nameMap.end ())
            {
              NS_LOG_LOGIC ("Name does not exist in name map");
              return 0;
            }
          else
            {
              node = i->second;
              remaining = remaining.substr (offset + 1);
              NS_LOG_LOGIC ("Intermediate segment parsed");
              continue;
            }
        }
    }

  NS_ASSERT_MSG (node, "NamesPriv::Find(): Internal error:  this can't happen");
  return 0;
}

Ptr<Object>
NamesPriv::Find (std::string path, std::string name)
{
  NS_LOG_FUNCTION (path << name);

  if (path == "/Names")
    {
      return Find (Ptr<Object> (0, false), name);
    }
  return Find (Find (path), name);
}

Ptr<Object>
NamesPriv::Find (Ptr<Object> context, std::string name)
{
  NS_LOG_FUNCTION (context << name);

  NameNode *node = 0;

  if (context == 0)
    {
      NS_LOG_LOGIC ("Zero context implies root NameNode");
      node = &m_root;
    }
  else
    {
      node = IsNamed (context);
      if (node == 0)
        {
          NS_LOG_LOGIC ("Context does not point to a previously named node");
          return 0;
        }
    }

  std::map<std::string, NameNode *>::iterator i = node->m_nameMap.find (name);
  if (i == node->m_nameMap.end ())
    {
      NS_LOG_LOGIC ("Name does not exist in name map");
      return 0;
    }
  else
    {
      NS_LOG_LOGIC ("Name exists in name map");
      return i->second->m_object;
    }
}

NameNode *
NamesPriv::IsNamed (Ptr<Object> object)
{
  NS_LOG_FUNCTION (object);

  std::map<Ptr<Object>, NameNode *>::iterator i = m_objectMap.find (object);
  if (i == m_objectMap.end ())
    {
      NS_LOG_LOGIC ("Object does not exist in object map, returning NameNode 0");
      return 0;
    }
  else
    {
      NS_LOG_LOGIC ("Object exists in object map, returning NameNode " << &i->second);
      return i->second;
    }
}

bool
NamesPriv::IsDuplicateName (NameNode *node, std::string name)
{
  NS_LOG_FUNCTION (node << name);

  std::map<std::string, NameNode *>::iterator i = node->m_nameMap.find (name);
  if (i == node->m_nameMap.end ())
    {
      NS_LOG_LOGIC ("Name does not exist in name map");
      return false;
    }
  else
    {
      NS_LOG_LOGIC ("Name exists in name map");
      return true;
    }
}

void
Names::Delete (void)
{
  NamesPriv::Delete ();
}

void
Names::Add (std::string name, Ptr<Object> object)
{
  bool result = NamesPriv::Get ()->Add (name, object);
  NS_ABORT_MSG_UNLESS (result, "Names::Add(): Error adding name " << name);
}

void
Names::Rename (std::string oldpath, std::string newname)
{
  bool result = NamesPriv::Get ()->Rename (oldpath, newname);
  NS_ABORT_MSG_UNLESS (result, "Names::Rename(): Error renaming " << oldpath << " to " << newname);
}

void
Names::Add (std::string path, std::string name, Ptr<Object> object)
{
  bool result = NamesPriv::Get ()->Add (path, name, object);
  NS_ABORT_MSG_UNLESS (result, "Names::Add(): Error adding " << path << " " << name);
}

void
Names::Rename (std::string path, std::string oldname, std::string newname)
{
  bool result = NamesPriv::Get ()->Rename (path, oldname, newname);
  NS_ABORT_MSG_UNLESS (result, "Names::Rename (): Error renaming " << path << " " << oldname << " to " << newname);
}

void
Names::Add (Ptr<Object> context, std::string name, Ptr<Object> object)
{
  bool result = NamesPriv::Get ()->Add (context, name, object);
  NS_ABORT_MSG_UNLESS (result, "Names::Add(): Error adding name " << name << " under context " << &context);
}

void
Names::Rename (Ptr<Object> context, std::string oldname, std::string newname)
{
  bool result = NamesPriv::Get ()->Rename (context, oldname, newname);
  NS_ABORT_MSG_UNLESS (result, "Names::Rename (): Error renaming " << oldname << " to " << newname << " under context " <<
                       &context);
}

std::string
Names::FindName (Ptr<Object> object)
{
  return NamesPriv::Get ()->FindName (object);
}

std::string
Names::FindPath (Ptr<Object> object)
{
  return NamesPriv::Get ()->FindPath (object);
}

Ptr<Object>
Names::FindInternal (std::string name)
{
  return NamesPriv::Get ()->Find (name);
}

Ptr<Object>
Names::FindInternal (std::string path, std::string name)
{
  return NamesPriv::Get ()->Find (path, name);
}

Ptr<Object>
Names::FindInternal (Ptr<Object> context, std::string name)
{
  return NamesPriv::Get ()->Find (context, name);
}

} //namespace ns3

#ifdef RUN_SELF_TESTS

#include "test.h"
#include "object-factory.h"

namespace ns3 {

class TestObject : public Object
{
public:
  static TypeId GetTypeId (void) 
  {
    static TypeId tid = TypeId ("TestObject")
      .SetParent (Object::GetTypeId ())
      .HideFromDocumentation ()
      .AddConstructor<TestObject> ();
    return tid;
  }
  TestObject () {}
  virtual void Dispose (void) {}
};

class NamesTest : public Test
{
public:
  NamesTest ();
  virtual bool RunTests (void);
};

NamesTest::NamesTest ()
  : Test ("Names")
{
}

bool 
NamesTest::RunTests (void)
{
  bool result = true;

  //
  // Names::Add and Names::Rename return void to align with the Config API.
  // The private versions of these functions do return error codes so we
  // can test to make sure errors are detected.  Names::Add and 
  // Names::Rename check for these error codes and abort if an error was
  // detected.  So when we expect that an error should be detected, we 
  // have to call the private routine to avoid a fatal error popping.
  // 
  // Name a couple of objects at the root level
  //
  Ptr<TestObject> client = CreateObject<TestObject> ();
  Names::Add ("Client", client);

  Ptr<TestObject> server = CreateObject<TestObject> ();
  Names::Add ("Server", server);

  //
  // We shouldn't be able to add another name to a previously named object
  //
  result = NamesPriv::Get ()->Add ("Not Client", client);
  NS_TEST_ASSERT_EQUAL (result, false);

  //
  // We shouldn't be able to duplicate a name at the root level.
  //
  Ptr<TestObject> secondClient = CreateObject<TestObject> ();
  result = NamesPriv::Get ()->Add ("Client", secondClient);
  NS_TEST_ASSERT_EQUAL (result, false);

  //
  // We should be able to add a new name in the first object's context
  //
  Ptr<TestObject> clientEth0 = CreateObject<TestObject> ();
  Names::Add (client, "eth0", clientEth0);

  //
  // We shouldn't be able to duplicate a name in that context.
  //
  Ptr<TestObject> secondClientEth0 = CreateObject<TestObject> ();
  result = NamesPriv::Get ()->Add (client, "eth0", secondClientEth0);
  NS_TEST_ASSERT_EQUAL (result, false);

  //
  // We should be able to add the same name in the second object's context
  //
  Ptr<TestObject> serverEth0 = CreateObject<TestObject> ();
  Names::Add (server, "eth0", serverEth0);

  //
  // We should be able to find the short names for the objects we created
  //
  std::string found;

  found = Names::FindName (client);
  NS_TEST_ASSERT_EQUAL (found, "Client");

  found = Names::FindName (server);
  NS_TEST_ASSERT_EQUAL (found, "Server");

  found = Names::FindName (clientEth0);
  NS_TEST_ASSERT_EQUAL (found, "eth0");

  found = Names::FindName (serverEth0);
  NS_TEST_ASSERT_EQUAL (found, "eth0");

  //
  // We should be able to find the full names for the objects we created
  //
  found = Names::FindPath (client);
  NS_TEST_ASSERT_EQUAL (found, "/Names/Client");

  found = Names::FindPath (server);
  NS_TEST_ASSERT_EQUAL (found, "/Names/Server");

  found = Names::FindPath (clientEth0);
  NS_TEST_ASSERT_EQUAL (found, "/Names/Client/eth0");

  found = Names::FindPath (serverEth0);
  NS_TEST_ASSERT_EQUAL (found, "/Names/Server/eth0");

  // 
  // We should be able to find the objects from a context and name combination.
  // Note that the Ptr<Object> (0, false) below is to differentiate a null object
  // pointer from a null string pointer -- not normally needed in real use-cases.
  // 
  //
  Ptr<TestObject> foundObject;

  foundObject = Names::Find<TestObject> (Ptr<Object> (0, false), "Client");
  NS_TEST_ASSERT_EQUAL (foundObject, client);

  foundObject = Names::Find<TestObject> (Ptr<Object> (0, false), "Server");
  NS_TEST_ASSERT_EQUAL (foundObject, server);

  foundObject = Names::Find<TestObject> (client, "eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, clientEth0);

  foundObject = Names::Find<TestObject> (server, "eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, serverEth0);

  //
  // We should be able to do the same thing by providing path strings instead
  // of context objects.
  //

  foundObject = Names::Find<TestObject> ("/Names", "Client");
  NS_TEST_ASSERT_EQUAL (foundObject, client);

  foundObject = Names::Find<TestObject> ("/Names", "Server");
  NS_TEST_ASSERT_EQUAL (foundObject, server);

  foundObject = Names::Find<TestObject> ("/Names/Client", "eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, clientEth0);

  foundObject = Names::Find<TestObject> ("/Names/Server", "eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, serverEth0);

  // 
  // We should be able to find the objects from their full path names
  //
  foundObject = Names::Find<TestObject> ("/Names/Client");
  NS_TEST_ASSERT_EQUAL (foundObject, client);

  foundObject = Names::Find<TestObject> ("/Names/Server");
  NS_TEST_ASSERT_EQUAL (foundObject, server);

  foundObject = Names::Find<TestObject> ("/Names/Client/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, clientEth0);

  foundObject = Names::Find<TestObject> ("/Names/Server/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, serverEth0);

  // 
  // We should be able to omit the root of the namespace from the full path names
  //
  foundObject = Names::Find<TestObject> ("Client");
  NS_TEST_ASSERT_EQUAL (foundObject, client);

  foundObject = Names::Find<TestObject> ("Server");
  NS_TEST_ASSERT_EQUAL (foundObject, server);

  foundObject = Names::Find<TestObject> ("Client/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, clientEth0);

  foundObject = Names::Find<TestObject> ("Server/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, serverEth0);

  //
  // We should be able to add objects while including the root of the namespace
  // in the name.
  //
  Ptr<TestObject> router1 = CreateObject<TestObject> ();
  Names::Add ("/Names/Router1", router1);

  //
  // We should be able to add objects while not including the root of the namespace
  // in the name.
  //
  Ptr<TestObject> router2 = CreateObject<TestObject> ();
  Names::Add ("Router2", router2);

  //
  // We should be able to add sub-objects while including the root of the namespace
  // in the name.
  //
  Ptr<TestObject> router1Eth0 = CreateObject<TestObject> ();
  Names::Add ("/Names/Router1/eth0", router1Eth0);

  //
  // We should be able to add sub-objects while not including the root of the namespace
  // in the name.
  //
  Ptr<TestObject> router2Eth0 = CreateObject<TestObject> ();
  Names::Add ("Router2/eth0", router2Eth0);

  //
  // We should be able to find these objects in the same two ways
  //
  foundObject = Names::Find<TestObject> ("/Names/Router1");
  NS_TEST_ASSERT_EQUAL (foundObject, router1);

  foundObject = Names::Find<TestObject> ("Router1");
  NS_TEST_ASSERT_EQUAL (foundObject, router1);

  foundObject = Names::Find<TestObject> ("/Names/Router2");
  NS_TEST_ASSERT_EQUAL (foundObject, router2);

  foundObject = Names::Find<TestObject> ("Router2");
  NS_TEST_ASSERT_EQUAL (foundObject, router2);

  foundObject = Names::Find<TestObject> ("/Names/Router1/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, router1Eth0);

  foundObject = Names::Find<TestObject> ("Router1/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, router1Eth0);

  foundObject = Names::Find<TestObject> ("/Names/Router2/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, router2Eth0);

  foundObject = Names::Find<TestObject> ("Router2/eth0");
  NS_TEST_ASSERT_EQUAL (foundObject, router2Eth0);

  //
  // We have a pile of names defined.  We should be able to rename them in the
  // usual ways.
  //
  Names::Rename ("/Names/Router1", "RouterX");

  foundObject = Names::Find<TestObject> ("/Names/RouterX");
  NS_TEST_ASSERT_EQUAL (foundObject, router1);

  Names::Rename ("Router2", "RouterY");

  foundObject = Names::Find<TestObject> ("RouterY");
  NS_TEST_ASSERT_EQUAL (foundObject, router2);

  Names::Rename ("/Names/RouterX/eth0", "ath0");

  foundObject = Names::Find<TestObject> ("/Names/RouterX/ath0");
  NS_TEST_ASSERT_EQUAL (foundObject, router1Eth0);

  foundObject = Names::Find<TestObject> ("RouterX/ath0");
  NS_TEST_ASSERT_EQUAL (foundObject, router1Eth0);

  //
  // We should not be able to rename an object into conflict with another
  // object.
  //

  result = NamesPriv::Get ()->Rename ("/Names/RouterX", "RouterY");
  NS_TEST_ASSERT_EQUAL (result, false);

  Names::Delete ();
  return true;
}

static NamesTest g_namesTests;

} // namespace ns3

#endif /* RUN_SELF_TESTS */