Add API to get the keyword name component for metadata and prefix announcements

Change-Id: I612afef79814cad8ada502aa820c4b8f2732daea
diff --git a/ndn-cxx/metadata-object.cpp b/ndn-cxx/metadata-object.cpp
index 0c8ca69..7f9a16d 100644
--- a/ndn-cxx/metadata-object.cpp
+++ b/ndn-cxx/metadata-object.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -16,6 +16,8 @@
  * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
  * <http://www.gnu.org/licenses/>.
  *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
  * @author Chavoosh Ghasemi <chghasemi@cs.arizona.edu>
  */
 
@@ -26,8 +28,6 @@
 static_assert(std::is_base_of<tlv::Error, MetadataObject::Error>::value,
               "MetadataObject::Error must inherit from tlv::Error");
 
-const name::Component KEYWORD_METADATA_COMP = "20 08 6D65746164617461"_block; // 32=metadata
-
 MetadataObject::MetadataObject() = default;
 
 MetadataObject::MetadataObject(const Data& data)
@@ -56,7 +56,7 @@
                          optional<uint64_t> version,
                          time::milliseconds freshnessPeriod) const
 {
-  if (discoveryInterestName.empty() || discoveryInterestName[-1] != KEYWORD_METADATA_COMP) {
+  if (discoveryInterestName.empty() || discoveryInterestName[-1] != getKeywordComponent()) {
     NDN_THROW(Error("Name " + discoveryInterestName.toUri() +
                     " is not a valid discovery Interest name"));
   }
@@ -78,17 +78,24 @@
   return *this;
 }
 
+const name::Component&
+MetadataObject::getKeywordComponent()
+{
+  static const name::Component nc(tlv::KeywordNameComponent, {'m', 'e', 't', 'a', 'd', 'a', 't', 'a'});
+  return nc;
+}
+
 bool
 MetadataObject::isValidName(const Name& name)
 {
-  return name.size() >= 3 && name[-3] == KEYWORD_METADATA_COMP &&
+  return name.size() >= 3 && name[-3] == getKeywordComponent() &&
          name[-2].isVersion() && name[-1].isSegment();
 }
 
 Interest
 MetadataObject::makeDiscoveryInterest(Name name)
 {
-  return Interest(name.append(KEYWORD_METADATA_COMP))
+  return Interest(name.append(getKeywordComponent()))
          .setCanBePrefix(true)
          .setMustBeFresh(true);
 }
diff --git a/ndn-cxx/metadata-object.hpp b/ndn-cxx/metadata-object.hpp
index 2e23f80..534b1fa 100644
--- a/ndn-cxx/metadata-object.hpp
+++ b/ndn-cxx/metadata-object.hpp
@@ -107,6 +107,12 @@
 
 public: // static methods
   /**
+   * @brief Returns the well-known keyword name component used for metadata objects (`32=metadata`)
+   */
+  static const name::Component&
+  getKeywordComponent();
+
+  /**
    * @brief Check whether @p name can be a valid metadata name
    */
   static bool
diff --git a/ndn-cxx/prefix-announcement.cpp b/ndn-cxx/prefix-announcement.cpp
index 3d706d3..6e315d3 100644
--- a/ndn-cxx/prefix-announcement.cpp
+++ b/ndn-cxx/prefix-announcement.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,7 +24,8 @@
 
 namespace ndn {
 
-const name::Component KEYWORD_PA_COMP = "20 02 5041"_block; // 32=PA
+static_assert(std::is_base_of<tlv::Error, PrefixAnnouncement::Error>::value,
+              "PrefixAnnouncement::Error must inherit from tlv::Error");
 
 PrefixAnnouncement::PrefixAnnouncement() = default;
 
@@ -32,7 +33,7 @@
   : m_data(std::move(data))
 {
   const Name& dataName = m_data->getName();
-  if (dataName.size() < 3 || dataName[-3] != KEYWORD_PA_COMP ||
+  if (dataName.size() < 3 || dataName[-3] != getKeywordComponent() ||
       !dataName[-2].isVersion() || !dataName[-1].isSegment()) {
     NDN_THROW(Error("Data is not a prefix announcement: wrong name structure"));
   }
@@ -71,9 +72,9 @@
 {
   if (!m_data) {
     Name dataName = m_announcedName;
-    dataName.append(KEYWORD_PA_COMP);
-    dataName.appendVersion(version.value_or(time::toUnixTimestamp(time::system_clock::now()).count()));
-    dataName.appendSegment(0);
+    dataName.append(getKeywordComponent())
+            .appendVersion(version.value_or(time::toUnixTimestamp(time::system_clock::now()).count()))
+            .appendSegment(0);
     m_data.emplace(dataName);
     m_data->setContentType(tlv::ContentType_PrefixAnn);
 
@@ -118,6 +119,13 @@
   return *this;
 }
 
+const name::Component&
+PrefixAnnouncement::getKeywordComponent()
+{
+  static const name::Component nc(tlv::KeywordNameComponent, {'P', 'A'});
+  return nc;
+}
+
 bool
 operator==(const PrefixAnnouncement& lhs, const PrefixAnnouncement& rhs)
 {
diff --git a/ndn-cxx/prefix-announcement.hpp b/ndn-cxx/prefix-announcement.hpp
index 387174c..ed9ea0d 100644
--- a/ndn-cxx/prefix-announcement.hpp
+++ b/ndn-cxx/prefix-announcement.hpp
@@ -119,6 +119,13 @@
   PrefixAnnouncement&
   setValidityPeriod(optional<security::ValidityPeriod> validity);
 
+public: // static methods
+  /**
+   * @brief Returns the well-known keyword name component used for prefix announcements (`32=PA`)
+   */
+  static const name::Component&
+  getKeywordComponent();
+
 private:
   mutable optional<Data> m_data;
   Name m_announcedName;
diff --git a/tests/unit/metadata-object.t.cpp b/tests/unit/metadata-object.t.cpp
index 7aae5cf..9606db7 100644
--- a/tests/unit/metadata-object.t.cpp
+++ b/tests/unit/metadata-object.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -31,19 +31,16 @@
 {
 public:
   MetadataObjectFixture()
-    : metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"))
-    , versionedContentName(Name(baseContentName)
+    : versionedContentName(Name(baseContentName)
                            .appendVersion(342092199154ULL))
     , metadataFullName(Name(baseContentName)
-                       .append(metadataComponent)
+                       .append(MetadataObject::getKeywordComponent())
                        .appendVersion(metadataVerNo)
                        .appendSegment(0))
   {
   }
 
 protected:
-  const name::Component metadataComponent;
-
   // content prefix
   const Name baseContentName = "/ndn/unit/tests";
   const Name versionedContentName;
@@ -90,13 +87,16 @@
   BOOST_CHECK_EXCEPTION(MetadataObject{data}, tlv::Error, [] (const auto& e) {
     return e.what() == "Name /ndn/unit/test is not a valid MetadataObject name"s;
   });
-  data.setName(Name("/ndn/unit/test").append(metadataComponent));
+  data.setName(Name("/ndn/unit/test").append(MetadataObject::getKeywordComponent()));
   BOOST_CHECK_EXCEPTION(MetadataObject{data}, tlv::Error, [] (const auto& e) {
     return e.what() == "Name /ndn/unit/test/32=metadata is not a valid MetadataObject name"s;
   });
 
   // invalid content type
-  data.setName(Name("/ndn/unit/test").append(metadataComponent).appendVersion().appendSegment(0));
+  data.setName(Name("/ndn/unit/test")
+               .append(MetadataObject::getKeywordComponent())
+               .appendVersion()
+               .appendSegment(0));
   data.setContentType(tlv::ContentType_Key);
   BOOST_CHECK_EXCEPTION(MetadataObject{data}, tlv::Error, [] (const auto& e) {
     return e.what() == "MetadataObject has invalid ContentType 2"s;
@@ -115,11 +115,19 @@
   });
 }
 
+BOOST_AUTO_TEST_CASE(KeywordComponent)
+{
+  BOOST_CHECK_EQUAL(MetadataObject::getKeywordComponent().wireEncode(),
+                    "20 08 6D65746164617461"_block);
+  BOOST_CHECK_EQUAL(MetadataObject::getKeywordComponent().toUri(name::UriFormat::CANONICAL),
+                    "32=metadata");
+}
+
 BOOST_AUTO_TEST_CASE(IsValidName)
 {
   // valid name
   Name name = Name("/ndn/unit/test")
-              .append(metadataComponent)
+              .append(MetadataObject::getKeywordComponent())
               .appendVersion()
               .appendSegment(0);
   BOOST_CHECK(MetadataObject::isValidName(name));
@@ -131,7 +139,7 @@
   // version component is missing
   BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-2)), false);
 
-  // keyword name component `32=keyword` is missing
+  // keyword component is missing
   BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-3)), false);
 
   // too short name
@@ -141,9 +149,9 @@
   name = name.getPrefix(-2).appendSegment(0).appendVersion();
   BOOST_CHECK_EQUAL(MetadataObject::isValidName(name), false);
 
-  // invalid name component keyword
+  // invalid keyword name component
   name = name.getPrefix(-3)
-         .append(32, reinterpret_cast<const uint8_t*>("foo"), std::strlen("foo"))
+         .append(tlv::KeywordNameComponent, reinterpret_cast<const uint8_t*>("foo"), std::strlen("foo"))
          .appendVersion()
          .appendSegment(0);
   BOOST_CHECK_EQUAL(MetadataObject::isValidName(name), false);
@@ -152,7 +160,7 @@
 BOOST_AUTO_TEST_CASE(MakeDiscoveryInterest)
 {
   Interest interest = MetadataObject::makeDiscoveryInterest(baseContentName);
-  BOOST_CHECK_EQUAL(interest.getName(), Name(baseContentName).append(metadataComponent));
+  BOOST_CHECK_EQUAL(interest.getName(), Name(baseContentName).append(MetadataObject::getKeywordComponent()));
   BOOST_CHECK(interest.getCanBePrefix());
   BOOST_CHECK(interest.getMustBeFresh());
 }
diff --git a/tests/unit/prefix-announcement.t.cpp b/tests/unit/prefix-announcement.t.cpp
index f6cad22..9a3eba9 100644
--- a/tests/unit/prefix-announcement.t.cpp
+++ b/tests/unit/prefix-announcement.t.cpp
@@ -255,6 +255,14 @@
   BOOST_CHECK_NE(pa2, pa);
 }
 
+BOOST_AUTO_TEST_CASE(KeywordComponent)
+{
+  BOOST_CHECK_EQUAL(PrefixAnnouncement::getKeywordComponent().wireEncode(),
+                    "20 02 5041"_block);
+  BOOST_CHECK_EQUAL(PrefixAnnouncement::getKeywordComponent().toUri(name::UriFormat::CANONICAL),
+                    "32=PA");
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestPrefixAnnouncement
 
 } // namespace tests