src/core/config.cc
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 04 Jul 2009 08:15:48 +0200
changeset 4654 2eaebe77d66b
parent 4147 5d8530130930
permissions -rw-r--r--
Added tag ns-3.5 for changeset c975274c9707
     1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     2 /*
     3  * Copyright (c) 2008 INRIA
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License version 2 as
     7  * published by the Free Software Foundation;
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program; if not, write to the Free Software
    16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    17  *
    18  * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
    19  */
    20 #include "config.h"
    21 #include "singleton.h"
    22 #include "object.h"
    23 #include "global-value.h"
    24 #include "object-vector.h"
    25 #include "names.h"
    26 #include "pointer.h"
    27 #include "log.h"
    28 #include <sstream>
    29 
    30 NS_LOG_COMPONENT_DEFINE ("Config");
    31 
    32 namespace ns3 {
    33 
    34 namespace Config {
    35 
    36 MatchContainer::MatchContainer ()
    37 {}
    38 MatchContainer::MatchContainer (const std::vector<Ptr<Object> > &objects, 
    39                                 const std::vector<std::string> &contexts,
    40                                 std::string path)
    41   : m_objects (objects),
    42     m_contexts (contexts),
    43     m_path (path)
    44 {}
    45 MatchContainer::Iterator 
    46 MatchContainer::Begin (void) const
    47 {
    48   return m_objects.begin ();
    49 }
    50 MatchContainer::Iterator 
    51 MatchContainer::End (void) const
    52 {
    53   return m_objects.end ();
    54 }
    55 uint32_t 
    56 MatchContainer::GetN (void) const
    57 {
    58   return m_objects.size ();
    59 }
    60 Ptr<Object> 
    61 MatchContainer::Get (uint32_t i) const
    62 {
    63   return m_objects[i];
    64 }
    65 std::string 
    66 MatchContainer::GetMatchedPath (uint32_t i) const
    67 {
    68   return m_contexts[i];
    69 }
    70 std::string 
    71 MatchContainer::GetPath (void) const
    72 {
    73   return m_path;
    74 }
    75 
    76 void 
    77 MatchContainer::Set (std::string name, const AttributeValue &value)
    78 {
    79   for (Iterator tmp = Begin (); tmp != End (); ++tmp)
    80     {
    81       Ptr<Object> object = *tmp;
    82       object->SetAttribute (name, value);
    83     }
    84 }
    85 void 
    86 MatchContainer::Connect (std::string name, const CallbackBase &cb)
    87 {
    88   NS_ASSERT (m_objects.size () == m_contexts.size ());
    89   for (uint32_t i = 0; i < m_objects.size (); ++i)
    90     {
    91       Ptr<Object> object = m_objects[i];
    92       std::string ctx = m_contexts[i] + name;
    93       object->TraceConnect (name, ctx, cb);
    94     }
    95 }
    96 void 
    97 MatchContainer::ConnectWithoutContext (std::string name, const CallbackBase &cb)
    98 {
    99   for (Iterator tmp = Begin (); tmp != End (); ++tmp)
   100     {
   101       Ptr<Object> object = *tmp;
   102       object->TraceConnectWithoutContext (name, cb);
   103     }
   104 }
   105 void 
   106 MatchContainer::Disconnect (std::string name, const CallbackBase &cb)
   107 {
   108   NS_ASSERT (m_objects.size () == m_contexts.size ());
   109   for (uint32_t i = 0; i < m_objects.size (); ++i)
   110     {
   111       Ptr<Object> object = m_objects[i];
   112       std::string ctx = m_contexts[i] + name;
   113       object->TraceDisconnect (name, ctx, cb);
   114     }
   115 }
   116 void 
   117 MatchContainer::DisconnectWithoutContext (std::string name, const CallbackBase &cb)
   118 {
   119   for (Iterator tmp = Begin (); tmp != End (); ++tmp)
   120     {
   121       Ptr<Object> object = *tmp;
   122       object->TraceDisconnectWithoutContext (name, cb);
   123     }
   124 }
   125 
   126 } // namespace Config
   127 
   128 class ArrayMatcher
   129 {
   130 public:
   131   ArrayMatcher (std::string element);
   132   bool Matches (uint32_t i) const;
   133 private:
   134   bool StringToUint32 (std::string str, uint32_t *value) const;
   135   std::string m_element;
   136 };
   137 
   138 
   139 ArrayMatcher::ArrayMatcher (std::string element)
   140   : m_element (element)
   141 {}
   142 bool 
   143 ArrayMatcher::Matches (uint32_t i) const
   144 {
   145   if (m_element == "*")
   146     {
   147       NS_LOG_DEBUG ("Array "<<i<<" matches *");
   148       return true;
   149     }
   150   std::string::size_type tmp;
   151   tmp = m_element.find ("|");
   152   if (tmp != std::string::npos)
   153     {
   154       std::string left = m_element.substr (0, tmp-0);
   155       std::string right = m_element.substr (tmp+1, m_element.size () - (tmp + 1));
   156       ArrayMatcher matcher = ArrayMatcher (left);
   157       if (matcher.Matches (i))
   158 	{
   159 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<left);
   160 	  return true;
   161 	}
   162       matcher = ArrayMatcher (right);
   163       if (matcher.Matches (i))
   164 	{
   165 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<right);
   166 	  return true;
   167 	}
   168       NS_LOG_DEBUG ("Array "<<i<<" does not match "<<m_element);
   169       return false;
   170     }
   171   std::string::size_type leftBracket = m_element.find ("[");
   172   std::string::size_type rightBracket = m_element.find ("]");
   173   std::string::size_type dash = m_element.find ("-");
   174   if (leftBracket == 0 && rightBracket == m_element.size () - 1 &&
   175       dash > leftBracket && dash < rightBracket)
   176     {
   177       std::string lowerBound = m_element.substr (leftBracket + 1, dash - (leftBracket + 1));
   178       std::string upperBound = m_element.substr (dash + 1, rightBracket - (dash + 1));
   179       uint32_t min;
   180       uint32_t max;
   181       if (StringToUint32 (lowerBound, &min) && 
   182 	  StringToUint32 (upperBound, &max) &&
   183 	  i >= min && i <= max)
   184         {
   185 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<m_element);
   186           return true;
   187         }
   188       else
   189 	{
   190 	  NS_LOG_DEBUG ("Array "<<i<<" does not "<<m_element);
   191 	  return false;
   192 	}
   193     }
   194   uint32_t value;
   195   if (StringToUint32 (m_element, &value) &&
   196       i == value)
   197     {
   198       NS_LOG_DEBUG ("Array "<<i<<" matches "<<m_element);
   199       return true;
   200     }
   201   NS_LOG_DEBUG ("Array "<<i<<" does not match "<<m_element);
   202   return false;
   203 }
   204 
   205 bool
   206 ArrayMatcher::StringToUint32 (std::string str, uint32_t *value) const
   207 {
   208   std::istringstream iss;
   209   iss.str (str);
   210   iss >> (*value);
   211   return !iss.bad () && !iss.fail ();
   212 }
   213 
   214 
   215 class Resolver
   216 {
   217 public:
   218   Resolver (std::string path);
   219   virtual ~Resolver ();
   220 
   221   void Resolve (Ptr<Object> root);
   222 private:
   223   void Canonicalize (void);
   224   void DoResolve (std::string path, Ptr<Object> root);
   225   void DoArrayResolve (std::string path, const ObjectVectorValue &vector);
   226   void DoResolveOne (Ptr<Object> object);
   227   std::string GetResolvedPath (void) const;
   228   virtual void DoOne (Ptr<Object> object, std::string path) = 0;
   229   std::vector<std::string> m_workStack;
   230   std::string m_path;
   231 };
   232 
   233 Resolver::Resolver (std::string path)
   234   : m_path (path)
   235 {
   236   Canonicalize ();
   237 }
   238 Resolver::~Resolver ()
   239 {}
   240 void
   241 Resolver::Canonicalize (void)
   242 {
   243   // ensure that we start and end with a '/'
   244   std::string::size_type tmp = m_path.find ("/");
   245   if (tmp != 0)
   246     {
   247       // no slash at start
   248       m_path = "/" + m_path;
   249     }
   250   tmp = m_path.find_last_of ("/");
   251   if (tmp != (m_path.size () - 1))
   252     {
   253       // no slash at end
   254       m_path = m_path + "/";
   255     }
   256 }
   257 
   258 void 
   259 Resolver::Resolve (Ptr<Object> root)
   260 {
   261   DoResolve (m_path, root);
   262 }
   263 
   264 std::string
   265 Resolver::GetResolvedPath (void) const
   266 {
   267   std::string fullPath = "/";
   268   for (std::vector<std::string>::const_iterator i = m_workStack.begin (); i != m_workStack.end (); i++)
   269     {
   270       fullPath += *i + "/";
   271     }
   272   return fullPath;
   273 }
   274 
   275 void 
   276 Resolver::DoResolveOne (Ptr<Object> object)
   277 {
   278   NS_LOG_DEBUG ("resolved="<<GetResolvedPath ());
   279   DoOne (object, GetResolvedPath ());
   280 }
   281 
   282 void
   283 Resolver::DoResolve (std::string path, Ptr<Object> root)
   284 {
   285   NS_LOG_FUNCTION (path << root);
   286   std::string::size_type tmp;
   287   tmp = path.find ("/");
   288   NS_ASSERT (tmp == 0);
   289   std::string::size_type next = path.find ("/", 1);
   290 
   291   if (next == std::string::npos)
   292     {
   293       //
   294       // If root is zero, we're beginning to see if we can use the object name 
   295       // service to resolve this path.  It is impossible to have a object name 
   296       // associated with the root of the object name service since that root
   297       // is not an object.  This path must be referring to something in another
   298       // namespace and it will have been found already since the name service
   299       // is always consulted last.
   300       // 
   301       if (root)
   302         {
   303           DoResolveOne (root);
   304         }
   305       return;
   306     }
   307   std::string item = path.substr (1, next-1);
   308   std::string pathLeft = path.substr (next, path.size ()-next);
   309 
   310   //
   311   // If root is zero, we're beginning to see if we can use the object name 
   312   // service to resolve this path.  In this case, we must see the name space 
   313   // "/Names" on the front of this path.  There is no object associated with 
   314   // the root of the "/Names" namespace, so we just ignore it and move on to 
   315   // the next segment.
   316   //
   317   if (root == 0)
   318     {
   319       std::string::size_type offset = path.find ("/Names");
   320       if (offset == 0)
   321         {
   322           m_workStack.push_back (item);
   323           DoResolve (pathLeft, root);
   324           m_workStack.pop_back ();
   325           return;
   326         }
   327     }
   328 
   329   //
   330   // We have an item (possibly a segment of a namespace path.  Check to see if
   331   // we can determine that this segment refers to a named object.  If root is
   332   // zero, this means to look in the root of the "/Names" name space, otherwise
   333   // it refers to a name space context (level).
   334   //
   335   Ptr<Object> namedObject = Names::Find<Object> (root, item);
   336   if (namedObject)
   337     {
   338       NS_LOG_DEBUG ("Name system resolved item = " << item << " to " << namedObject);
   339       m_workStack.push_back (item);
   340       DoResolve (pathLeft, namedObject);
   341       m_workStack.pop_back ();
   342       return;
   343     }
   344 
   345   //
   346   // We're done with the object name service hooks, so proceed down the path
   347   // of types and attributes; but only if root is nonzero.  If root is zero
   348   // and we find ourselves here, we are trying to check in the namespace for
   349   // a path that is not in the "/Names" namespace.  We will have previously
   350   // found any matches, so we just bail out.
   351   //
   352   if (root == 0)
   353     {
   354       return;
   355     }
   356   std::string::size_type dollarPos = item.find ("$");
   357   if (dollarPos == 0)
   358     {
   359       // This is a call to GetObject
   360       std::string tidString = item.substr (1, item.size () - 1);
   361       NS_LOG_DEBUG ("GetObject="<<tidString<<" on path="<<GetResolvedPath ());
   362       TypeId tid = TypeId::LookupByName (tidString);
   363       Ptr<Object> object = root->GetObject<Object> (tid);
   364       if (object == 0)
   365 	{
   366 	  NS_LOG_DEBUG ("GetObject ("<<tidString<<") failed on path="<<GetResolvedPath ());
   367 	  return;
   368 	}
   369       m_workStack.push_back (item);
   370       DoResolve (pathLeft, object);
   371       m_workStack.pop_back ();
   372     }
   373   else 
   374     {
   375       // this is a normal attribute.
   376       TypeId tid = root->GetInstanceTypeId ();
   377       struct TypeId::AttributeInfo info;
   378       if (!tid.LookupAttributeByName (item, &info))
   379 	{
   380 	  NS_LOG_DEBUG ("Requested item="<<item<<" does not exist on path="<<GetResolvedPath ());
   381 	  return;
   382 	}
   383       // attempt to cast to a pointer checker.
   384       const PointerChecker *ptr = dynamic_cast<const PointerChecker *> (PeekPointer (info.checker));
   385       if (ptr != 0)
   386 	{
   387 	  NS_LOG_DEBUG ("GetAttribute(ptr)="<<item<<" on path="<<GetResolvedPath ());
   388           PointerValue ptr;
   389           root->GetAttribute (item, ptr);
   390 	  Ptr<Object> object = ptr.Get<Object> ();
   391 	  if (object == 0)
   392 	    {
   393 	      NS_LOG_ERROR ("Requested object name=\""<<item<<
   394 			    "\" exists on path=\""<<GetResolvedPath ()<<"\""
   395 			    " but is null.");
   396 	      return;
   397 	    }
   398 	  m_workStack.push_back (item);
   399 	  DoResolve (pathLeft, object);
   400 	  m_workStack.pop_back ();
   401 	}
   402       // attempt to cast to an object vector.
   403       const ObjectVectorChecker *vectorChecker = dynamic_cast<const ObjectVectorChecker *> (PeekPointer (info.checker));
   404       if (vectorChecker != 0)
   405 	{
   406 	  NS_LOG_DEBUG ("GetAttribute(vector)="<<item<<" on path="<<GetResolvedPath ());
   407 	  ObjectVectorValue vector;
   408           root->GetAttribute (item, vector);
   409 	  m_workStack.push_back (item);
   410 	  DoArrayResolve (pathLeft, vector);
   411 	  m_workStack.pop_back ();
   412 	}
   413       // this could be anything else and we don't know what to do with it.
   414       // So, we just ignore it.
   415     }
   416 }
   417 
   418 void 
   419 Resolver::DoArrayResolve (std::string path, const ObjectVectorValue &vector)
   420 {
   421   NS_ASSERT (path != "");
   422   std::string::size_type tmp;
   423   tmp = path.find ("/");
   424   NS_ASSERT (tmp == 0);
   425   std::string::size_type next = path.find ("/", 1);
   426   if (next == std::string::npos)
   427     {
   428       NS_FATAL_ERROR ("vector path includes no index data on path=\""<<path<<"\"");
   429     }
   430   std::string item = path.substr (1, next-1);
   431   std::string pathLeft = path.substr (next, path.size ()-next);
   432 
   433   ArrayMatcher matcher = ArrayMatcher (item);
   434   for (uint32_t i = 0; i < vector.GetN (); i++)
   435     {
   436       if (matcher.Matches (i))
   437 	{
   438 	  std::ostringstream oss;
   439 	  oss << i;
   440 	  m_workStack.push_back (oss.str ());
   441 	  DoResolve (pathLeft, vector.Get (i));
   442 	  m_workStack.pop_back ();
   443 	}
   444     }
   445 }
   446 
   447 
   448 class ConfigImpl 
   449 {
   450 public:
   451   void Set (std::string path, const AttributeValue &value);
   452   void ConnectWithoutContext (std::string path, const CallbackBase &cb);
   453   void Connect (std::string path, const CallbackBase &cb);
   454   void DisconnectWithoutContext (std::string path, const CallbackBase &cb);
   455   void Disconnect (std::string path, const CallbackBase &cb);
   456   Config::MatchContainer LookupMatches (std::string path);
   457 
   458   void RegisterRootNamespaceObject (Ptr<Object> obj);
   459   void UnregisterRootNamespaceObject (Ptr<Object> obj);
   460 
   461   uint32_t GetRootNamespaceObjectN (void) const;
   462   Ptr<Object> GetRootNamespaceObject (uint32_t i) const;
   463   
   464 private:
   465   void ParsePath (std::string path, std::string *root, std::string *leaf) const;
   466   typedef std::vector<Ptr<Object> > Roots;
   467   Roots m_roots;
   468 };
   469 
   470 void 
   471 ConfigImpl::ParsePath (std::string path, std::string *root, std::string *leaf) const
   472 {
   473   std::string::size_type slash = path.find_last_of ("/");
   474   NS_ASSERT (slash != std::string::npos);
   475   *root = path.substr (0, slash);
   476   *leaf = path.substr (slash+1, path.size ()-(slash+1));
   477   NS_LOG_FUNCTION (path << *root << *leaf);
   478 }
   479 
   480 void 
   481 ConfigImpl::Set (std::string path, const AttributeValue &value)
   482 {
   483   std::string root, leaf;
   484   ParsePath (path, &root, &leaf);
   485   Config::MatchContainer container = LookupMatches (root);
   486   container.Set (leaf, value);
   487 }
   488 void 
   489 ConfigImpl::ConnectWithoutContext (std::string path, const CallbackBase &cb)
   490 {
   491   std::string root, leaf;
   492   ParsePath (path, &root, &leaf);
   493   Config::MatchContainer container = LookupMatches (root);
   494   container.ConnectWithoutContext (leaf, cb);
   495 }
   496 void 
   497 ConfigImpl::DisconnectWithoutContext (std::string path, const CallbackBase &cb)
   498 {
   499   std::string root, leaf;
   500   ParsePath (path, &root, &leaf);
   501   Config::MatchContainer container = LookupMatches (root);
   502   container.DisconnectWithoutContext (leaf, cb);
   503 }
   504 void 
   505 ConfigImpl::Connect (std::string path, const CallbackBase &cb)
   506 {
   507   std::string root, leaf;
   508   ParsePath (path, &root, &leaf);
   509   Config::MatchContainer container = LookupMatches (root);
   510   container.Connect (leaf, cb);
   511 }
   512 void 
   513 ConfigImpl::Disconnect (std::string path, const CallbackBase &cb)
   514 {
   515   std::string root, leaf;
   516   ParsePath (path, &root, &leaf);
   517   Config::MatchContainer container = LookupMatches (root);
   518   container.Disconnect (leaf, cb);
   519 }
   520 
   521 Config::MatchContainer 
   522 ConfigImpl::LookupMatches (std::string path)
   523 {
   524   NS_LOG_FUNCTION (path);
   525   class LookupMatchesResolver : public Resolver 
   526   {
   527   public:
   528     LookupMatchesResolver (std::string path)
   529       : Resolver (path)
   530     {}
   531     virtual void DoOne (Ptr<Object> object, std::string path) {
   532       m_objects.push_back (object);
   533       m_contexts.push_back (path);
   534     }
   535     std::vector<Ptr<Object> > m_objects;
   536     std::vector<std::string> m_contexts;
   537   } resolver = LookupMatchesResolver (path);
   538   for (Roots::const_iterator i = m_roots.begin (); i != m_roots.end (); i++)
   539     {
   540       resolver.Resolve (*i);
   541     }
   542 
   543   //
   544   // See if we can do something with the object name service.  Starting with
   545   // the root pointer zeroed indicates to the resolver that it should start
   546   // looking at the root of the "/Names" namespace during this go.
   547   //
   548   resolver.Resolve (0);
   549 
   550   return Config::MatchContainer (resolver.m_objects, resolver.m_contexts, path);
   551 }
   552 
   553 void 
   554 ConfigImpl::RegisterRootNamespaceObject (Ptr<Object> obj)
   555 {
   556   m_roots.push_back (obj);
   557 }
   558 
   559 void 
   560 ConfigImpl::UnregisterRootNamespaceObject (Ptr<Object> obj)
   561 {
   562   for (std::vector<Ptr<Object> >::iterator i = m_roots.begin (); i != m_roots.end (); i++)
   563     {
   564       if (*i == obj)
   565 	{
   566 	  m_roots.erase (i);
   567 	  return;
   568 	}
   569     }
   570 }
   571 
   572 uint32_t 
   573 ConfigImpl::GetRootNamespaceObjectN (void) const
   574 {
   575   return m_roots.size ();
   576 }
   577 Ptr<Object> 
   578 ConfigImpl::GetRootNamespaceObject (uint32_t i) const
   579 {
   580   return m_roots[i];
   581 }
   582 
   583 namespace Config {
   584 
   585 void Set (std::string path, const AttributeValue &value)
   586 {
   587   Singleton<ConfigImpl>::Get ()->Set (path, value);
   588 }
   589 void SetDefault (std::string name, const AttributeValue &value)
   590 {
   591   AttributeList::GetGlobal ()->Set (name, value);
   592 }
   593 bool SetDefaultFailSafe (std::string name, const AttributeValue &value)
   594 {
   595   return AttributeList::GetGlobal ()->SetFailSafe (name, value);
   596 }
   597 void SetGlobal (std::string name, const AttributeValue &value)
   598 {
   599   GlobalValue::Bind (name, value);
   600 }
   601 bool SetGlobalFailSafe (std::string name, const AttributeValue &value)
   602 {
   603   return GlobalValue::BindFailSafe (name, value);
   604 }
   605 void ConnectWithoutContext (std::string path, const CallbackBase &cb)
   606 {
   607   Singleton<ConfigImpl>::Get ()->ConnectWithoutContext (path, cb);
   608 }
   609 void DisconnectWithoutContext (std::string path, const CallbackBase &cb)
   610 {
   611   Singleton<ConfigImpl>::Get ()->DisconnectWithoutContext (path, cb);
   612 }
   613 void 
   614 Connect (std::string path, const CallbackBase &cb)
   615 {
   616   Singleton<ConfigImpl>::Get ()->Connect (path, cb);
   617 }
   618 void 
   619 Disconnect (std::string path, const CallbackBase &cb)
   620 {
   621   Singleton<ConfigImpl>::Get ()->Disconnect (path, cb);
   622 }
   623 Config::MatchContainer LookupMatches (std::string path)
   624 {
   625   return Singleton<ConfigImpl>::Get ()->LookupMatches (path);
   626 }
   627 
   628 void RegisterRootNamespaceObject (Ptr<Object> obj)
   629 {
   630   Singleton<ConfigImpl>::Get ()->RegisterRootNamespaceObject (obj);
   631 }
   632 
   633 void UnregisterRootNamespaceObject (Ptr<Object> obj)
   634 {
   635   Singleton<ConfigImpl>::Get ()->UnregisterRootNamespaceObject (obj);
   636 }
   637 
   638 uint32_t GetRootNamespaceObjectN (void)
   639 {
   640   return Singleton<ConfigImpl>::Get ()->GetRootNamespaceObjectN ();
   641 }
   642 
   643 Ptr<Object> GetRootNamespaceObject (uint32_t i)
   644 {
   645   return Singleton<ConfigImpl>::Get ()->GetRootNamespaceObject (i);
   646 }
   647 
   648 
   649 } // namespace Config
   650 
   651 } // namespace ns3
   652 
   653 #ifdef RUN_SELF_TESTS
   654 
   655 #include "test.h"
   656 #include "integer.h"
   657 #include "traced-value.h"
   658 #include "trace-source-accessor.h"
   659 #include "callback.h"
   660 
   661 namespace ns3 {
   662 
   663 class MyNode : public Object
   664 {
   665 public:
   666   static TypeId GetTypeId (void);
   667 
   668   void AddNodeA (Ptr<MyNode> a);
   669   void AddNodeB (Ptr<MyNode> b);
   670 
   671   void SetNodeA (Ptr<MyNode> a);
   672   void SetNodeB (Ptr<MyNode> b);
   673 
   674   int8_t GetA (void) const;
   675   int8_t GetB (void) const;
   676 
   677 private:
   678   std::vector<Ptr<MyNode> > m_nodesA;
   679   std::vector<Ptr<MyNode> > m_nodesB;
   680   Ptr<MyNode> m_nodeA;
   681   Ptr<MyNode> m_nodeB;
   682   int8_t m_a;
   683   int8_t m_b;
   684   TracedValue<int16_t> m_trace;
   685 };
   686 
   687 TypeId MyNode::GetTypeId (void)
   688 {
   689   static TypeId tid = TypeId ("MyNode")
   690     .SetParent<Object> ()
   691     .AddAttribute ("NodesA", "",
   692 		   ObjectVectorValue (),
   693 		   MakeObjectVectorAccessor (&MyNode::m_nodesA),
   694 		   MakeObjectVectorChecker<MyNode> ())
   695     .AddAttribute ("NodesB", "",
   696 		   ObjectVectorValue (),
   697 		   MakeObjectVectorAccessor (&MyNode::m_nodesB),
   698 		   MakeObjectVectorChecker<MyNode> ())
   699     .AddAttribute ("NodeA", "",
   700                    PointerValue (),
   701 		   MakePointerAccessor (&MyNode::m_nodeA),
   702 		   MakePointerChecker<MyNode> ())
   703     .AddAttribute ("NodeB", "",
   704                    PointerValue (),
   705 		   MakePointerAccessor (&MyNode::m_nodeB),
   706 		   MakePointerChecker<MyNode> ())
   707     .AddAttribute ("A", "",
   708 		   IntegerValue (10),
   709 		   MakeIntegerAccessor (&MyNode::m_a),
   710 		   MakeIntegerChecker<int8_t> ())
   711     .AddAttribute ("B", "",
   712 		   IntegerValue (9),
   713 		   MakeIntegerAccessor (&MyNode::m_b),
   714 		   MakeIntegerChecker<int8_t> ())
   715     .AddAttribute ("Source", "XX",
   716 		   IntegerValue (-1),
   717 		   MakeIntegerAccessor (&MyNode::m_trace),
   718 		   MakeIntegerChecker<int16_t> ())
   719     .AddTraceSource ("Source", "XX",
   720 		     MakeTraceSourceAccessor (&MyNode::m_trace))
   721     ;
   722   return tid;
   723 }
   724 
   725 void
   726 MyNode::SetNodeA (Ptr<MyNode> a)
   727 {
   728   m_nodeA = a;
   729 }
   730 void
   731 MyNode::SetNodeB (Ptr<MyNode> b)
   732 {
   733   m_nodeB = b;
   734 }
   735 void 
   736 MyNode::AddNodeA (Ptr<MyNode> a)
   737 {
   738   m_nodesA.push_back (a);
   739 }
   740 void 
   741 MyNode::AddNodeB (Ptr<MyNode> b)
   742 {
   743   m_nodesB.push_back (b);
   744 }
   745 int8_t 
   746 MyNode::GetA (void) const
   747 {
   748   return m_a;
   749 }
   750 int8_t 
   751 MyNode::GetB (void) const
   752 {
   753   return m_b;
   754 }
   755 
   756 
   757 class ConfigTest : public Test
   758 {
   759 public:
   760   ConfigTest ();
   761   virtual bool RunTests (void);
   762 private:
   763   void ChangeNotification (int16_t old, int16_t newValue);
   764   void ChangeNotificationWithPath (std::string path, int16_t old, int16_t newValue);
   765   int16_t m_traceNotification;
   766   std::string m_tracePath;
   767 };
   768 
   769 static ConfigTest g_configTestUnique;
   770 
   771 ConfigTest::ConfigTest ()
   772   : Test ("Config")
   773 {}
   774 
   775 void
   776 ConfigTest::ChangeNotification (int16_t oldValue, int16_t newValue)
   777 {
   778   m_traceNotification = newValue;
   779 }
   780 
   781 void 
   782 ConfigTest::ChangeNotificationWithPath (std::string path, int16_t old, int16_t newValue)
   783 {
   784   m_traceNotification = newValue;
   785   m_tracePath = path;
   786 }
   787 
   788 bool
   789 ConfigTest::RunTests (void)
   790 {
   791   bool result = true;
   792 
   793   Ptr<MyNode> root = CreateObject<MyNode> ();
   794   Config::RegisterRootNamespaceObject (root);
   795   Config::Set ("/A", IntegerValue (1));
   796   Config::Set ("/B", IntegerValue (-1));
   797   IntegerValue v;
   798   root->GetAttribute ("A", v);
   799   NS_TEST_ASSERT_EQUAL (v.Get (), 1);
   800   root->GetAttribute ("B", v);
   801   NS_TEST_ASSERT_EQUAL (v.Get (), -1);
   802 
   803   Ptr<MyNode> a = CreateObject<MyNode> ();
   804   root->SetNodeA (a);
   805   Config::Set ("/NodeA/A", IntegerValue (2));
   806   Config::Set ("/NodeA/B", IntegerValue (-2));
   807   a->GetAttribute ("A", v);
   808   NS_TEST_ASSERT_EQUAL (v.Get (), 2);
   809   a->GetAttribute ("B", v);
   810   NS_TEST_ASSERT_EQUAL (v.Get (), -2);
   811   Config::Set ("/NodeB/A", IntegerValue (3));
   812   Config::Set ("/NodeB/B", IntegerValue (-3));
   813   a->GetAttribute ("A", v);
   814   NS_TEST_ASSERT_EQUAL (v.Get (), 2);
   815   a->GetAttribute ("B", v);
   816   NS_TEST_ASSERT_EQUAL (v.Get (), -2);
   817 
   818   Ptr<MyNode> b = CreateObject<MyNode> ();
   819   a->SetNodeB (b);
   820   Config::Set ("/NodeA/NodeB/A", IntegerValue (4));
   821   Config::Set ("/NodeA/NodeB/B", IntegerValue (-4));
   822   b->GetAttribute ("A", v);
   823   NS_TEST_ASSERT_EQUAL (v.Get (), 4);
   824   b->GetAttribute ("B", v);
   825   NS_TEST_ASSERT_EQUAL (v.Get (), -4);
   826 
   827   Ptr<MyNode> c = CreateObject<MyNode> ();
   828   root->SetNodeB (c);
   829   Config::Set ("/NodeB/A", IntegerValue (5));
   830   Config::Set ("/NodeB/B", IntegerValue (-5));
   831   c->GetAttribute ("A", v);
   832   NS_TEST_ASSERT_EQUAL (v.Get (), 5);
   833   c->GetAttribute ("B", v);
   834   NS_TEST_ASSERT_EQUAL (v.Get (), -5);
   835 
   836 
   837   Ptr<MyNode> d0 = CreateObject<MyNode> ();
   838   Ptr<MyNode> d1 = CreateObject<MyNode> ();
   839   Ptr<MyNode> d2 = CreateObject<MyNode> ();
   840   Ptr<MyNode> d3 = CreateObject<MyNode> ();
   841   b->AddNodeB (d0);
   842   b->AddNodeB (d1);
   843   b->AddNodeB (d2);
   844   b->AddNodeB (d3);
   845   Config::Set ("/NodeA/NodeB/NodesB/0/A", IntegerValue (-11));
   846   d0->GetAttribute ("A", v);
   847   NS_TEST_ASSERT_EQUAL (v.Get (), -11);
   848   d0->GetAttribute ("B", v);
   849   NS_TEST_ASSERT_EQUAL (v.Get (), 9);
   850   d1->GetAttribute ("A", v);
   851   NS_TEST_ASSERT_EQUAL (v.Get (), 10);
   852   d1->GetAttribute ("B", v);
   853   NS_TEST_ASSERT_EQUAL (v.Get (), 9);
   854   Config::Set ("/NodeA/NodeB/NodesB/0|1/A", IntegerValue (-12));
   855   d0->GetAttribute ("A", v);
   856   NS_TEST_ASSERT_EQUAL (v.Get (), -12);
   857   d1->GetAttribute ("A", v);
   858   NS_TEST_ASSERT_EQUAL (v.Get (), -12);
   859   Config::Set ("/NodeA/NodeB/NodesB/|0|1|/A", IntegerValue (-13));
   860   d0->GetAttribute ("A", v);
   861   NS_TEST_ASSERT_EQUAL (v.Get (), -13);
   862   d1->GetAttribute ("A", v);
   863   NS_TEST_ASSERT_EQUAL (v.Get (), -13);
   864   Config::Set ("/NodeA/NodeB/NodesB/[0-2]/A", IntegerValue (-14));
   865   d0->GetAttribute ("A", v);
   866   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   867   d1->GetAttribute ("A", v);
   868   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   869   d2->GetAttribute ("A", v);
   870   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   871   Config::Set ("/NodeA/NodeB/NodesB/[1-3]/A", IntegerValue (-15));
   872   d0->GetAttribute ("A", v);
   873   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   874   d1->GetAttribute ("A", v);
   875   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   876   d2->GetAttribute ("A", v);
   877   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   878   d3->GetAttribute ("A", v);
   879   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   880   Config::Set ("/NodeA/NodeB/NodesB/[0-1]|3/A", IntegerValue (-16));
   881   d0->GetAttribute ("A", v);
   882   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   883   d1->GetAttribute ("A", v);
   884   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   885   d2->GetAttribute ("A", v);
   886   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   887   d3->GetAttribute ("A", v);
   888   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   889 
   890 
   891   Config::ConnectWithoutContext ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   892 		   MakeCallback (&ConfigTest::ChangeNotification, this));
   893   m_traceNotification = 0;
   894   // this should trigger no notification
   895   d2->SetAttribute ("Source", IntegerValue (-2));
   896   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   897   m_traceNotification = 0;
   898   // this should trigger a notification
   899   d1->SetAttribute ("Source", IntegerValue (-3));
   900   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   901   Config::DisconnectWithoutContext ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   902 		      MakeCallback (&ConfigTest::ChangeNotification, this));
   903   m_traceNotification = 0;
   904   // this should _not_ trigger a notification
   905   d1->SetAttribute ("Source", IntegerValue (-4));
   906   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   907 
   908   
   909   Config::Connect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   910 			      MakeCallback (&ConfigTest::ChangeNotificationWithPath, this));
   911   m_traceNotification = 0;
   912   // this should trigger no notification
   913   d2->SetAttribute ("Source", IntegerValue (-2));
   914   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   915   m_traceNotification = 0;
   916   m_tracePath = "";
   917   // this should trigger a notification
   918   d1->SetAttribute ("Source", IntegerValue (-3));
   919   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   920   NS_TEST_ASSERT_EQUAL (m_tracePath, "/NodeA/NodeB/NodesB/1/Source");
   921   m_traceNotification = 0;
   922   m_tracePath = "";
   923   // this should trigger a notification
   924   d3->SetAttribute ("Source", IntegerValue (-3));
   925   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   926   NS_TEST_ASSERT_EQUAL (m_tracePath, "/NodeA/NodeB/NodesB/3/Source");
   927   Config::Disconnect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   928 				 MakeCallback (&ConfigTest::ChangeNotificationWithPath, this));
   929   m_traceNotification = 0;
   930   // this should _not_ trigger a notification
   931   d1->SetAttribute ("Source", IntegerValue (-4));
   932   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   933 
   934   //
   935   // The Config system is intertwined with the Names system.  In the process
   936   // of parsing the paths above, we also created a NamesPriv singleton.  In
   937   // order to get a valgrind-clean run we need to clean up that singleton.
   938   //
   939   Names::Delete ();
   940 
   941   return result;
   942 }
   943 
   944 } // namespace ns3
   945 
   946 
   947 #endif /* RUN_SELF_TEST */