Add MetadataObject class to encode/decode RDR-style metadata

Change-Id: I6688b630a4889614e7df5c1f91b91be370a350cd
Refs: #4707
diff --git a/ndn-cxx/metadata-object.cpp b/ndn-cxx/metadata-object.cpp
new file mode 100644
index 0000000..78ad7f4
--- /dev/null
+++ b/ndn-cxx/metadata-object.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2019 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * @author Chavoosh Ghasemi <chghasemi@cs.arizona.edu>
+ */
+
+#include "ndn-cxx/metadata-object.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+
+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)
+{
+  if (data.getContentType() != tlv::ContentType_Blob) {
+    BOOST_THROW_EXCEPTION(Error("Expected ContentType to be BLOB but " +
+                                boost::lexical_cast<std::string>(data.getContentType()) +
+                                " is provided"));
+  }
+
+  if (!isValidName(data.getName())) {
+    BOOST_THROW_EXCEPTION(Error("Name " + data.getName().toUri() +
+                                " is not a valid MetadataObject name"));
+  }
+
+  data.getContent().parse();
+  // ignore non-Name elements before the first one
+  m_versionedName.wireDecode(data.getContent().get(tlv::Name));
+}
+
+Data
+MetadataObject::makeData(Name discoveryInterestName,
+                         KeyChain& keyChain,
+                         const ndn::security::SigningInfo& si,
+                         optional<uint64_t> version,
+                         time::milliseconds freshnessPeriod) const
+{
+  if (discoveryInterestName.empty() || discoveryInterestName[-1] != KEYWORD_METADATA_COMP) {
+    BOOST_THROW_EXCEPTION(Error("Name " + discoveryInterestName.toUri() +
+                                " is not a valid discovery Interest name"));
+  }
+  discoveryInterestName.appendVersion(version);
+  discoveryInterestName.appendSegment(0);
+
+  Data data(discoveryInterestName);
+  data.setContent(m_versionedName.wireEncode());
+  data.setFreshnessPeriod(freshnessPeriod);
+  keyChain.sign(data, si);
+
+  return data;
+}
+
+MetadataObject&
+MetadataObject::setVersionedName(const Name& name)
+{
+  m_versionedName = name;
+  return *this;
+}
+
+bool
+MetadataObject::isValidName(const Name& name)
+{
+  return name.size() >= 3 && name[-3] == KEYWORD_METADATA_COMP &&
+         name[-2].isVersion() && name[-1].isSegment();
+}
+
+Interest
+MetadataObject::makeDiscoveryInterest(Name name)
+{
+  return Interest(name.append(KEYWORD_METADATA_COMP))
+         .setCanBePrefix(true)
+         .setMustBeFresh(true);
+}
+
+} // namespace ndn