--- 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.