src/core/config.cc
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sun, 02 Mar 2008 21:00:37 +0100
changeset 2531 b451b5fc8b57
parent 2504 da3ec9cc3ba3
child 2532 86a40c7cbfe9
permissions -rw-r--r--
implement context-based trace connection
     1 #include "config.h"
     2 #include "singleton.h"
     3 #include "object.h"
     4 #include "global-value.h"
     5 #include "object-vector.h"
     6 #include "log.h"
     7 #include <sstream>
     8 
     9 NS_LOG_COMPONENT_DEFINE ("Config");
    10 
    11 namespace ns3 {
    12 
    13 class ArrayMatcher
    14 {
    15 public:
    16   ArrayMatcher (std::string element);
    17   bool Matches (uint32_t i) const;
    18 private:
    19   bool StringToUint32 (std::string str, uint32_t *value) const;
    20   std::string m_element;
    21 };
    22 
    23 
    24 ArrayMatcher::ArrayMatcher (std::string element)
    25   : m_element (element)
    26 {}
    27 bool 
    28 ArrayMatcher::Matches (uint32_t i) const
    29 {
    30   if (m_element == "*")
    31     {
    32       NS_LOG_DEBUG ("Array "<<i<<" matches *");
    33       return true;
    34     }
    35   std::string::size_type tmp;
    36   tmp = m_element.find ("|");
    37   if (tmp != std::string::npos)
    38     {
    39       std::string left = m_element.substr (0, tmp-0);
    40       std::string right = m_element.substr (tmp+1, m_element.size () - (tmp + 1));
    41       ArrayMatcher matcher = ArrayMatcher (left);
    42       if (matcher.Matches (i))
    43 	{
    44 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<left);
    45 	  return true;
    46 	}
    47       matcher = ArrayMatcher (right);
    48       if (matcher.Matches (i))
    49 	{
    50 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<right);
    51 	  return true;
    52 	}
    53       NS_LOG_DEBUG ("Array "<<i<<" does not match "<<m_element);
    54       return false;
    55     }
    56   std::string::size_type leftBracket = m_element.find ("[");
    57   std::string::size_type rightBracket = m_element.find ("]");
    58   std::string::size_type dash = m_element.find ("-");
    59   if (leftBracket == 0 && rightBracket == m_element.size () - 1 &&
    60       dash > leftBracket && dash < rightBracket)
    61     {
    62       std::string lowerBound = m_element.substr (leftBracket + 1, dash - (leftBracket + 1));
    63       std::string upperBound = m_element.substr (dash + 1, rightBracket - (dash + 1));
    64       uint32_t min;
    65       uint32_t max;
    66       if (StringToUint32 (lowerBound, &min) && 
    67 	  StringToUint32 (upperBound, &max) &&
    68 	  i >= min && i <= max)
    69         {
    70 	  NS_LOG_DEBUG ("Array "<<i<<" matches "<<m_element);
    71           return true;
    72         }
    73       else
    74 	{
    75 	  NS_LOG_DEBUG ("Array "<<i<<" does not "<<m_element);
    76 	  return false;
    77 	}
    78     }
    79   uint32_t value;
    80   if (StringToUint32 (m_element, &value) &&
    81       i == value)
    82     {
    83       NS_LOG_DEBUG ("Array "<<i<<" matches "<<m_element);
    84       return true;
    85     }
    86   NS_LOG_DEBUG ("Array "<<i<<" does not match "<<m_element);
    87   return false;
    88 }
    89 
    90 bool
    91 ArrayMatcher::StringToUint32 (std::string str, uint32_t *value) const
    92 {
    93   std::istringstream iss;
    94   iss.str (str);
    95   iss >> (*value);
    96   return !iss.bad () && !iss.fail ();
    97 }
    98 
    99 
   100 class Resolver
   101 {
   102 public:
   103   Resolver (std::string path);
   104   virtual ~Resolver ();
   105 
   106   void Resolve (Ptr<Object> root);
   107 private:
   108   void DoResolve (std::string path, Ptr<Object> root);
   109   void DoArrayResolve (std::string path, const ObjectVector &vector);
   110   void DoResolveOne (Ptr<Object> object, std::string name);
   111   std::string GetResolvedPath (std::string name) const;
   112   virtual void DoOne (Ptr<Object> object, std::string path, std::string name) = 0;
   113   std::vector<std::string> m_workStack;
   114   std::string m_path;
   115 };
   116 
   117 Resolver::Resolver (std::string path)
   118   : m_path (path)
   119 {}
   120 Resolver::~Resolver ()
   121 {}
   122 
   123 void 
   124 Resolver::Resolve (Ptr<Object> root)
   125 {
   126   DoResolve (m_path, root);
   127 }
   128 
   129 std::string
   130 Resolver::GetResolvedPath (std::string name) const
   131 {
   132   std::string fullPath = "";
   133   for (std::vector<std::string>::const_iterator i = m_workStack.begin (); i != m_workStack.end (); i++)
   134     {
   135       fullPath += "/" + *i;
   136     }
   137   fullPath += "/" + name;
   138   return fullPath;
   139 }
   140 
   141 void 
   142 Resolver::DoResolveOne (Ptr<Object> object, std::string name)
   143 {
   144   NS_LOG_DEBUG ("resolved="<<GetResolvedPath (name));
   145   DoOne (object, GetResolvedPath (name), name);
   146 }
   147 
   148 void
   149 Resolver::DoResolve (std::string path, Ptr<Object> root)
   150 {
   151   NS_ASSERT (path != "");
   152   std::string::size_type pos = path.find ("/");
   153   if (pos != 0)
   154     {
   155       NS_FATAL_ERROR ("path does not start with a \"/\": \""<<path<<"\"");
   156       return;
   157     }
   158   std::string::size_type next = path.find ("/", 1);
   159   if (next == std::string::npos)
   160     {
   161       std::string attributeName = path.substr (1, path.size ()-1);
   162       NS_LOG_DEBUG ("handle attr="<<attributeName);
   163       DoOne (root, GetResolvedPath (attributeName), attributeName);
   164       return;
   165     }
   166   std::string item = path.substr (1, next-1);
   167   std::string pathLeft = path.substr (next, path.size ()-next);
   168 
   169   std::string::size_type dollarPos = item.find ("$");
   170   if (dollarPos == 0)
   171     {
   172       // This is a call to GetObject
   173       std::string tidString = item.substr (1, item.size () - 1);
   174       NS_LOG_DEBUG ("GetObject="<<tidString<<"on path="<<GetResolvedPath (""));
   175       TypeId tid = TypeId::LookupByName (tidString);
   176       Ptr<Object> object = root->GetObject<Object> (tid);
   177       if (object == 0)
   178 	{
   179 	  NS_LOG_DEBUG ("GetObject ("<<tidString<<") failed on path="<<GetResolvedPath (""));
   180 	  return;
   181 	}
   182       m_workStack.push_back (item);
   183       DoResolve (pathLeft, object);
   184       m_workStack.pop_back ();
   185     }
   186   else 
   187     {
   188       // this is a normal attribute.
   189       TypeId tid = root->GetRealTypeId ();
   190       struct TypeId::AttributeInfo info;
   191       if (!tid.LookupAttributeByName (item, &info))
   192 	{
   193 	  NS_LOG_DEBUG ("Requested item="<<item<<" does not exist on path="<<GetResolvedPath (""));
   194 	  return;
   195 	}
   196       // attempt to cast to a pointer checker.
   197       const PtrChecker *ptr = dynamic_cast<const PtrChecker *> (PeekPointer (info.checker));
   198       if (ptr != 0)
   199 	{
   200 	  NS_LOG_DEBUG ("GetAttribute(ptr)="<<item<<" on path="<<GetResolvedPath (""));
   201 	  // XXX: This is not completely right because anything could be stored in a
   202 	  // Ptr<>. We really need to fix this by thinking seriously about our
   203 	  // object hierarchy.
   204 	  Ptr<Object> object = root->GetAttribute (item);
   205 	  if (object == 0)
   206 	    {
   207 	      NS_LOG_ERROR ("Requested object name=\""<<item<<
   208 			    "\" exists on path=\""<<GetResolvedPath ("")<<"\""
   209 			    " but is null.");
   210 	      return;
   211 	    }
   212 	  m_workStack.push_back (item);
   213 	  DoResolve (pathLeft, object);
   214 	  m_workStack.pop_back ();
   215 	}
   216       // attempt to cast to an object vector.
   217       const ObjectVectorChecker *vectorChecker = dynamic_cast<const ObjectVectorChecker *> (PeekPointer (info.checker));
   218       if (vectorChecker != 0)
   219 	{
   220 	  NS_LOG_DEBUG ("GetAttribute(vector)="<<item<<" on path="<<GetResolvedPath (""));
   221 	  ObjectVector vector = root->GetAttribute (item);
   222 	  m_workStack.push_back (item);
   223 	  DoArrayResolve (pathLeft, vector);
   224 	  m_workStack.pop_back ();
   225 	}
   226       // this could be anything else and we don't know what to do with it.
   227       // So, we just ignore it.
   228     }
   229 }
   230 
   231 void 
   232 Resolver::DoArrayResolve (std::string path, const ObjectVector &vector)
   233 {
   234   NS_ASSERT (path != "");
   235   std::string::size_type pos = path.find ("/");
   236   if (pos != 0)
   237     {
   238       NS_FATAL_ERROR ("path does not start with a \"/\": \""<<path<<"\"");
   239       return;
   240     }
   241   std::string::size_type next = path.find ("/", 1);
   242   if (next == std::string::npos)
   243     {
   244       NS_LOG_DEBUG ("vector path includes no index data on path=\""<<path<<"\"");
   245       return;
   246     }
   247   std::string item = path.substr (1, next-1);
   248   std::string pathLeft = path.substr (next, path.size ()-next);
   249 
   250   ArrayMatcher matcher = ArrayMatcher (item);
   251   for (uint32_t i = 0; i < vector.GetN (); i++)
   252     {
   253       if (matcher.Matches (i))
   254 	{
   255 	  std::ostringstream oss;
   256 	  oss << i;
   257 	  m_workStack.push_back (oss.str ());
   258 	  DoResolve (pathLeft, vector.Get (i));
   259 	  m_workStack.pop_back ();
   260 	}
   261     }
   262 }
   263 
   264 
   265 class ConfigImpl 
   266 {
   267 public:
   268   void Set (std::string path, Attribute value);
   269   void Connect (std::string path, const CallbackBase &cb);
   270   void ConnectWithContext (std::string path, const CallbackBase &cb);
   271   void Disconnect (std::string path, const CallbackBase &cb);
   272 
   273   void RegisterRootNamespaceObject (Ptr<Object> obj);
   274   void UnregisterRootNamespaceObject (Ptr<Object> obj);
   275   
   276 private:
   277   typedef std::vector<Ptr<Object> > Roots;
   278   Roots m_roots;
   279 };
   280 
   281 void 
   282 ConfigImpl::Set (std::string path, Attribute value)
   283 {
   284   class SetResolver : public Resolver 
   285   {
   286   public:
   287     SetResolver (std::string path, Attribute value)
   288       : Resolver (path),
   289 	m_value (value) {}
   290   private:
   291     virtual void DoOne (Ptr<Object> object, std::string path, std::string name) {
   292       object->SetAttribute (name, m_value);
   293     }
   294     Attribute m_value;
   295   } resolver = SetResolver (path, value);
   296   for (Roots::const_iterator i = m_roots.begin (); i != m_roots.end (); i++)
   297     {
   298       resolver.Resolve (*i);
   299     }
   300 }
   301 void 
   302 ConfigImpl::Connect (std::string path, const CallbackBase &cb)
   303 {
   304   class ConnectResolver : public Resolver 
   305   {
   306   public:
   307     ConnectResolver (std::string path, const CallbackBase &cb)
   308       : Resolver (path),
   309 	m_cb (cb) {}
   310   private:
   311     virtual void DoOne (Ptr<Object> object, std::string path, std::string name) {
   312       object->TraceSourceConnect (name, m_cb);
   313     }
   314     CallbackBase m_cb;
   315   } resolver = ConnectResolver (path, cb);
   316   for (Roots::const_iterator i = m_roots.begin (); i != m_roots.end (); i++)
   317     {
   318       resolver.Resolve (*i);
   319     }
   320 }
   321 void 
   322 ConfigImpl::Disconnect (std::string path, const CallbackBase &cb)
   323 {
   324   class DisconnectResolver : public Resolver 
   325   {
   326   public:
   327     DisconnectResolver (std::string path, const CallbackBase &cb)
   328       : Resolver (path),
   329 	m_cb (cb) {}
   330   private:
   331     virtual void DoOne (Ptr<Object> object, std::string path, std::string name) {
   332       object->TraceSourceDisconnect (name, m_cb);
   333     }
   334     CallbackBase m_cb;
   335   } resolver = DisconnectResolver (path, cb);
   336   for (Roots::const_iterator i = m_roots.begin (); i != m_roots.end (); i++)
   337     {
   338       resolver.Resolve (*i);
   339     }
   340 }
   341 void 
   342 ConfigImpl::ConnectWithContext (std::string path, const CallbackBase &cb)
   343 {
   344   class ConnectWithContextResolver : public Resolver 
   345   {
   346   public:
   347     ConnectWithContextResolver (std::string path, const CallbackBase &cb)
   348       : Resolver (path),
   349 	m_cb (cb) {}
   350   private:
   351     virtual void DoOne (Ptr<Object> object, std::string path, std::string name) {
   352       object->TraceSourceConnectWithContext (name, path, m_cb);
   353     }
   354     CallbackBase m_cb;
   355   } resolver = ConnectWithContextResolver (path, cb);
   356   for (Roots::const_iterator i = m_roots.begin (); i != m_roots.end (); i++)
   357     {
   358       resolver.Resolve (*i);
   359     }
   360 }
   361 void 
   362 ConfigImpl::RegisterRootNamespaceObject (Ptr<Object> obj)
   363 {
   364   m_roots.push_back (obj);
   365 }
   366 
   367 void 
   368 ConfigImpl::UnregisterRootNamespaceObject (Ptr<Object> obj)
   369 {
   370   for (std::vector<Ptr<Object> >::iterator i = m_roots.begin (); i != m_roots.end (); i++)
   371     {
   372       if (*i == obj)
   373 	{
   374 	  m_roots.erase (i);
   375 	  return;
   376 	}
   377     }
   378 }
   379 
   380 
   381 namespace Config {
   382 
   383 void Set (std::string path, Attribute value)
   384 {
   385   Singleton<ConfigImpl>::Get ()->Set (path, value);
   386 }
   387 void SetDefault (std::string name, Attribute value)
   388 {
   389   AttributeList::GetGlobal ()->Set (name, value);
   390 }
   391 void SetGlobal (std::string name, Attribute value)
   392 {
   393   GlobalValue::Bind (name, value);
   394 }
   395 void Connect (std::string path, const CallbackBase &cb)
   396 {
   397   Singleton<ConfigImpl>::Get ()->Connect (path, cb);
   398 }
   399 void Disconnect (std::string path, const CallbackBase &cb)
   400 {
   401   Singleton<ConfigImpl>::Get ()->Disconnect (path, cb);
   402 }
   403 void 
   404 ConnectWithContext (std::string path, const CallbackBase &cb)
   405 {
   406   Singleton<ConfigImpl>::Get ()->ConnectWithContext (path, cb);
   407 }
   408 
   409 void RegisterRootNamespaceObject (Ptr<Object> obj)
   410 {
   411   Singleton<ConfigImpl>::Get ()->RegisterRootNamespaceObject (obj);
   412 }
   413 
   414 void Unregister (Ptr<Object> obj)
   415 {
   416   Singleton<ConfigImpl>::Get ()->UnregisterRootNamespaceObject (obj);
   417 }
   418 
   419 } // namespace Config
   420 
   421 } // namespace ns3
   422 
   423 #ifdef RUN_SELF_TESTS
   424 
   425 #include "test.h"
   426 #include "integer.h"
   427 #include "traced-value.h"
   428 #include "trace-source-accessor.h"
   429 #include "callback.h"
   430 
   431 namespace ns3 {
   432 
   433 class MyNode : public Object
   434 {
   435 public:
   436   static TypeId GetTypeId (void);
   437 
   438   void AddNodeA (Ptr<MyNode> a);
   439   void AddNodeB (Ptr<MyNode> b);
   440 
   441   void SetNodeA (Ptr<MyNode> a);
   442   void SetNodeB (Ptr<MyNode> b);
   443 
   444   int8_t GetA (void) const;
   445   int8_t GetB (void) const;
   446 
   447 private:
   448   std::vector<Ptr<MyNode> > m_nodesA;
   449   std::vector<Ptr<MyNode> > m_nodesB;
   450   Ptr<MyNode> m_nodeA;
   451   Ptr<MyNode> m_nodeB;
   452   int8_t m_a;
   453   int8_t m_b;
   454   TracedValue<int16_t> m_trace;
   455 };
   456 
   457 TypeId MyNode::GetTypeId (void)
   458 {
   459   static TypeId tid = TypeId ("MyNode")
   460     .SetParent<Object> ()
   461     .AddAttribute ("NodesA", "",
   462 		   ObjectVector (),
   463 		   MakeObjectVectorAccessor (&MyNode::m_nodesA),
   464 		   MakeObjectVectorChecker ())
   465     .AddAttribute ("NodesB", "",
   466 		   ObjectVector (),
   467 		   MakeObjectVectorAccessor (&MyNode::m_nodesB),
   468 		   MakeObjectVectorChecker ())
   469     .AddAttribute ("NodeA", "",
   470 		   Ptr<MyNode> (0),
   471 		   MakePtrAccessor (&MyNode::m_nodeA),
   472 		   MakePtrChecker<MyNode> ())
   473     .AddAttribute ("NodeB", "",
   474 		   Ptr<MyNode> (0),
   475 		   MakePtrAccessor (&MyNode::m_nodeB),
   476 		   MakePtrChecker<MyNode> ())
   477     .AddAttribute ("A", "",
   478 		   Integer (10),
   479 		   MakeIntegerAccessor (&MyNode::m_a),
   480 		   MakeIntegerChecker<int8_t> ())
   481     .AddAttribute ("B", "",
   482 		   Integer (9),
   483 		   MakeIntegerAccessor (&MyNode::m_b),
   484 		   MakeIntegerChecker<int8_t> ())
   485     .AddAttribute ("Source", "XX",
   486 		   Integer (-1),
   487 		   MakeIntegerAccessor (&MyNode::m_trace),
   488 		   MakeIntegerChecker<int16_t> ())
   489     .AddTraceSource ("Source", "XX",
   490 		     MakeTraceSourceAccessor (&MyNode::m_trace))
   491     ;
   492   return tid;
   493 }
   494 
   495 void
   496 MyNode::SetNodeA (Ptr<MyNode> a)
   497 {
   498   m_nodeA = a;
   499 }
   500 void
   501 MyNode::SetNodeB (Ptr<MyNode> b)
   502 {
   503   m_nodeB = b;
   504 }
   505 void 
   506 MyNode::AddNodeA (Ptr<MyNode> a)
   507 {
   508   m_nodesA.push_back (a);
   509 }
   510 void 
   511 MyNode::AddNodeB (Ptr<MyNode> b)
   512 {
   513   m_nodesB.push_back (b);
   514 }
   515 int8_t 
   516 MyNode::GetA (void) const
   517 {
   518   return m_a;
   519 }
   520 int8_t 
   521 MyNode::GetB (void) const
   522 {
   523   return m_b;
   524 }
   525 
   526 
   527 class ConfigTest : public Test
   528 {
   529 public:
   530   ConfigTest ();
   531   virtual bool RunTests (void);
   532 private:
   533   void ChangeNotification (int16_t old, int16_t newValue);
   534   void ChangeNotificationWithPath (std::string path, int16_t old, int16_t newValue);
   535   int16_t m_traceNotification;
   536   std::string m_tracePath;
   537 };
   538 
   539 static ConfigTest g_configTestUnique;
   540 
   541 ConfigTest::ConfigTest ()
   542   : Test ("Config")
   543 {}
   544 
   545 void
   546 ConfigTest::ChangeNotification (int16_t oldValue, int16_t newValue)
   547 {
   548   m_traceNotification = newValue;
   549 }
   550 
   551 void 
   552 ConfigTest::ChangeNotificationWithPath (std::string path, int16_t old, int16_t newValue)
   553 {
   554   m_traceNotification = newValue;
   555   m_tracePath = path;
   556 }
   557 
   558 bool
   559 ConfigTest::RunTests (void)
   560 {
   561   bool result = true;
   562 
   563   Ptr<MyNode> root = CreateObject<MyNode> ();
   564   Config::RegisterRootNamespaceObject (root);
   565   Config::Set ("/A", Integer (1));
   566   Config::Set ("/B", Integer (-1));
   567   Integer v = root->GetAttribute ("A");
   568   NS_TEST_ASSERT_EQUAL (v.Get (), 1);
   569   v = root->GetAttribute ("B");
   570   NS_TEST_ASSERT_EQUAL (v.Get (), -1);
   571 
   572   Ptr<MyNode> a = CreateObject<MyNode> ();
   573   root->SetNodeA (a);
   574   Config::Set ("/NodeA/A", Integer (2));
   575   Config::Set ("/NodeA/B", Integer (-2));
   576   v = a->GetAttribute ("A");
   577   NS_TEST_ASSERT_EQUAL (v.Get (), 2);
   578   v = a->GetAttribute ("B");
   579   NS_TEST_ASSERT_EQUAL (v.Get (), -2);
   580   Config::Set ("/NodeB/A", Integer (3));
   581   Config::Set ("/NodeB/B", Integer (-3));
   582   v = a->GetAttribute ("A");
   583   NS_TEST_ASSERT_EQUAL (v.Get (), 2);
   584   v = a->GetAttribute ("B");
   585   NS_TEST_ASSERT_EQUAL (v.Get (), -2);
   586 
   587   Ptr<MyNode> b = CreateObject<MyNode> ();
   588   a->SetNodeB (b);
   589   Config::Set ("/NodeA/NodeB/A", Integer (4));
   590   Config::Set ("/NodeA/NodeB/B", Integer (-4));
   591   v = b->GetAttribute ("A");
   592   NS_TEST_ASSERT_EQUAL (v.Get (), 4);
   593   v = b->GetAttribute ("B");
   594   NS_TEST_ASSERT_EQUAL (v.Get (), -4);
   595 
   596   Ptr<MyNode> c = CreateObject<MyNode> ();
   597   root->SetNodeB (c);
   598   Config::Set ("/NodeB/A", Integer (5));
   599   Config::Set ("/NodeB/B", Integer (-5));
   600   v = c->GetAttribute ("A");
   601   NS_TEST_ASSERT_EQUAL (v.Get (), 5);
   602   v = c->GetAttribute ("B");
   603   NS_TEST_ASSERT_EQUAL (v.Get (), -5);
   604 
   605 
   606   Ptr<MyNode> d0 = CreateObject<MyNode> ();
   607   Ptr<MyNode> d1 = CreateObject<MyNode> ();
   608   Ptr<MyNode> d2 = CreateObject<MyNode> ();
   609   Ptr<MyNode> d3 = CreateObject<MyNode> ();
   610   b->AddNodeB (d0);
   611   b->AddNodeB (d1);
   612   b->AddNodeB (d2);
   613   b->AddNodeB (d3);
   614   Config::Set ("/NodeA/NodeB/NodesB/0/A", Integer (-11));
   615   v = d0->GetAttribute ("A");
   616   NS_TEST_ASSERT_EQUAL (v.Get (), -11);
   617   v = d0->GetAttribute ("B");
   618   NS_TEST_ASSERT_EQUAL (v.Get (), 9);
   619   v = d1->GetAttribute ("A");
   620   NS_TEST_ASSERT_EQUAL (v.Get (), 10);
   621   v = d1->GetAttribute ("B");
   622   NS_TEST_ASSERT_EQUAL (v.Get (), 9);
   623   Config::Set ("/NodeA/NodeB/NodesB/0|1/A", Integer (-12));
   624   v = d0->GetAttribute ("A");
   625   NS_TEST_ASSERT_EQUAL (v.Get (), -12);
   626   v = d1->GetAttribute ("A");
   627   NS_TEST_ASSERT_EQUAL (v.Get (), -12);
   628   Config::Set ("/NodeA/NodeB/NodesB/|0|1|/A", Integer (-13));
   629   v = d0->GetAttribute ("A");
   630   NS_TEST_ASSERT_EQUAL (v.Get (), -13);
   631   v = d1->GetAttribute ("A");
   632   NS_TEST_ASSERT_EQUAL (v.Get (), -13);
   633   Config::Set ("/NodeA/NodeB/NodesB/[0-2]/A", Integer (-14));
   634   v = d0->GetAttribute ("A");
   635   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   636   v = d1->GetAttribute ("A");
   637   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   638   v = d2->GetAttribute ("A");
   639   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   640   Config::Set ("/NodeA/NodeB/NodesB/[1-3]/A", Integer (-15));
   641   v = d0->GetAttribute ("A");
   642   NS_TEST_ASSERT_EQUAL (v.Get (), -14);
   643   v = d1->GetAttribute ("A");
   644   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   645   v = d2->GetAttribute ("A");
   646   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   647   v = d3->GetAttribute ("A");
   648   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   649   Config::Set ("/NodeA/NodeB/NodesB/[0-1]|3/A", Integer (-16));
   650   v = d0->GetAttribute ("A");
   651   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   652   v = d1->GetAttribute ("A");
   653   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   654   v = d2->GetAttribute ("A");
   655   NS_TEST_ASSERT_EQUAL (v.Get (), -15);
   656   v = d3->GetAttribute ("A");
   657   NS_TEST_ASSERT_EQUAL (v.Get (), -16);
   658 
   659 
   660   Config::Connect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   661 		   MakeCallback (&ConfigTest::ChangeNotification, this));
   662   m_traceNotification = 0;
   663   // this should trigger no notification
   664   d2->SetAttribute ("Source", Integer (-2));
   665   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   666   m_traceNotification = 0;
   667   // this should trigger a notification
   668   d1->SetAttribute ("Source", Integer (-3));
   669   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   670   Config::Disconnect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   671 		      MakeCallback (&ConfigTest::ChangeNotification, this));
   672   m_traceNotification = 0;
   673   // this should _not_ trigger a notification
   674   d1->SetAttribute ("Source", Integer (-4));
   675   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   676 
   677   
   678   Config::ConnectWithContext ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   679 			      MakeCallback (&ConfigTest::ChangeNotificationWithPath, this));
   680   m_traceNotification = 0;
   681   // this should trigger no notification
   682   d2->SetAttribute ("Source", Integer (-2));
   683   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   684   m_traceNotification = 0;
   685   m_tracePath = "";
   686   // this should trigger a notification
   687   d1->SetAttribute ("Source", Integer (-3));
   688   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   689   NS_TEST_ASSERT_EQUAL (m_tracePath, "/NodeA/NodeB/NodesB/1/Source")
   690   m_traceNotification = 0;
   691   m_tracePath = "";
   692   // this should trigger a notification
   693   d3->SetAttribute ("Source", Integer (-3));
   694   NS_TEST_ASSERT_EQUAL (m_traceNotification, -3);
   695   NS_TEST_ASSERT_EQUAL (m_tracePath, "/NodeA/NodeB/NodesB/3/Source");
   696   // Yes, disconnection _cannot_ work with 'context-based connection.
   697   // XXX: what do we do about this ?
   698   Config::Disconnect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
   699 		      MakeCallback (&ConfigTest::ChangeNotificationWithPath, this));
   700   m_traceNotification = 0;
   701   // this should _not_ trigger a notification
   702   d1->SetAttribute ("Source", Integer (-4));
   703   NS_TEST_ASSERT_EQUAL (m_traceNotification, 0);
   704 
   705 
   706 
   707   return result;
   708 }
   709 
   710 } // namespace ns3
   711 
   712 
   713 #endif /* RUN_SELF_TEST */