Refactor config file parsing and improve error handling

Change-Id: I8a54ba1f0d414bea70923e0f45ca354ac7e957a3
diff --git a/src/ndn-traffic-client.cpp b/src/ndn-traffic-client.cpp
index adba253..005b8d4 100644
--- a/src/ndn-traffic-client.cpp
+++ b/src/ndn-traffic-client.cpp
@@ -18,7 +18,6 @@
  * Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  */
 
-#include "logger.hpp"
 #include "util.hpp"
 
 #include <ndn-cxx/face.hpp>
@@ -27,7 +26,6 @@
 #include <ndn-cxx/util/backports.hpp>
 #include <ndn-cxx/util/random.hpp>
 
-#include <cctype>
 #include <limits>
 #include <vector>
 
@@ -36,7 +34,6 @@
 #include <boost/asio/signal_set.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/logic/tribool.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/program_options/options_description.hpp>
 #include <boost/program_options/parsers.hpp>
@@ -58,12 +55,6 @@
   {
   }
 
-  bool
-  hasError() const
-  {
-    return m_hasError;
-  }
-
   void
   setMaximumInterests(uint64_t maxInterests)
   {
@@ -83,15 +74,30 @@
     m_wantQuiet = true;
   }
 
-  void
+  int
   run()
   {
     m_logger.initializeLog(to_string(random::generateWord32()));
-    initializeTrafficConfiguration();
+
+    if (!readConfigurationFile(m_configurationFile, m_trafficPatterns, m_logger)) {
+      return 2;
+    }
+
+    if (!checkTrafficPatternCorrectness()) {
+      m_logger.log("ERROR: Traffic configuration provided is not proper", false, true);
+      return 2;
+    }
+
+    m_logger.log("Traffic configuration file processing completed.\n", true, false);
+    for (std::size_t i = 0; i < m_trafficPatterns.size(); i++) {
+      m_logger.log("Traffic Pattern Type #" + to_string(i + 1), false, false);
+      m_trafficPatterns[i].printTrafficConfiguration(m_logger);
+      m_logger.log("", false, false);
+    }
 
     if (m_nMaximumInterests == 0) {
       logStatistics();
-      return;
+      return 0;
     }
 
     m_signalSet.async_wait([this] (auto&&...) { this->stop(); });
@@ -102,11 +108,12 @@
 
     try {
       m_face.processEvents();
+      return m_hasError ? 1 : 0;
     }
     catch (const std::exception& e) {
       m_logger.log("ERROR: "s + e.what(), true, true);
-      m_hasError = true;
       m_ioService.stop();
+      return 1;
     }
   }
 
@@ -120,14 +127,12 @@
       std::string detail;
 
       detail += "TrafficPercentage=" + to_string(m_trafficPercentage) + ", ";
-      if (!m_name.empty())
-        detail += "Name=" + m_name + ", ";
+      detail += "Name=" + m_name + ", ";
       if (m_nameAppendBytes)
         detail += "NameAppendBytes=" + to_string(*m_nameAppendBytes) + ", ";
       if (m_nameAppendSeqNum)
         detail += "NameAppendSequenceNumber=" + to_string(*m_nameAppendSeqNum) + ", ";
-      if (!boost::logic::indeterminate(m_mustBeFresh))
-        detail += "MustBeFresh=" + to_string(bool(m_mustBeFresh)) + ", ";
+      detail += "MustBeFresh=" + to_string(m_mustBeFresh) + ", ";
       if (m_nonceDuplicationPercentage > 0)
         detail += "NonceDuplicationPercentage=" + to_string(m_nonceDuplicationPercentage) + ", ";
       if (m_interestLifetime >= 0_ms)
@@ -143,36 +148,45 @@
     }
 
     bool
-    processConfigurationDetail(const std::string& detail, Logger& logger, int lineNumber)
+    parseConfigurationLine(const std::string& line, Logger& logger, int lineNumber)
     {
       std::string parameter, value;
-      if (parseParameterAndValue(detail, parameter, value)) {
-        if (parameter == "TrafficPercentage")
-          m_trafficPercentage = std::stoul(value);
-        else if (parameter == "Name")
-          m_name = value;
-        else if (parameter == "NameAppendBytes")
-          m_nameAppendBytes = std::stoul(value);
-        else if (parameter == "NameAppendSequenceNumber")
-          m_nameAppendSeqNum = std::stoull(value);
-        else if (parameter == "MustBeFresh")
-          m_mustBeFresh = bool(std::stoul(value));
-        else if (parameter == "NonceDuplicationPercentage")
-          m_nonceDuplicationPercentage = std::stoul(value);
-        else if (parameter == "InterestLifetime")
-          m_interestLifetime = time::milliseconds(std::stoul(value));
-        else if (parameter == "NextHopFaceId")
-          m_nextHopFaceId = std::stoull(value);
-        else if (parameter == "ExpectedContent")
-          m_expectedContent = value;
-        else
-          logger.log("Line " + to_string(lineNumber) +
-                     " - Unknown parameter '" + parameter + "'", false, true);
+      if (!extractParameterAndValue(line, parameter, value)) {
+        logger.log("Line " + to_string(lineNumber) + " - Invalid syntax: " + line,
+                   false, true);
+        return false;
+      }
+
+      if (parameter == "TrafficPercentage") {
+        m_trafficPercentage = std::stoul(value);
+      }
+      else if (parameter == "Name") {
+        m_name = value;
+      }
+      else if (parameter == "NameAppendBytes") {
+        m_nameAppendBytes = std::stoul(value);
+      }
+      else if (parameter == "NameAppendSequenceNumber") {
+        m_nameAppendSeqNum = std::stoull(value);
+      }
+      else if (parameter == "MustBeFresh") {
+        m_mustBeFresh = parseBoolean(value);
+      }
+      else if (parameter == "NonceDuplicationPercentage") {
+        m_nonceDuplicationPercentage = std::stoul(value);
+      }
+      else if (parameter == "InterestLifetime") {
+        m_interestLifetime = time::milliseconds(std::stoul(value));
+      }
+      else if (parameter == "NextHopFaceId") {
+        m_nextHopFaceId = std::stoull(value);
+      }
+      else if (parameter == "ExpectedContent") {
+        m_expectedContent = value;
       }
       else {
-        logger.log("Line " + to_string(lineNumber) +
-                   " - Invalid traffic configuration line: " + detail, false, true);
-        return false;
+        logger.log("Line " + to_string(lineNumber) + " - Ignoring unknown parameter: " + parameter,
+                   false, true);
       }
       return true;
     }
@@ -188,7 +202,7 @@
     std::string m_name;
     optional<std::size_t> m_nameAppendBytes;
     optional<uint64_t> m_nameAppendSeqNum;
-    boost::logic::tribool m_mustBeFresh = boost::logic::indeterminate;
+    bool m_mustBeFresh = false;
     uint8_t m_nonceDuplicationPercentage = 0;
     time::milliseconds m_interestLifetime = -1_ms;
     uint64_t m_nextHopFaceId = 0;
@@ -272,88 +286,10 @@
   bool
   checkTrafficPatternCorrectness() const
   {
+    // TODO
     return true;
   }
 
-  void
-  parseConfigurationFile()
-  {
-    std::ifstream patternFile;
-    m_logger.log("Parsing traffic configuration file: " + m_configurationFile, true, true);
-
-    patternFile.open(m_configurationFile.data());
-    if (patternFile.is_open()) {
-      int lineNumber = 0;
-      std::string patternLine;
-      while (getline(patternFile, patternLine)) {
-        lineNumber++;
-        if (std::isalpha(patternLine[0])) {
-          InterestTrafficConfiguration interestData;
-          bool shouldSkipLine = false;
-          if (interestData.processConfigurationDetail(patternLine, m_logger, lineNumber)) {
-            while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) {
-              lineNumber++;
-              if (!interestData.processConfigurationDetail(patternLine, m_logger, lineNumber)) {
-                shouldSkipLine = true;
-                break;
-              }
-            }
-            lineNumber++;
-          }
-          else {
-            shouldSkipLine = true;
-          }
-          if (!shouldSkipLine) {
-            if (interestData.checkTrafficDetailCorrectness()) {
-              m_trafficPatterns.push_back(interestData);
-            }
-          }
-        }
-      }
-      patternFile.close();
-
-      if (!checkTrafficPatternCorrectness()) {
-        m_logger.log("ERROR: Traffic configuration provided is not proper - " +
-                     m_configurationFile, false, true);
-        exit(2);
-      }
-
-      m_logger.log("Traffic configuration file processing completed.\n", true, false);
-      for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) {
-        m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, false);
-        m_trafficPatterns[patternId].printTrafficConfiguration(m_logger);
-        m_logger.log("", false, false);
-      }
-    }
-    else {
-      m_logger.log("ERROR: Unable to open traffic configuration file: " + m_configurationFile, false, true);
-      exit(2);
-    }
-  }
-
-  void
-  initializeTrafficConfiguration()
-  {
-    namespace fs = boost::filesystem;
-
-    fs::path configPath(m_configurationFile);
-    if (fs::exists(configPath)) {
-      if (fs::is_regular_file(configPath)) {
-        parseConfigurationFile();
-      }
-      else {
-        m_logger.log("ERROR: Traffic configuration file is not a regular file: " +
-                     m_configurationFile, false, true);
-        exit(2);
-      }
-    }
-    else {
-      m_logger.log("ERROR: Traffic configuration file does not exist: " +
-                   m_configurationFile, false, true);
-      exit(2);
-    }
-  }
-
   uint32_t
   getNewNonce()
   {
@@ -409,10 +345,7 @@
     }
     interest.setName(name);
 
-    if (pattern.m_mustBeFresh)
-      interest.setMustBeFresh(true);
-    else if (!pattern.m_mustBeFresh)
-      interest.setMustBeFresh(false);
+    interest.setMustBeFresh(pattern.m_mustBeFresh);
 
     static std::uniform_int_distribution<> duplicateNonceDist(1, 100);
     if (duplicateNonceDist(random::getRandomNumberEngine()) <= pattern.m_nonceDuplicationPercentage)
@@ -688,7 +621,5 @@
     client.setQuietLogging();
   }
 
-  client.run();
-
-  return client.hasError() ? 1 : 0;
+  return client.run();
 }
diff --git a/src/ndn-traffic-server.cpp b/src/ndn-traffic-server.cpp
index a6d8fef..25ace67 100644
--- a/src/ndn-traffic-server.cpp
+++ b/src/ndn-traffic-server.cpp
@@ -18,7 +18,6 @@
  * Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  */
 
-#include "logger.hpp"
 #include "util.hpp"
 
 #include <ndn-cxx/data.hpp>
@@ -28,7 +27,6 @@
 #include <ndn-cxx/util/backports.hpp>
 #include <ndn-cxx/util/random.hpp>
 
-#include <cctype>
 #include <limits>
 #include <sstream>
 #include <vector>
@@ -57,12 +55,6 @@
   {
   }
 
-  bool
-  hasError() const
-  {
-    return m_hasError;
-  }
-
   void
   setMaximumInterests(uint64_t maxInterests)
   {
@@ -82,15 +74,30 @@
     m_wantQuiet = true;
   }
 
-  void
+  int
   run()
   {
     m_logger.initializeLog(to_string(random::generateWord32()));
-    initializeTrafficConfiguration();
+
+    if (!readConfigurationFile(m_configurationFile, m_trafficPatterns, m_logger)) {
+      return 2;
+    }
+
+    if (!checkTrafficPatternCorrectness()) {
+      m_logger.log("ERROR: Traffic configuration provided is not proper", false, true);
+      return 2;
+    }
+
+    m_logger.log("Traffic configuration file processing completed.\n", true, false);
+    for (std::size_t i = 0; i < m_trafficPatterns.size(); i++) {
+      m_logger.log("Traffic Pattern Type #" + to_string(i + 1), false, false);
+      m_trafficPatterns[i].printTrafficConfiguration(m_logger);
+      m_logger.log("", false, false);
+    }
 
     if (m_nMaximumInterests == 0) {
       logStatistics();
-      return;
+      return 0;
     }
 
     m_signalSet.async_wait([this] (const boost::system::error_code&, int) {
@@ -110,11 +117,12 @@
 
     try {
       m_face.processEvents();
+      return m_hasError ? 1 : 0;
     }
     catch (const std::exception& e) {
       m_logger.log("ERROR: "s + e.what(), true, true);
-      m_hasError = true;
       m_ioService.stop();
+      return 1;
     }
   }
 
@@ -151,40 +159,39 @@
     }
 
     bool
-    processConfigurationDetail(const std::string& detail, Logger& logger, int lineNumber)
+    parseConfigurationLine(const std::string& line, Logger& logger, int lineNumber)
     {
       std::string parameter, value;
-      if (parseParameterAndValue(detail, parameter, value)) {
-        if (parameter == "Name") {
-          m_name = value;
-        }
-        else if (parameter == "ContentDelay") {
-          m_contentDelay = time::milliseconds(std::stoul(value));
-        }
-        else if (parameter == "FreshnessPeriod") {
-          m_freshnessPeriod = time::milliseconds(std::stoul(value));
-        }
-        else if (parameter == "ContentType") {
-          m_contentType = std::stoul(value);
-        }
-        else if (parameter == "ContentBytes") {
-          m_contentLength = std::stoul(value);
-        }
-        else if (parameter == "Content") {
-          m_content = value;
-        }
-        else if (parameter == "SigningInfo") {
-          m_signingInfo = security::SigningInfo(value);
-        }
-        else {
-          logger.log("Line " + to_string(lineNumber) +
-                     " - Unknown parameter '" + parameter + "'", false, true);
-        }
+      if (!extractParameterAndValue(line, parameter, value)) {
+        logger.log("Line " + to_string(lineNumber) + " - Invalid syntax: " + line,
+                   false, true);
+        return false;
+      }
+
+      if (parameter == "Name") {
+        m_name = value;
+      }
+      else if (parameter == "ContentDelay") {
+        m_contentDelay = time::milliseconds(std::stoul(value));
+      }
+      else if (parameter == "FreshnessPeriod") {
+        m_freshnessPeriod = time::milliseconds(std::stoul(value));
+      }
+      else if (parameter == "ContentType") {
+        m_contentType = std::stoul(value);
+      }
+      else if (parameter == "ContentBytes") {
+        m_contentLength = std::stoul(value);
+      }
+      else if (parameter == "Content") {
+        m_content = value;
+      }
+      else if (parameter == "SigningInfo") {
+        m_signingInfo = security::SigningInfo(value);
       }
       else {
-        logger.log("Line " + to_string(lineNumber) +
-                   " - Invalid traffic configuration line: " + detail, false, true);
-        return false;
+        logger.log("Line " + to_string(lineNumber) + " - Ignoring unknown parameter: " + parameter,
+                   false, true);
       }
       return true;
     }
@@ -226,88 +233,10 @@
   bool
   checkTrafficPatternCorrectness() const
   {
+    // TODO
     return true;
   }
 
-  void
-  parseConfigurationFile()
-  {
-    std::ifstream patternFile;
-    m_logger.log("Parsing traffic configuration file: " + m_configurationFile, true, true);
-
-    patternFile.open(m_configurationFile.data());
-    if (patternFile.is_open()) {
-      int lineNumber = 0;
-      std::string patternLine;
-      while (getline(patternFile, patternLine)) {
-        lineNumber++;
-        if (std::isalpha(patternLine[0])) {
-          DataTrafficConfiguration dataData;
-          bool shouldSkipLine = false;
-          if (dataData.processConfigurationDetail(patternLine, m_logger, lineNumber)) {
-            while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) {
-              lineNumber++;
-              if (!dataData.processConfigurationDetail(patternLine, m_logger, lineNumber)) {
-                shouldSkipLine = true;
-                break;
-              }
-            }
-            lineNumber++;
-          }
-          else {
-            shouldSkipLine = true;
-          }
-          if (!shouldSkipLine) {
-            if (dataData.checkTrafficDetailCorrectness()) {
-              m_trafficPatterns.push_back(dataData);
-            }
-          }
-        }
-      }
-      patternFile.close();
-
-      if (!checkTrafficPatternCorrectness()) {
-        m_logger.log("ERROR: Traffic configuration provided is not proper - " +
-                     m_configurationFile, false, true);
-        exit(2);
-      }
-
-      m_logger.log("Traffic configuration file processing completed.\n", true, false);
-      for (std::size_t patternId = 0; patternId < m_trafficPatterns.size(); patternId++) {
-        m_logger.log("Traffic Pattern Type #" + to_string(patternId + 1), false, false);
-        m_trafficPatterns[patternId].printTrafficConfiguration(m_logger);
-        m_logger.log("", false, false);
-      }
-    }
-    else {
-      m_logger.log("ERROR: Unable to open traffic configuration file: " + m_configurationFile, false, true);
-      exit(2);
-    }
-  }
-
-  void
-  initializeTrafficConfiguration()
-  {
-    namespace fs = boost::filesystem;
-
-    fs::path configPath(m_configurationFile);
-    if (fs::exists(configPath)) {
-      if (fs::is_regular_file(configPath)) {
-        parseConfigurationFile();
-      }
-      else {
-        m_logger.log("ERROR: Traffic configuration file is not a regular file: " +
-                     m_configurationFile, false, true);
-        exit(2);
-      }
-    }
-    else {
-      m_logger.log("ERROR: Traffic configuration file does not exist: " +
-                   m_configurationFile, false, true);
-      exit(2);
-    }
-  }
-
   static std::string
   getRandomByteString(std::size_t length)
   {
@@ -501,7 +430,5 @@
     server.setQuietLogging();
   }
 
-  server.run();
-
-  return server.hasError() ? 1 : 0;
+  return server.run();
 }
diff --git a/src/util.hpp b/src/util.hpp
index 65052d8..bafdc39 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -21,13 +21,17 @@
 #ifndef NTG_UTIL_HPP
 #define NTG_UTIL_HPP
 
+#include "logger.hpp"
+
 #include <cctype>
 #include <string>
 
+#include <boost/algorithm/string/predicate.hpp>
+
 namespace ndn {
 
 static inline bool
-parseParameterAndValue(const std::string& input, std::string& parameter, std::string& value)
+extractParameterAndValue(const std::string& input, std::string& parameter, std::string& value)
 {
   static const std::string allowedCharacters = ":/+._-%";
   parameter = "";
@@ -56,6 +60,65 @@
   return !parameter.empty() && !value.empty() && i == input.length();
 }
 
+static inline bool
+parseBoolean(const std::string& input)
+{
+  if (boost::iequals(input, "no") || boost::iequals(input, "off") ||
+      boost::iequals(input, "false") || input == "0")
+    return false;
+
+  if (boost::iequals(input, "yes") || boost::iequals(input, "on") ||
+      boost::iequals(input, "true") || input == "1")
+    return true;
+
+  throw std::invalid_argument("'" + input + "' is not a valid boolean value");
+}
+
+template<typename TrafficConfigurationType>
+static inline bool
+readConfigurationFile(const std::string& filename,
+                      std::vector<TrafficConfigurationType>& patterns,
+                      Logger& logger)
+{
+  std::ifstream patternFile(filename);
+  if (!patternFile) {
+    logger.log("ERROR: Unable to open traffic configuration file: " + filename, false, true);
+    return false;
+  }
+
+  logger.log("Reading traffic configuration file: " + filename, true, true);
+
+  int lineNumber = 0;
+  std::string patternLine;
+  while (getline(patternFile, patternLine)) {
+    lineNumber++;
+    if (std::isalpha(patternLine[0])) {
+      TrafficConfigurationType trafficConf;
+      bool shouldSkipLine = false;
+      if (trafficConf.parseConfigurationLine(patternLine, logger, lineNumber)) {
+        while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) {
+          lineNumber++;
+          if (!trafficConf.parseConfigurationLine(patternLine, logger, lineNumber)) {
+            shouldSkipLine = true;
+            break;
+          }
+        }
+        lineNumber++;
+      }
+      else {
+        shouldSkipLine = true;
+      }
+      if (!shouldSkipLine) {
+        if (trafficConf.checkTrafficDetailCorrectness()) {
+          patterns.push_back(std::move(trafficConf));
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
 } // namespace ndn
 
 #endif // NTG_UTIL_HPP