add Name and Selector classes
diff --git a/include/ccnx-common.h b/include/ccnx-common.h
index 0636a62..06e7700 100644
--- a/include/ccnx-common.h
+++ b/include/ccnx-common.h
@@ -1,6 +1,14 @@
 #ifndef CCNX_COMMON_H
 #define CCNX_COMMON_H
 
+extern "C" {
+#include <ccn/ccn.h>
+#include <ccn/charbuf.h>
+#include <ccn/keystore.h>
+#include <ccn/uri.h>
+#include <ccn/bloom.h>
+#include <ccn/signing.h>
+}
 #include <vector>
 #include <boost/shared_ptr.hpp>
 #include <boost/exception/all.hpp>
@@ -24,10 +32,8 @@
 
 // --- Bytes operations end ---
 
-// --- Name operation start ---
-void
-split(const string &name, Comps &comps);
-// --- Name operation end ---
+// Exceptions
+typedef boost::error_info<struct tag_errmsg, std::string> error_info_str;
 
 } // Ccnx
 #endif // CCNX_COMMON_H
diff --git a/include/ccnx-interest.h b/include/ccnx-interest.h
deleted file mode 100644
index fd1db18..0000000
--- a/include/ccnx-interest.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef CCNX_INTEREST_H
-#define CCNX_INTEREST_H
-#include "ccnx-common.h"
-
-namespace Ccnx {
-
-// Currently, other classes use string when Interest is needed.
-// The constructor from string ensures that if we change other classes' APIs to use Interest object 
-// instead of string, it is still backwards compatible
-// Using a separate Interest class instead of simple string allows us to add control fields
-// to the Interest, e.g. ChildSelector. Perhaps that's a separate Selectors class. Will do it after ChronoShare project finishes.
-
-// Since the selector is only useful when sending Interest (in callbacks, usually we only need to know the name of the Interest),
-// we currently only use Interest object in sendInterest of CcnxWrapper. In other places, Interest object is equivalent of
-// its string name.
-
-class Interest
-{
-public:
-  Interest(const string &name) : m_name(name) {}
-  virtual ~Interest() {}
-
-  string
-  name() const { return m_name; }
-
-protected:
-  string m_name;
-};
-
-} // Ccnx
-#endif
diff --git a/include/ccnx-name.h b/include/ccnx-name.h
new file mode 100644
index 0000000..84c045f
--- /dev/null
+++ b/include/ccnx-name.h
@@ -0,0 +1,134 @@
+#ifndef CCNX_NAME_H
+#define CCNX_NAME_H
+#include <boost/shared_ptr.hpp>
+#include "ccnx-common.h"
+
+namespace Ccnx {
+
+//  This class is mostly used in CcnxWrapper; users should not be directly using this class
+// The main purpose of this class to is avoid manually create and destroy charbuf everytime
+class CcnxCharbuf
+{
+public:
+  CcnxCharbuf();
+  CcnxCharbuf(ccn_charbuf *buf);
+  ~CcnxCharbuf();
+
+  // expose internal data structure, use with caution!!
+  ccn_charbuf *
+  getBuf() { return m_buf; }
+
+protected:
+  ccn_charbuf *m_buf;
+};
+
+typedef boost::shared_ptr<CcnxCharbuf> CcnxCharbufPtr;
+
+struct NameException:
+  virtual boost::exception, virtual exception {};
+class Name
+{
+public:
+  Name();
+  Name(const string &name);
+  Name(const vector<Bytes> &comps);
+  Name(const Name &other);
+  Name(const unsigned char *data, const ccn_indexbuf *comps);
+  virtual ~Name() {}
+
+  CcnxCharbufPtr
+  toCcnxCharbuf() const;
+
+  Name &
+  appendComp(const Bytes &comp);
+
+  Name &
+  appendComp(const string &compStr);
+
+  int
+  size() const {return m_comps.size();}
+
+  Bytes
+  getComp(int index) const;
+
+  // return string format of the comp
+  // if all characters are printable, simply returns the string
+  // if not, print the bytes in hex string format
+  string
+  getCompAsString(int index) const;
+
+  Name
+  getPartialName(int start, int n) const;
+
+
+protected:
+  vector<Bytes> m_comps;
+};
+
+ostream&
+operator <<(ostream &os, const Name &name);
+
+struct InterestSelectorException:
+  virtual boost::exception, virtual exception {};
+
+class Selectors
+{
+public:
+  Selectors();
+  Selectors(const Selectors &other);
+  ~Selectors(){};
+
+  typedef enum
+  {
+    AOK_CS = 0x1,
+    AOK_NEW = 0x2,
+    AOK_DEFAULT = 0x3, // (AOK_CS | AOK_NEW)
+    AOK_STALE = 0x4,
+    AOK_EXPIRE = 0x10
+  } AOK;
+
+  typedef enum
+  {
+    LEFT = 0,
+    RIGHT = 1,
+    DEFAULT = 2
+  } CHILD_SELECTOR;
+
+  inline Selectors &
+  maxSuffixComps(int maxSCs) {m_maxSuffixComps = maxSCs; return *this;}
+
+  inline Selectors &
+  minSuffixComps(int minSCs) {m_minSuffixComps = minSCs; return *this;}
+
+  inline Selectors &
+  answerOriginKind(AOK kind) {m_answerOriginKind = kind; return *this;}
+
+  inline Selectors &
+  interestLifetime(int lifetime) {m_interestLifetime = lifetime; return *this;}
+
+  inline Selectors &
+  scope(int scope) {m_scope = scope; return *this;}
+
+  inline Selectors &
+  childSelector(CHILD_SELECTOR child) {m_childSelector = child; return *this;}
+
+  // this has no effect now
+  inline Selectors &
+  publisherPublicKeyDigest(const Bytes &digest) {m_publisherPublicKeyDigest = digest; return *this;}
+
+  CcnxCharbufPtr
+  toCcnxCharbuf();
+
+private:
+  int m_maxSuffixComps;
+  int m_minSuffixComps;
+  AOK m_answerOriginKind;
+  double m_interestLifetime;
+  int m_scope;
+  CHILD_SELECTOR m_childSelector;
+  // not used now
+  Bytes m_publisherPublicKeyDigest;
+};
+
+} // Ccnx
+#endif
diff --git a/include/ccnx-tunnel.h b/include/ccnx-tunnel.h
index dff2f50..15d5223 100644
--- a/include/ccnx-tunnel.h
+++ b/include/ccnx-tunnel.h
@@ -39,7 +39,7 @@
   publishContentObject(const string &name, const Bytes &contentObject, int freshness);
 
   virtual int
-  sendInterest (const Interest &interest, Closure *closure);
+  sendInterest (const string &interest, Closure *closure);
 
 
   // prefix is topology-independent
diff --git a/include/ccnx-wrapper.h b/include/ccnx-wrapper.h
index 9b50ae8..90089e7 100644
--- a/include/ccnx-wrapper.h
+++ b/include/ccnx-wrapper.h
@@ -1,21 +1,12 @@
 #ifndef CCNX_WRAPPER_H
 #define CCNX_WRAPPER_H
 
-extern "C" {
-#include <ccn/ccn.h>
-#include <ccn/charbuf.h>
-#include <ccn/keystore.h>
-#include <ccn/uri.h>
-#include <ccn/bloom.h>
-#include <ccn/signing.h>
-}
-
 #include <boost/thread/locks.hpp>
 #include <boost/thread/recursive_mutex.hpp>
 #include <boost/thread/thread.hpp>
 
 #include "ccnx-common.h"
-#include "ccnx-interest.h"
+#include "ccnx-name.h"
 #include "ccnx-closure.h"
 
 using namespace std;
@@ -46,7 +37,7 @@
   clearInterestFilter (const string &prefix);
 
   virtual int
-  sendInterest (const Interest &interest, Closure *closure);
+  sendInterest (const string &interest, Closure *closure);
 
   virtual int
   publishData (const string &name, const unsigned char *buf, size_t len, int freshness);
diff --git a/src/ccnx-name.cpp b/src/ccnx-name.cpp
new file mode 100644
index 0000000..bc7b9bd
--- /dev/null
+++ b/src/ccnx-name.cpp
@@ -0,0 +1,253 @@
+#include "ccnx-name.h"
+#include <boost/lexical_cast.hpp>
+#include <ctype.h>
+#include <boost/algorithm/string/join.hpp>
+
+namespace Ccnx{
+
+CcnxCharbuf::CcnxCharbuf()
+            : m_buf(NULL)
+{
+  m_buf = ccn_charbuf_create();
+}
+
+CcnxCharbuf::CcnxCharbuf(ccn_charbuf *buf)
+            : m_buf(NULL)
+{
+  if (buf != NULL)
+  {
+    m_buf = ccn_charbuf_create();
+    ccn_charbuf_reserve(m_buf, buf->length);
+    memcpy(m_buf->buf, buf->buf, buf->length);
+    m_buf->length = buf->length;
+  }
+}
+
+CcnxCharbuf::~CcnxCharbuf()
+{
+  ccn_charbuf_destroy(&m_buf);
+}
+
+Name::Name()
+{
+}
+
+Name::Name(const string &name)
+{
+  stringstream ss(name);
+  string compStr;
+  while(getline(ss, compStr, '/'))
+  {
+    Bytes comp(compStr.begin(), compStr.end());
+    m_comps.push_back(comp);
+  }
+}
+
+Name::Name(const vector<Bytes> &comps)
+{
+  m_comps = comps;
+}
+
+Name::Name(const Name &other)
+{
+  m_comps = other.m_comps;
+}
+
+Name::Name(const unsigned char *data, const ccn_indexbuf *comps)
+{
+  for (int i = 0; i < comps->n - 1; i++)
+  {
+    const unsigned char *compPtr;
+    size_t size;
+    ccn_name_comp_get(data, comps, i, &compPtr, &size);
+    Bytes comp;
+    readRaw(comp, compPtr, size);
+    m_comps.push_back(comp);
+  }
+}
+
+CcnxCharbufPtr
+Name::toCcnxCharbuf() const
+{
+  CcnxCharbufPtr ptr(new CcnxCharbuf());
+  ccn_charbuf *cbuf = ptr->getBuf();
+  ccn_name_init(cbuf);
+  int size = m_comps.size();
+  for (int i = 0; i < size; i++)
+  {
+    ccn_name_append(cbuf, head(m_comps[i]), m_comps[i].size());
+  }
+  return ptr;
+}
+
+Name &
+Name::appendComp(const Bytes &comp)
+{
+  m_comps.push_back(comp);
+  return *this;
+}
+
+Name &
+Name::appendComp(const string &compStr)
+{
+  Bytes comp(compStr.begin(), compStr.end());
+  appendComp(comp);
+  return *this;
+}
+
+Bytes
+Name::getComp(int index) const
+{
+  if (index >= m_comps.size())
+  {
+    boost::throw_exception(NameException() << error_info_str("Index out of range: " + boost::lexical_cast<string>(index)));
+  }
+  return m_comps[index];
+}
+
+string
+Name::getCompAsString(int index) const
+{
+  Bytes comp = getComp(index);
+  stringstream ss(stringstream::out);
+  int size = comp.size();
+  for (int i = 0; i < size; i++)
+  {
+    unsigned char ch = comp[i];
+    if (isprint(ch))
+    {
+      ss << (char) ch;
+    }
+    else
+    {
+      ss << "%" << hex << setfill('0') << setw(2) << ch;
+    }
+  }
+
+  return ss.str();
+}
+
+Name
+Name::getPartialName(int start, int n) const
+{
+  int size = m_comps.size();
+  if (start < 0 || start >= size || start + n > size)
+  {
+    stringstream ss(stringstream::out);
+    ss << "getPartialName() parameter out of range! ";
+    ss << "start = " << start;
+    ss << "n = " << n;
+    ss << "size = " << size;
+    boost::throw_exception(NameException() << error_info_str(ss.str()));
+  }
+
+  vector<Bytes> comps;
+  for (int i = 0; i < n; i++)
+  {
+    comps.push_back(m_comps[start + i]);
+  }
+  return Name(comps);
+}
+
+ostream &
+operator <<(ostream &os, const Name &name)
+{
+  int size = name.size();
+  vector<string> strComps;
+  for (int i = 0; i < size; i++)
+  {
+    strComps.push_back(name.getCompAsString(i));
+  }
+  string joined = boost::algorithm::join(strComps, "/");
+  os << "/" << joined;
+  return os;
+}
+
+
+Selectors::Selectors()
+          : m_maxSuffixComps(-1)
+          , m_minSuffixComps(-1)
+          , m_answerOriginKind(AOK_DEFAULT)
+          , m_interestLifetime(-1.0)
+          , m_scope(-1)
+          , m_childSelector(DEFAULT)
+{
+}
+
+Selectors::Selectors(const Selectors &other)
+{
+  m_maxSuffixComps = other.m_maxSuffixComps;
+  m_minSuffixComps = other.m_minSuffixComps;
+  m_answerOriginKind = other.m_answerOriginKind;
+  m_interestLifetime = other.m_interestLifetime;
+  m_scope = other.m_scope;
+  m_childSelector = other.m_childSelector;
+  m_publisherPublicKeyDigest = other.m_publisherPublicKeyDigest;
+}
+
+CcnxCharbufPtr
+Selectors::toCcnxCharbuf()
+{
+  CcnxCharbufPtr ptr(new CcnxCharbuf());
+  ccn_charbuf *cbuf = ptr->getBuf();
+  ccn_charbuf_append_tt(cbuf, CCN_DTAG_Interest, CCN_DTAG);
+  ccn_charbuf_append_tt(cbuf, CCN_DTAG_Name, CCN_DTAG);
+  ccn_charbuf_append_closer(cbuf); // </Name>
+
+  if (m_maxSuffixComps < m_minSuffixComps)
+  {
+    boost::throw_exception(InterestSelectorException() << error_info_str("MaxSuffixComps = " + boost::lexical_cast<string>(m_maxSuffixComps) + " is smaller than  MinSuffixComps = " + boost::lexical_cast<string>(m_minSuffixComps)));
+  }
+
+  if (m_maxSuffixComps > 0)
+  {
+    ccnb_tagged_putf(cbuf, CCN_DTAG_MaxSuffixComponents, "%d", m_maxSuffixComps);
+  }
+
+  if (m_minSuffixComps > 0)
+  {
+    ccnb_tagged_putf(cbuf, CCN_DTAG_MinSuffixComponents, "%d", m_minSuffixComps);
+  }
+
+  if (m_answerOriginKind != AOK_DEFAULT)
+  {
+    // it was not using "ccnb_tagged_putf" in ccnx c code, no idea why
+    ccn_charbuf_append_tt(cbuf, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
+    ccnb_append_number(cbuf, m_answerOriginKind);
+    ccn_charbuf_append_closer(cbuf); // <AnswerOriginKind>
+  }
+
+  if (m_scope != -1)
+  {
+    ccnb_tagged_putf(cbuf, CCN_DTAG_Scope, "%d", m_scope);
+  }
+
+  if (m_interestLifetime > 0.0)
+  {
+    // Ccnx timestamp unit is weird 1/4096 second
+    // this is from their code
+    unsigned lifetime = 4096 * (m_interestLifetime + 1.0/8192.0);
+    if (lifetime == 0 || lifetime > (30 << 12))
+    {
+      boost::throw_exception(InterestSelectorException() << error_info_str("Ccnx requires 0 < lifetime < 30.0. lifetime= " + boost::lexical_cast<string>(m_interestLifetime)));
+    }
+    unsigned char buf[3] = {0};
+    for (int i = sizeof(buf) - 1; i >= 0; i--, lifetime >>= 8)
+    {
+      buf[i] = lifetime & 0xff;
+    }
+    ccnb_append_tagged_blob(cbuf, CCN_DTAG_InterestLifetime, buf, sizeof(buf));
+  }
+
+  if (m_childSelector != DEFAULT)
+  {
+    ccnb_tagged_putf(cbuf, CCN_DTAG_ChildSelector, "%d", m_childSelector);
+  }
+
+
+  ccn_charbuf_append_closer(cbuf); // </Interest>
+
+  return ptr;
+}
+
+} // Ccnx
diff --git a/src/ccnx-tunnel.cpp b/src/ccnx-tunnel.cpp
index 06adae0..037bd8a 100644
--- a/src/ccnx-tunnel.cpp
+++ b/src/ccnx-tunnel.cpp
@@ -28,9 +28,9 @@
 }
 
 int
-CcnxTunnel::sendInterest (const Interest &interest, Closure *closure)
+CcnxTunnel::sendInterest (const string &interest, Closure *closure)
 {
-  string strInterest = interest.name();
+  string strInterest = interest;
   string tunneledInterest = queryRoutableName(strInterest);
   Closure *cp = new TunnelClosure(closure, this, strInterest);
   sendInterest(tunneledInterest, cp);
diff --git a/src/ccnx-wrapper.cpp b/src/ccnx-wrapper.cpp
index d84dc0d..1980e6e 100644
--- a/src/ccnx-wrapper.cpp
+++ b/src/ccnx-wrapper.cpp
@@ -32,17 +32,6 @@
   return &bytes[0];
 }
 
-void
-split(const string &name, Comps &comps)
-{
-  stringstream ss(name);
-  string comp;
-  while(getline(ss, comp, '/'))
-  {
-    comps.push_back(comp);
-  }
-}
-
 CcnxWrapper::CcnxWrapper()
   : m_handle (0)
   , m_keyStore (0)
@@ -386,7 +375,7 @@
   return CCN_UPCALL_RESULT_OK;
 }
 
-int CcnxWrapper::sendInterest (const Interest &interest, Closure *closure)
+int CcnxWrapper::sendInterest (const string &interest, Closure *closure)
 {
   UniqueRecLock(m_mutex);
   if (!m_running || !m_connected)
@@ -395,7 +384,7 @@
   ccn_charbuf *pname = ccn_charbuf_create();
   ccn_closure *dataClosure = new ccn_closure;
 
-  ccn_name_from_uri (pname, interest.name().c_str());
+  ccn_name_from_uri (pname, interest.c_str());
   dataClosure->data = (void *)closure;
 
   dataClosure->p = &incomingData;
diff --git a/wscript b/wscript
index b7991ae..e35bc4c 100644
--- a/wscript
+++ b/wscript
@@ -62,7 +62,8 @@
             'src/ccnx-pco.cpp',
             'src/ccnx-closure.cpp',
             'src/ccnx-tunnel.cpp',
-            'src/object-db-file.cpp'
+            'src/object-db-file.cpp',
+            'src/ccnx-name.cpp',
             ],
         use = 'BOOST BOOST_THREAD SSL CCNX',
         includes = ['include', ],