base: ensure Element::Error inherits from tlv::Error

Adding static asserts to ensure (Name|name::Component|Interest|
Selectors|Exclude|KeyLocator|Data|MetaInfo|Signature|SignatureInfo)
::Error are subclasses of tlv::Error.

This commit also adds WireEncodable and WireDecodable concept checks
to Name,name::Component,Interest,Selectors,Exclude,KeyLocator,Data,
MetaInfo,SignatureInfo types.

This commit also moves definition of Selectors method into .cpp.

refs #1983

Change-Id: I15220b93437d4a624ae09df66defc91ceac2386c
diff --git a/src/selectors.cpp b/src/selectors.cpp
new file mode 100644
index 0000000..bfb4df4
--- /dev/null
+++ b/src/selectors.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 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/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "selectors.hpp"
+#include "encoding/encoding-buffer.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Selectors>));
+BOOST_CONCEPT_ASSERT((WireEncodable<Selectors>));
+BOOST_CONCEPT_ASSERT((WireDecodable<Selectors>));
+static_assert(std::is_base_of<tlv::Error, Selectors::Error>::value,
+              "Selectors::Error must inherit from tlv::Error");
+
+Selectors::Selectors()
+  : m_minSuffixComponents(-1)
+  , m_maxSuffixComponents(-1)
+  , m_childSelector(-1)
+  , m_mustBeFresh(false)
+{
+}
+
+Selectors::Selectors(int minSuffixComponents, int maxSuffixComponents,
+                     const Exclude& exclude,
+                     int childSelector,
+                     bool mustBeFresh)
+  : m_minSuffixComponents(minSuffixComponents)
+  , m_maxSuffixComponents(maxSuffixComponents)
+  , m_exclude(exclude)
+  , m_childSelector(childSelector)
+  , m_mustBeFresh(mustBeFresh)
+{
+}
+
+Selectors::Selectors(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+bool
+Selectors::empty() const
+{
+  return m_minSuffixComponents < 0 &&
+         m_maxSuffixComponents < 0 &&
+         m_publisherPublicKeyLocator.empty() &&
+         m_exclude.empty() &&
+         m_childSelector < 0 &&
+         !m_mustBeFresh;
+}
+
+template<bool T>
+size_t
+Selectors::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  // Selectors ::= SELECTORS-TYPE TLV-LENGTH
+  //                 MinSuffixComponents?
+  //                 MaxSuffixComponents?
+  //                 PublisherPublicKeyLocator?
+  //                 Exclude?
+  //                 ChildSelector?
+  //                 MustBeFresh?
+
+  // (reverse encoding)
+
+  // MustBeFresh
+  if (getMustBeFresh()) {
+    totalLength += prependBooleanBlock(block, tlv::MustBeFresh);
+  }
+
+  // ChildSelector
+  if (getChildSelector() >= 0) {
+    totalLength += prependNonNegativeIntegerBlock(block, tlv::ChildSelector, getChildSelector());
+  }
+
+  // Exclude
+  if (!getExclude().empty()) {
+    totalLength += getExclude().wireEncode(block);
+  }
+
+  // PublisherPublicKeyLocator
+  if (!getPublisherPublicKeyLocator().empty()) {
+    totalLength += getPublisherPublicKeyLocator().wireEncode(block);
+  }
+
+  // MaxSuffixComponents
+  if (getMaxSuffixComponents() >= 0) {
+    totalLength += prependNonNegativeIntegerBlock(block, tlv::MaxSuffixComponents,
+                                                  getMaxSuffixComponents());
+  }
+
+  // MinSuffixComponents
+  if (getMinSuffixComponents() >= 0) {
+    totalLength += prependNonNegativeIntegerBlock(block, tlv::MinSuffixComponents,
+                                                  getMinSuffixComponents());
+  }
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::Selectors);
+  return totalLength;
+}
+
+template size_t
+Selectors::wireEncode<true>(EncodingImpl<true>& estimator) const;
+
+template size_t
+Selectors::wireEncode<false>(EncodingImpl<false>& encoder) const;
+
+const Block&
+Selectors::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+Selectors::wireDecode(const Block& wire)
+{
+  if (wire.type() != tlv::Selectors)
+    throw tlv::Error("Unexpected TLV type when decoding Selectors");
+
+  *this = Selectors();
+
+  m_wire = wire;
+  m_wire.parse();
+
+  // MinSuffixComponents
+  Block::element_const_iterator val = m_wire.find(tlv::MinSuffixComponents);
+  if (val != m_wire.elements_end()) {
+    m_minSuffixComponents = readNonNegativeInteger(*val);
+  }
+
+  // MaxSuffixComponents
+  val = m_wire.find(tlv::MaxSuffixComponents);
+  if (val != m_wire.elements_end()) {
+    m_maxSuffixComponents = readNonNegativeInteger(*val);
+  }
+
+  // PublisherPublicKeyLocator
+  val = m_wire.find(tlv::KeyLocator);
+  if (val != m_wire.elements_end()) {
+    m_publisherPublicKeyLocator.wireDecode(*val);
+  }
+
+  // Exclude
+  val = m_wire.find(tlv::Exclude);
+  if (val != m_wire.elements_end()) {
+    m_exclude.wireDecode(*val);
+  }
+
+  // ChildSelector
+  val = m_wire.find(tlv::ChildSelector);
+  if (val != m_wire.elements_end()) {
+    m_childSelector = readNonNegativeInteger(*val);
+  }
+
+  // MustBeFresh
+  val = m_wire.find(tlv::MustBeFresh);
+  if (val != m_wire.elements_end()) {
+    m_mustBeFresh = true;
+  }
+}
+
+Selectors&
+Selectors::setMinSuffixComponents(int minSuffixComponents)
+{
+  m_minSuffixComponents = minSuffixComponents;
+  m_wire.reset();
+  return *this;
+}
+
+Selectors&
+Selectors::setMaxSuffixComponents(int maxSuffixComponents)
+{
+  m_maxSuffixComponents = maxSuffixComponents;
+  m_wire.reset();
+  return *this;
+}
+
+Selectors&
+Selectors::setPublisherPublicKeyLocator(const KeyLocator& keyLocator)
+{
+  m_publisherPublicKeyLocator = keyLocator;
+  m_wire.reset();
+  return *this;
+}
+
+Selectors&
+Selectors::setExclude(const Exclude& exclude)
+{
+  m_exclude = exclude;
+  m_wire.reset();
+  return *this;
+}
+
+Selectors&
+Selectors::setChildSelector(int childSelector)
+{
+  m_childSelector = childSelector;
+  m_wire.reset();
+  return *this;
+}
+
+Selectors&
+Selectors::setMustBeFresh(bool mustBeFresh)
+{
+  m_mustBeFresh = mustBeFresh;
+  m_wire.reset();
+  return *this;
+}
+
+bool
+Selectors::operator==(const Selectors& other) const
+{
+  return wireEncode() == other.wireEncode();
+}
+
+} // namespace ndn