add a Rename function to the object name service to facilitate auto naming
authorCraig Dowell <craigdo@ee.washington.edu>
Fri, 30 Jan 2009 17:34:41 -0800
changeset 4153 93bb91eae5cd
parent 4152 99e350f3f7b4
child 4154 10e3985a1a0b
add a Rename function to the object name service to facilitate auto naming
examples/names.cc
src/core/names.cc
src/core/names.h
--- a/examples/names.cc	Fri Jan 30 10:34:05 2009 -0800
+++ b/examples/names.cc	Fri Jan 30 17:34:41 2009 -0800
@@ -52,19 +52,30 @@
   //
   // We're going to use the zeroth node in the container as the client, and
   // the first node as the server.  Add some "human readable" names for these
-  // nodes.  The names below will go into the name system as "/Names/client"
+  // nodes.  The names below will go into the name system as "/Names/clientZero"
   // and "/Names/server", but note that the Add function assumes that if you
   // omit the leading "/Names/" the remaining string is assumed to be rooted
   // in the "/Names" namespace. The following calls,
   //
-  //  Names::Add ("client", n.Get (0));
-  //  Names::Add ("/Names/client", n.Get (0));
+  //  Names::Add ("clientZero", n.Get (0));
+  //  Names::Add ("/Names/clientZero", n.Get (0));
   //
   // will produce identical results.
   //
-  Names::Add ("client", n.Get (0));
+  Names::Add ("clientZero", n.Get (0));
   Names::Add ("/Names/server", n.Get (1));
 
+  //
+  // It is possible to rename a node that has been previously named.  This is
+  // useful in automatic name generation.  You can automatically generate node
+  // names such as, "node-0", "node-1", etc., and then go back and change
+  // the name of some distinguished node to another value --  "access-point" 
+  // for example.  We illustrate this by just changing the client's name.
+  // As is typical of the object name service, you can either provide or elide
+  // the "/Names" prefix as you choose.
+  //
+  Names::Rename ("clientZero", "client");
+
   InternetStackHelper internet;
   internet.Install (n);
 
--- 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.
--- a/src/core/names.h	Fri Jan 30 10:34:05 2009 -0800
+++ b/src/core/names.h	Fri Jan 30 17:34:41 2009 -0800
@@ -33,11 +33,21 @@
 public:
 
   /**
-   * Add the association between the string "name" and the Ptr<Object> obj
-   * at the root of the "/Names" name space.  This can be seen as equivalent
-   * to adding a Pointer Attribute called "name" to the to the root name 
-   * space object and then assigning the value obj to that attribute.  The 
-   * config facility will see it that way.
+   * \brief Add the association between the string "name" and the Ptr<Object> obj.
+   *
+   * The name may begin either with "/Names" to explicitly call out the fact 
+   * that the name provided is installed under the root of the name space, 
+   * or it may begin with the short name of the first obejct in the path.
+   * For example, Add ("/Names/client", obj) and Add ("client", obj) accomplish
+   * exactly the same thing.  Names at a given level in the name space must be
+   * unique.  It would be illegal to try and associate a different object with
+   * the same name: "client."
+   *
+   * As well as providing a single shortname, the name parameter can contain a 
+   * path that qualifies the shortname.  For example, you could make the call
+   * Add ("/Names/client/eth0", obj) if you had previously associated a name 
+   * "client" in the root of the name space.  Note that Add ("client/eth0", obj)
+   * would accomplish exactly the same thing.
    *
    * \param name The name of the object you want to associate.
    * \param obj A smart pointer to the object itself.
@@ -45,6 +55,62 @@
   static bool Add (std::string name, Ptr<Object> obj);
 
   /**
+   * \brief Rename a previously associated name.
+   *
+   * The name may begin either with "/Names" to explicitly call out the fact 
+   * that the name provided is installed under the root of the name space, 
+   * or it may begin with the short name of the first obejct in the path.
+   * For example, Rename ("/Names/client", "newname") and 
+   * Rename ("client", "newname") will accomplish exactly the same thing --
+   * to find an object at the root of the name space called "client" and change
+   * its name to "newname".  Names at a given level in the name space must be
+   * unique.  It would be illegal to try and rename the association to another
+   * name that already exists at the namespace level indicated by the path.
+   *
+   * As well as providing a single shortname, the name parameter can contain a 
+   * path that qualifies the shortname you want to change.  For example, you 
+   * could make the call Rename ("/Names/client/eth0", "ath0") if "ath0" has
+   * not been previously used at this level (under "client").  Note that 
+   * Rename ("client/eth0", "ath0") would accomplish exactly the same thing.
+   *
+   * \param oldname The current name of the object you want to change.
+   * \param newname The new name of the object you want to change.
+   *
+   * \returns true if the name change was successfully completed, false otherwise
+   */
+  static bool Rename (std::string oldname, std::string newname);
+
+  /**
+   * \brief An intermediate form of Add allowing you to provide a context in the 
+   * form of a name string.
+   *
+   * \see Add (Ptr<Object> context, std::string name, Ptr<Object> object);
+   *
+   * \param context A fully qualified name describing a previously named object.
+   *                under which you want this name to be defined.
+   * \param name The name of the object you want to associate.
+   * \param obj A smart pointer to the object itself.
+   *
+   * \returns true if the association was successfully completed, false otherwise
+   */
+  static bool Add (std::string context, std::string name, Ptr<Object> object);
+
+  /**
+   * \brief An intermediate form of Rename allowing you to provide a context in
+   * the form of a name string.
+   *
+   * \see Rename (Ptr<Object> context, std::string oldname, std::string newname);
+   *
+   * \param context A fully qualified name describing a previously named object.
+   *                under which you want this name to be changed.
+   * \param oldname The current shortname of the object you want to change.
+   * \param newname The new shortname of the object you want to change.
+   *
+   * \returns true if the name change was successfully completed, false otherwise
+   */
+  static bool Rename (std::string context, std::string oldname, std::string newname);
+
+  /**
    * Add the association between the string "name" and the Ptr<Object> obj
    * in the object context given by the Ptr<Object> context.  This can be
    * seen as equivalent to adding a Pointer Attribute called "name" to the 
@@ -61,20 +127,25 @@
   static bool Add (Ptr<Object> context, std::string name, Ptr<Object> object);
 
   /**
-   * Syntactic sugar around the Object context Name method.  Allows you to 
-   * specify the context with a string instead of the pointer.  If the first
-   * parameter (context) is "/Names" this turns into a call into Name at the
-   * root of the name space.  Otherwise it does a FindObjectFromFullNameInternal
-   * on the context and adds the name to a subspace.
+   * \brief Rename a previously associated shortname.
    *
-   * \param context A fully qualified name describing a previously named object.
-   *                under which you want this name to be defined.
-   * \param name The name of the object you want to associate.
-   * \param obj A smart pointer to the object itself.
+   * The context for the name -- the namespace level must be provided by passing
+   * a context object.  The oldname is then interpreted as a shortname which is
+   * defined "under" this context.  For example, if you have previously done an
+   * Add ("/Names/client", obj), you have associated the object obj with the
+   * name "client."  If you create an "eth0" under "client", then the obj that
+   * you associated with "client" becomes the context for "eth0" object.  If 
+   * you want to rename "eth0", you would provide that obj as the context for
+   * the Rename.  This would look like Rename (obj, "eth0", "ath0").
    *
-   * \returns true if the association was successfully completed, false otherwise
+   * \param context A spart pointer to an object under which to look for the
+   *                oldname.
+   * \param oldname The current shortname of the object you want to change.
+   * \param newname The new shortname of the object you want to change.
+   *
+   * \returns true if the name change was successfully completed, false otherwise
    */
-  static bool Add (std::string context, std::string name, Ptr<Object> object);
+  static bool Rename (Ptr<Object> context, std::string oldname, std::string newname);
 
   /**
    * Given a pointer to an object, look to see if that object has a name