face: Implementing InterestFilter abstraction to be used with setInterestFilter

This commit minimally changes the API, primarily altering the internal
structures preparing for separation of `registerPrefix` (=send a command
to local forwarder to register FIB/RIB entry) and `setInterestFilter`
(=update library's InterestFilter->Callback dispatch table).

The existing setInterestFilter methods preserve all previous functionality
(any string URI or ndn::Name can be supplied as a first parameter),
but also allow InterestFilter as the filtering parameter.
InterestFilter, provides a way to select Interest either based on prefix,
as before, or based on prefix and regular expression.

Change-Id: Id71404f2163f82c261018d21db172111c4b0da69
Refs: #1275
diff --git a/src/interest-filter.hpp b/src/interest-filter.hpp
new file mode 100644
index 0000000..4cb77af
--- /dev/null
+++ b/src/interest-filter.hpp
@@ -0,0 +1,181 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (c) 2013-2014,  Regents of the University of California.
+ * All rights reserved.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * This file licensed under New BSD License.  See COPYING for detailed information about
+ * ndn-cxx library copyright, permissions, and redistribution restrictions.
+ */
+
+#ifndef NDN_INTEREST_FILTER_HPP
+#define NDN_INTEREST_FILTER_HPP
+
+#include "util/regex/regex-pattern-list-matcher.hpp"
+#include "name.hpp"
+
+namespace ndn {
+
+class InterestFilter
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Create an Interest filter to match Interests by prefix
+   *
+   * This filter will match all Interests, whose name start with the given prefix
+   *
+   * Any Name can be implicitly converted to the InterestFilter.
+   */
+  InterestFilter(const Name& prefix);
+
+  /**
+   * @brief Create an Interest filter to match Interests by prefix URI
+   *
+   * This filter will match all Interests, whose name start with the given prefix
+   *
+   * Any const char* can be implicitly converted to the InterestFilter.
+   */
+  InterestFilter(const char* prefixUri);
+
+  /**
+   * @brief Create an Interest filter to match Interests by prefix URI
+   *
+   * This filter will match all Interests, whose name start with the given prefix
+   *
+   * Any std::string can be implicitly converted to the InterestFilter.
+   */
+  InterestFilter(const std::string& prefixUri);
+
+  /**
+   * @brief Create an Interest filter to match Interest by prefix and regular expression
+   *
+   * This filter will match all Interests, whose name start with the given prefix and
+   * other components of the Interest name match the given regular expression.
+   * For example, the following InterestFilter:
+   *
+   *    InterestFilter("/hello", "<world><>+")
+   *
+   * will match all Interests, whose name has prefix `/hello`, which is followed by
+   * component `world` and has at least one more component after it.  Examples:
+   *
+   *    /hello/world/!
+   *    /hello/world/x/y/z
+   *
+   * Note that regular expression will need to match all components (e.g., there is
+   * an implicit heading `^` and trailing `$` symbols in the regular expression).
+   */
+  InterestFilter(const Name& prefix, const std::string& regexFilter);
+
+  /**
+   * @brief Implicit conversion to Name (to provide backwards compatibility for onInterest callback)
+   */
+  operator const Name&() const
+  {
+    if (static_cast<bool>(m_regexFilter)) {
+      throw Error("Please update OnInterest callback to accept const InterestFilter& "
+                  "(non-trivial Interest filter is being used)");
+    }
+    return m_prefix;
+  }
+
+  /**
+   * @brief Check if specified name matches the filter
+   */
+  bool
+  doesMatch(const Name& name) const;
+
+  const Name&
+  getPrefix() const
+  {
+    return m_prefix;
+  }
+
+  bool
+  hasRegexFilter() const
+  {
+    return static_cast<bool>(m_regexFilter);
+  }
+
+  const RegexPatternListMatcher&
+  getRegexFilter() const
+  {
+    return *m_regexFilter;
+  }
+
+private:
+  Name m_prefix;
+  shared_ptr<RegexPatternListMatcher> m_regexFilter;
+};
+
+inline
+InterestFilter::InterestFilter(const Name& prefix)
+  : m_prefix(prefix)
+{
+}
+
+inline
+InterestFilter::InterestFilter(const char* prefixUri)
+  : m_prefix(prefixUri)
+{
+}
+
+inline
+InterestFilter::InterestFilter(const std::string& prefixUri)
+  : m_prefix(prefixUri)
+{
+}
+
+inline
+InterestFilter::InterestFilter(const Name& prefix, const std::string& regexFilter)
+  : m_prefix(prefix)
+  , m_regexFilter(new RegexPatternListMatcher(regexFilter, shared_ptr<RegexBackrefManager>()))
+{
+}
+
+inline bool
+InterestFilter::doesMatch(const Name& name) const
+{
+  if (name.size() < m_prefix.size())
+    return false;
+
+  if (hasRegexFilter()) {
+    // perform prefix match and regular expression match for the remaining components
+    bool isMatch = m_prefix.isPrefixOf(name);
+
+    if (!isMatch)
+      return false;
+
+    return m_regexFilter->match(name, m_prefix.size(), name.size() - m_prefix.size());
+  }
+  else {
+    // perform just prefix match
+
+    return m_prefix.isPrefixOf(name);
+  }
+}
+
+inline std::ostream&
+operator<<(std::ostream& os, const InterestFilter& filter)
+{
+  os << filter.getPrefix();
+  if (filter.hasRegexFilter()) {
+    os << "?regex=" << filter.getRegexFilter();
+  }
+  return os;
+}
+
+} // namespace ndn
+
+#endif // NDN_INTEREST_FILTER_HPP