table: allow strategy versioning

refs #1893

Change-Id: I44eb8dd319d737d786b6ce6f753ae2f9076cf9c1
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index 58300fe..323bb3d 100644
--- a/daemon/fw/best-route-strategy2.cpp
+++ b/daemon/fw/best-route-strategy2.cpp
@@ -31,8 +31,7 @@
 
 NFD_LOG_INIT("BestRouteStrategy2");
 
-/// \todo set to ndn:/localhost/nfd/strategy/best-route/%FD%02 after #1893 completion
-const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route");
+const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%02");
 /// \todo don't use fixed interval; make it adaptive or use exponential back-off #1913
 const time::milliseconds BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL(100);
 
diff --git a/daemon/fw/broadcast-strategy.cpp b/daemon/fw/broadcast-strategy.cpp
index c619e04..48ab17b 100644
--- a/daemon/fw/broadcast-strategy.cpp
+++ b/daemon/fw/broadcast-strategy.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,14 +21,14 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "broadcast-strategy.hpp"
 
 namespace nfd {
 namespace fw {
 
-const Name BroadcastStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/broadcast");
+const Name BroadcastStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/broadcast/%FD%01");
 
 BroadcastStrategy::BroadcastStrategy(Forwarder& forwarder, const Name& name)
   : Strategy(forwarder, name)
diff --git a/daemon/fw/client-control-strategy.cpp b/daemon/fw/client-control-strategy.cpp
index f8d83a8..d3700e3 100644
--- a/daemon/fw/client-control-strategy.cpp
+++ b/daemon/fw/client-control-strategy.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "client-control-strategy.hpp"
 #include "core/logger.hpp"
@@ -30,7 +31,8 @@
 
 NFD_LOG_INIT("ClientControlStrategy");
 
-const Name ClientControlStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/client-control");
+const Name
+ClientControlStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/client-control/%FD%01");
 
 ClientControlStrategy::ClientControlStrategy(Forwarder& forwarder, const Name& name)
   : BestRouteStrategy(forwarder, name)
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
index 925a3cf..bb93bd5 100644
--- a/daemon/fw/ncc-strategy.cpp
+++ b/daemon/fw/ncc-strategy.cpp
@@ -30,7 +30,7 @@
 namespace nfd {
 namespace fw {
 
-const Name NccStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/ncc");
+const Name NccStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/ncc/%FD%01");
 
 NccStrategy::NccStrategy(Forwarder& forwarder, const Name& name)
   : Strategy(forwarder, name)
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index f910383..e1ff23c 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -37,6 +37,13 @@
 class Strategy : public enable_shared_from_this<Strategy>, noncopyable
 {
 public:
+  /** \brief construct a strategy instance
+   *  \param forwarder a reference to the Forwarder, used to enable actions and accessors.
+   *         Strategy subclasses should pass this reference,
+   *         and should not keep a reference themselves.
+   *  \param name the strategy Name.
+   *         It's recommended to include a version number as the last component.
+   */
   Strategy(Forwarder& forwarder, const Name& name);
 
   virtual
diff --git a/daemon/table/strategy-choice.cpp b/daemon/table/strategy-choice.cpp
index aa5db1b..6500191 100644
--- a/daemon/table/strategy-choice.cpp
+++ b/daemon/table/strategy-choice.cpp
@@ -44,9 +44,14 @@
 }
 
 bool
-StrategyChoice::hasStrategy(const Name& strategyName) const
+StrategyChoice::hasStrategy(const Name& strategyName, bool isExact) const
 {
-  return m_strategyInstances.count(strategyName) > 0;
+  if (isExact) {
+    return m_strategyInstances.count(strategyName) > 0;
+  }
+  else {
+    return static_cast<bool>(this->getStrategy(strategyName));
+  }
 }
 
 bool
@@ -64,35 +69,51 @@
   return true;
 }
 
+shared_ptr<fw::Strategy>
+StrategyChoice::getStrategy(const Name& strategyName) const
+{
+  shared_ptr<fw::Strategy> candidate;
+  for (StrategyInstanceTable::const_iterator it = m_strategyInstances.lower_bound(strategyName);
+       it != m_strategyInstances.end() && strategyName.isPrefixOf(it->first); ++it) {
+    switch (it->first.size() - strategyName.size()) {
+    case 0: // exact match
+      return it->second;
+    case 1: // unversioned strategyName matches versioned strategy
+      candidate = it->second;
+      break;
+    }
+  }
+  return candidate;
+}
+
 bool
 StrategyChoice::insert(const Name& prefix, const Name& strategyName)
 {
-  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.lookup(prefix);
-  shared_ptr<Entry> entry = nameTreeEntry->getStrategyChoiceEntry();
-  shared_ptr<Strategy> oldStrategy;
-
-  if (static_cast<bool>(entry)) {
-    if (entry->getStrategy().getName() == strategyName) {
-      NFD_LOG_TRACE("insert(" << prefix << "," << strategyName << ") not changing");
-      return true;
-    }
-    oldStrategy = entry->getStrategy().shared_from_this();
-    NFD_LOG_TRACE("insert(" << prefix << "," << strategyName << ") "
-                  "changing from " << oldStrategy->getName());
-  }
-
   shared_ptr<Strategy> strategy = this->getStrategy(strategyName);
   if (!static_cast<bool>(strategy)) {
     NFD_LOG_ERROR("insert(" << prefix << "," << strategyName << ") strategy not installed");
     return false;
   }
 
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.lookup(prefix);
+  shared_ptr<Entry> entry = nameTreeEntry->getStrategyChoiceEntry();
+  shared_ptr<Strategy> oldStrategy;
+  if (static_cast<bool>(entry)) {
+    if (entry->getStrategy().getName() == strategy->getName()) {
+      NFD_LOG_TRACE("insert(" << prefix << ") not changing " << strategy->getName());
+      return true;
+    }
+    oldStrategy = entry->getStrategy().shared_from_this();
+    NFD_LOG_TRACE("insert(" << prefix << ") changing from " << oldStrategy->getName() <<
+                  " to " << strategy->getName());
+  }
+
   if (!static_cast<bool>(entry)) {
     oldStrategy = this->findEffectiveStrategy(prefix).shared_from_this();
     entry = make_shared<Entry>(prefix);
     nameTreeEntry->setStrategyChoiceEntry(entry);
     ++m_nItems;
-    NFD_LOG_TRACE("insert(" << prefix << "," << strategyName << ") new entry");
+    NFD_LOG_TRACE("insert(" << prefix << ") new entry " << strategy->getName());
   }
 
   this->changeStrategy(entry, oldStrategy, strategy);
@@ -187,13 +208,6 @@
   return findEffectiveStrategy(nameTreeEntry);
 }
 
-shared_ptr<fw::Strategy>
-StrategyChoice::getStrategy(const Name& strategyName)
-{
-  StrategyInstanceTable::iterator it = m_strategyInstances.find(strategyName);
-  return it != m_strategyInstances.end() ? it->second : shared_ptr<fw::Strategy>();
-}
-
 void
 StrategyChoice::setDefaultStrategy(shared_ptr<Strategy> strategy)
 {
diff --git a/daemon/table/strategy-choice.hpp b/daemon/table/strategy-choice.hpp
index 9441dcc..d454a29 100644
--- a/daemon/table/strategy-choice.hpp
+++ b/daemon/table/strategy-choice.hpp
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #ifndef NFD_DAEMON_TABLE_STRATEGY_CHOICE_HPP
 #define NFD_DAEMON_TABLE_STRATEGY_CHOICE_HPP
@@ -31,16 +31,29 @@
 
 namespace nfd {
 
+/** \brief represents the Strategy Choice table
+ *
+ *  The Strategy Choice table maintains available Strategy types,
+ *  and associates Name prefixes with Strategy types.
+ *
+ *  Each strategy is identified by a strategyName.
+ *  It's recommended to include a version number as the last component of strategyName.
+ *
+ *  A Name prefix is owned by a strategy if a longest prefix match on the
+ *  Strategy Choice table returns that strategy.
+ */
 class StrategyChoice : noncopyable
 {
 public:
   StrategyChoice(NameTree& nameTree, shared_ptr<fw::Strategy> defaultStrategy);
 
 public: // available Strategy types
-  /** \return true if strategy is installed
+  /** \brief determines if a strategy is installed
+   *  \param isExact true to require exact match, false to permit unversioned strategyName
+   *  \return true if strategy is installed
    */
   bool
-  hasStrategy(const Name& strategyName) const;
+  hasStrategy(const Name& strategyName, bool isExact = false) const;
 
   /** \brief install a strategy
    *  \return true if installed; false if not installed due to duplicate strategyName
@@ -50,8 +63,13 @@
 
 public: // Strategy Choice table
   /** \brief set strategy of prefix to be strategyName
-   *  \param strategyName the strategy to be used, must be installed
+   *  \param strategyName the strategy to be used
    *  \return true on success
+   *
+   *  This method set a strategy onto a Name prefix.
+   *  The strategy must have been installed.
+   *  The strategyName can either be exact (contains version component),
+   *  or omit the version component to pick the latest version.
    */
   bool
   insert(const Name& prefix, const Name& strategyName);
@@ -125,8 +143,11 @@
   end() const;
 
 private:
+  /** \brief get Strategy instance by strategyName
+   *  \param strategyName a versioned or unversioned strategyName
+   */
   shared_ptr<fw::Strategy>
-  getStrategy(const Name& strategyName);
+  getStrategy(const Name& strategyName) const;
 
   void
   setDefaultStrategy(shared_ptr<fw::Strategy> strategy);
diff --git a/tests/daemon/table/strategy-choice.cpp b/tests/daemon/table/strategy-choice.cpp
index 0f48538..cf19138 100644
--- a/tests/daemon/table/strategy-choice.cpp
+++ b/tests/daemon/table/strategy-choice.cpp
@@ -210,6 +210,73 @@
   BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
 }
 
+BOOST_AUTO_TEST_CASE(Versioning)
+{
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  Name nameP1("ndn:/strategy/P/%FD%01");
+  Name nameP2("ndn:/strategy/P/%FD%02");
+  Name name3("ndn:/%FD%03");
+  Name name4("ndn:/%FD%04");
+  Name nameQ("ndn:/strategy/Q");
+  Name nameQ5("ndn:/strategy/Q/%FD%05");
+  shared_ptr<Strategy> strategyP1 = make_shared<DummyStrategy>(ref(forwarder), nameP1);
+  shared_ptr<Strategy> strategyP2 = make_shared<DummyStrategy>(ref(forwarder), nameP2);
+  shared_ptr<Strategy> strategy3  = make_shared<DummyStrategy>(ref(forwarder), name3);
+  shared_ptr<Strategy> strategy4  = make_shared<DummyStrategy>(ref(forwarder), name4);
+  shared_ptr<Strategy> strategyQ  = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+  shared_ptr<Strategy> strategyQ5 = make_shared<DummyStrategy>(ref(forwarder), nameQ5);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+
+  // install
+  BOOST_CHECK_EQUAL(table.install(strategyP1), true);
+  BOOST_CHECK_EQUAL(table.install(strategyP1), false);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP,  false), true);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP,  true),  false);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP1, true),  true);
+
+  BOOST_CHECK_EQUAL(table.install(strategyP2), true);
+  BOOST_CHECK_EQUAL(table.install(strategy3),  true);
+  BOOST_CHECK_EQUAL(table.install(strategy4),  true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ),  true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ5), true);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  // exact match, { '/'=>Q }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP));
+  // { '/'=>P2 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP2);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP1));
+  // { '/'=>P1 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP1);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP2));
+  // { '/'=>P2 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP2);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(! table.insert("ndn:/", "ndn:/strategy/A"));
+  // not installed
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(! table.insert("ndn:/", "ndn:/strategy/Z"));
+  // not installed
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameP1));
+  BOOST_CHECK(table.insert("ndn:/", "ndn:/"));
+  // match one component longer only, { '/'=>4 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), name4);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests