src/core/names.cc
changeset 4153 93bb91eae5cd
parent 4151 682a4cae7a5f
child 4158 2b6dba4062e1
--- a/src/core/names.cc	Fri Jan 30 10:34:05 2009 -0800
+++ b/src/core/names.cc	Fri Jan 30 17:34:41 2009 -0800
@@ -84,8 +84,11 @@
   ~NamesPriv ();
 
   bool Add (std::string name, Ptr<Object> obj);
+  bool Rename (std::string oldname, std::string newname);
   bool Add (std::string context, std::string name, Ptr<Object> object);
+  bool Rename (std::string context, std::string oldname, std::string newname);
   bool Add (Ptr<Object> context, std::string name, Ptr<Object> object);
+  bool Rename (Ptr<Object> context, std::string oldname, std::string newname);
   std::string FindShortName (Ptr<Object> object);
   std::string FindFullName (Ptr<Object> object);
   Ptr<Object> FindObjectFromFullName (std::string name);
@@ -224,6 +227,69 @@
 }
 
 bool
+NamesPriv::Rename (std::string oldname, std::string newname)
+{
+  NS_LOG_FUNCTION (oldname << 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 name.
+  //
+  // If we are given a name that begins with "/Names/" we assume that this is a
+  // fullname to the object we want to change.  We split the fullname into a 
+  // context string and and a final segment and then call the "Real" Rename.
+  //
+  std::string namespaceName = "/Names";
+  std::string::size_type offset = oldname.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 = oldname.find ("/");
+      if (offset == 0)
+        {
+          NS_ASSERT_MSG (false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
+          return false;
+        }
+
+      oldname = "/Names/" + oldname;
+    }
+  
+  //
+  // There must now be a fully qualified longname in the oldname string.  All 
+  // fully qualified names begin with "/Names".  We have to split off the final 
+  // segment which will become the shortname we want to rename.  A '/' that
+  // separates the context 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 = oldname.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 shortname in the path at all.  It can be
+  // any other index.
+  //
+  NS_ASSERT_MSG (i != 0, "NamesPriv::Add(): Can't find a shortname in the name string");
+
+  //
+  // We now know where the context string starts and ends, and where the
+  // shortname starts and ends.  All we have to do is to call our available
+  // function for creating addubg a shortname under a context string.
+  //
+  return Rename (oldname.substr (0, i), oldname.substr (i + 1), newname);
+}
+
+bool
 NamesPriv::Add (std::string context, std::string name, Ptr<Object> object)
 {
   if (context == "/Names")
@@ -234,6 +300,16 @@
 }
 
 bool
+NamesPriv::Rename (std::string context, std::string oldname, std::string newname)
+{
+  if (context == "/Names")
+    {
+      return Rename (Ptr<Object> (0, false), oldname, newname);
+    }
+  return Rename (FindObjectFromFullName (context), oldname, newname);
+}
+
+bool
 NamesPriv::Add (Ptr<Object> context, std::string name, Ptr<Object> object)
 {
   NS_LOG_FUNCTION (context << name << object);
@@ -268,6 +344,53 @@
   return true;
 }
 
+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::FindShortName (Ptr<Object> object)
 {
@@ -490,17 +613,35 @@
 }
 
 bool
+Names::Rename (std::string oldname, std::string newname)
+{
+  return NamesPriv::Get ()->Rename (oldname, newname);
+}
+
+bool
 Names::Add (Ptr<Object> context, std::string name, Ptr<Object> object)
 {
   return NamesPriv::Get ()->Add (context, name, object);
 }
 
 bool
+Names::Rename (Ptr<Object> context, std::string oldname, std::string newname)
+{
+  return NamesPriv::Get ()->Rename (context, oldname, newname);
+}
+
+bool
 Names::Add (std::string context, std::string name, Ptr<Object> object)
 {
   return NamesPriv::Get ()->Add (context, name, object);
 }
 
+bool
+Names::Rename (std::string context, std::string oldname, std::string newname)
+{
+  return NamesPriv::Get ()->Rename (context, oldname, newname);
+}
+
 std::string
 Names::FindShortName (Ptr<Object> object)
 {
@@ -782,6 +923,39 @@
   NS_TEST_ASSERT_EQUAL (foundObject, wirelessAth0);
 
   //
+  // We have a pile of names defined.  We should be able to rename them in the
+  // usual ways.
+  //
+  result = Names::Rename ("/Names/Router1", "RouterX");
+  NS_TEST_ASSERT_EQUAL (result, true);
+
+  foundObject = Names::Find<TestObject> ("/Names/RouterX");
+  NS_TEST_ASSERT_EQUAL (foundObject, router1);
+
+  result = Names::Rename ("Router2", "RouterY");
+  NS_TEST_ASSERT_EQUAL (result, true);
+
+  foundObject = Names::Find<TestObject> ("RouterY");
+  NS_TEST_ASSERT_EQUAL (foundObject, router2);
+
+  result = Names::Rename ("/Names/RouterX/eth0", "ath0");
+  NS_TEST_ASSERT_EQUAL (result, true);
+
+  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 = Names::Rename ("/Names/RouterX", "RouterY");
+  NS_TEST_ASSERT_EQUAL (result, false);
+
+  //
   // Run the simulator and destroy it to get the Destroy method called on the
   // private implementation object.  We depend on seeing a valgrind-clean run of
   // the unit tests to really determine if the clean up was really successful.