diff -r 99e350f3f7b4 -r 93bb91eae5cd src/core/names.cc --- 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 obj); + bool Rename (std::string oldname, std::string newname); bool Add (std::string context, std::string name, Ptr object); + bool Rename (std::string context, std::string oldname, std::string newname); bool Add (Ptr context, std::string name, Ptr object); + bool Rename (Ptr context, std::string oldname, std::string newname); std::string FindShortName (Ptr object); std::string FindFullName (Ptr object); Ptr 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) { 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 (0, false), oldname, newname); + } + return Rename (FindObjectFromFullName (context), oldname, newname); +} + +bool NamesPriv::Add (Ptr context, std::string name, Ptr object) { NS_LOG_FUNCTION (context << name << object); @@ -268,6 +344,53 @@ return true; } +bool +NamesPriv::Rename (Ptr 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::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) { @@ -490,17 +613,35 @@ } bool +Names::Rename (std::string oldname, std::string newname) +{ + return NamesPriv::Get ()->Rename (oldname, newname); +} + +bool Names::Add (Ptr context, std::string name, Ptr object) { return NamesPriv::Get ()->Add (context, name, object); } bool +Names::Rename (Ptr 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) { 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) { @@ -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 ("/Names/RouterX"); + NS_TEST_ASSERT_EQUAL (foundObject, router1); + + result = Names::Rename ("Router2", "RouterY"); + NS_TEST_ASSERT_EQUAL (result, true); + + foundObject = Names::Find ("RouterY"); + NS_TEST_ASSERT_EQUAL (foundObject, router2); + + result = Names::Rename ("/Names/RouterX/eth0", "ath0"); + NS_TEST_ASSERT_EQUAL (result, true); + + foundObject = Names::Find ("/Names/RouterX/ath0"); + NS_TEST_ASSERT_EQUAL (foundObject, router1Eth0); + + foundObject = Names::Find ("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.