odin-detector/odin-data

View on GitHub
cpp/common/src/IpcMessage.cpp

Summary

Maintainability
Test Coverage
/*
 * IpcMessage.cpp
 *
 *  Created on: Feb 9, 2015
 *      Author: tcn45
 */

#include "IpcMessage.h"


namespace OdinData {

//! Default constructor - initialises all attributes.
//!
//! This consructs an empty IPC message object with initialised, but invalid, attributes and an
//! empty parameter block. The message can be subsequently populated with valid contents through
//! calls to the various setter methods provided.
//!
//! \param msg_type           - MsgType enumerated message type (default: MsgTypeIllegal)
//! \param msg_val            - MsgVal enumerated message value (default: MsgValIllegal)
//! \param strict_validation  - Enforces strict validation of the message contents during subsequent
//!                             setter calls (default: True)

IpcMessage::IpcMessage(MsgType msg_type, MsgVal msg_val, bool strict_validation) :
    strict_validation_(strict_validation),
    msg_type_(msg_type),
    msg_val_(msg_val),
    msg_id_(0),
    msg_timestamp_(boost::posix_time::microsec_clock::local_time())
{
  // Intialise empty JSON document
  doc_.SetObject();

  // Create the required params block
  rapidjson::Value params;
  params.SetObject();
  doc_.AddMember("params", params, doc_.GetAllocator());
};

//! Constructor taking JSON-formatted text message as argument.
//!
//! This constructor takes a JSON-formatted string as an argument to construct a message
//! based on its contents. If the string is not valid JSON syntax, an IpcMessageException
//! will be thrown. If strict validation is enabled, an IpcMessgeException will be
//! thrown if any of the message attributes do not have valid values.
//!
//! \param json_msg          - JSON-formatted string containing the message to parse
//! \param strict_validation - Enforces strict validation of the message contents during subsequent
//!                            setter calls (default: True)

IpcMessage::IpcMessage(const char* json_msg, bool strict_validation) :
    strict_validation_(strict_validation)
{

  // Parse the message, catching any unexpected exceptions from rapidjson
  try {
    doc_.Parse(json_msg);
  }
  catch (...)
  {
    throw OdinData::IpcMessageException("Unknown exception caught during parsing message");
  }

  // Test if the message parsed correctly, otherwise throw an exception
  if (doc_.HasParseError())
  {
    std::stringstream ss;
    ss << "JSON parse error creating message from string at offset " << doc_.GetErrorOffset() ;
    ss << " : " << rapidjson::GetParseError_En(doc_.GetParseError());
    throw OdinData::IpcMessageException(ss.str());
  }

  // Extract required valid attributes from message. If strict validation is enabled, throw an
  // exception if any are illegal
  msg_type_ = valid_msg_type(get_attribute<std::string>("msg_type", "none"));
  if (strict_validation_ && (msg_type_ == MsgTypeIllegal))
  {
    throw OdinData::IpcMessageException("Illegal or missing msg_type attribute in message");
  }

  msg_val_  = valid_msg_val(get_attribute<std::string>("msg_val", "none"));
  if (strict_validation_ && (msg_val_ == MsgValIllegal))
  {
    throw OdinData::IpcMessageException("Illegal or missing msg_val attribute in message");
  }

  msg_timestamp_ = valid_msg_timestamp(get_attribute<std::string>("timestamp", "none"));
  if (strict_validation_ && (msg_timestamp_ == boost::posix_time::not_a_date_time))
  {
    throw OdinData::IpcMessageException("Illegal or missing timestamp attribute in message");
  }

  try
  {
    msg_id_ = get_attribute<unsigned int>("id", 0);
  }
  catch (...)
  {
    if (strict_validation_)
    {
      throw OdinData::IpcMessageException("Illegal or missing id attribute in message");
    }
  }

  // Check if a params block is present. If strict validation is enabled, thrown an exception if
  // absent.
  if (strict_validation && !has_params())
  {
    throw OdinData::IpcMessageException("Missing params block in message");
  }

}

//! Constructor taking rapidJSON value as argument.
//!
//! This constructor takes a rapidJSON value as an argument to construct a message
//! based on its contents.
//!
//! \param value             - rapidJSON value to set as the parameters
//! \param msg_type          - MsgType enumerated message type (default: MsgTypeIllegal)
//! \param msg_val           - MsgVal enumerated message value (default: MsgValIllegal)
//! \param strict_validation - Enforces strict validation of the message contents during subsequent
//!                            setter calls (default: True)

IpcMessage::IpcMessage(const rapidjson::Value& value,
                       MsgType msg_type,
                       MsgVal msg_val,
                       bool strict_validation) :
    strict_validation_(strict_validation),
    msg_type_(msg_type),
    msg_val_(msg_val),
    msg_id_(0),
    msg_timestamp_(boost::posix_time::microsec_clock::local_time())
{
  // Intialise empty JSON document
  doc_.SetObject();

  // Make a deep copy of the value
  rapidjson::Value newValue(value, doc_.GetAllocator());
  // Create the required params block
  doc_.AddMember("params", newValue, doc_.GetAllocator());
}

//! Updates parameters from another IPCMessage.
//!
//! This will iterate the parameters in the given IPCMessage and set them on this instance.
//!
//! \param other - IPCMessage to take parameters from

void IpcMessage::update(const IpcMessage& other)
{
  std::vector<std::string> params = other.get_param_names();
  for (std::vector<std::string>::const_iterator itr = params.begin(); itr != params.end(); ++itr) {
    this->set_param<const rapidjson::Value&>(*itr, other.get_param<const rapidjson::Value&>(*itr));
  }
}

//! Updates parameters from a rapidJSON object.
//!
//! This method will update the parameters in message with the contents of the specified
//! JSON object.
//!
//! \param param_val - RapidJSON value object of parameters
//! \param param_prefix - string prefix for parameter path to set

void IpcMessage::update(const rapidjson::Value& param_val, std::string param_prefix)
{
    if (param_val.IsObject())
    {
      for (rapidjson::Value::ConstMemberIterator itr = param_val.MemberBegin();
          itr != param_val.MemberEnd(); ++itr)
      {

          std::string param_path = itr->name.GetString();
          if (!param_prefix.empty())
          {
            param_path = param_prefix + "/" + param_path;
          }
          this->update(itr->value, param_path);
      }
    }
    else
    {
      this->set_param<const rapidjson::Value&>(param_prefix, param_val);
    }
}

//! Returns a vector of all parameter names contained in the message.
//!
//! \return A vector of all parameter names in this message

std::vector<std::string> IpcMessage::get_param_names() const
{
  std::vector<std::string> names;
  rapidjson::Value::ConstMemberIterator itr = doc_.FindMember("params");
  for (rapidjson::Value::ConstMemberIterator param_itr = itr->value.MemberBegin();
      param_itr != itr->value.MemberEnd(); ++param_itr){
    std::string param_name(param_itr->name.GetString());
    names.push_back(param_name);
  }
  return names;
}

//! Searches for the named parameter in the message.
//!
//! This method returns true if the parameter is found in the message, or false
//! if the block or parameter is missing
//!
//! \param param_name - string name of the parameter to return
//! \return true if the parameter is present, otherwise false

bool IpcMessage::has_param(const std::string& param_name) const
{
  bool param_found = true;

  // Locate the params block
  rapidjson::Value::ConstMemberIterator itr = doc_.FindMember("params");
  if (itr == doc_.MemberEnd())
  {
    // No params block so we will not find the parameter
    param_found = false;
  }
  else
  {
    if (param_name.find("/") != param_name.npos)
    {
      std::string nodeName = param_name.substr(0, param_name.find("/"));
      std::string subParam = param_name.substr(param_name.find("/") + 1, param_name.npos);
      if (this->has_param(nodeName))
      {
        OdinData::IpcMessage node(this->get_param<const rapidjson::Value &>(nodeName));
        param_found = node.has_param(subParam);
      }
      else
      {
        param_found = false;
      }
    } else
    {
      // Attempt to locate parameter within block
      rapidjson::Value::ConstMemberIterator param_itr = itr->value.FindMember(param_name.c_str());
      if (param_itr == itr->value.MemberEnd())
      {
        // Couldn't find the parameter
        param_found = false;
      }
    }
  }
  return param_found;
}


//! Sets the message type to not acknowledged and sets an appropriate rejection message
//!
//! This method can be used to set this IpcMessage up as a rejection (nack) message.
//! A reason is supplied and set as a parameter called 'error'
//!
//! \return void

void IpcMessage::set_nack(const std::string& reason)
{
  this->set_msg_type(MsgTypeNack);
  this->set_param("error", reason);
}

//! Indicates if message has necessary attributes with legal values.
//!
//! This method indicates if the message is valid, i.e. that all required attributes
//! have legal values and that a parameter block is present
//!
//! \return bool value, true if the emssage is valid, false otherwise

bool IpcMessage::is_valid(void)
{
  return ((msg_type_ != MsgTypeIllegal) &
      (msg_val_ != MsgValIllegal) &
      (msg_timestamp_ != boost::posix_time::not_a_date_time) &
      has_params());
}

//! Returns type attribute of message.
//!
//! This method returns the "msg_type" type attribute of the message.
//!
//! \return MsgType enumerated message type attribute

const IpcMessage::MsgType IpcMessage::get_msg_type(void) const
{
  return msg_type_;
}

//! Returns value attribute of message.
//!
//! This method returns the "msg_val" value attribute of the message.
//!
//! \return MsgVal enumerated message value attribute

const IpcMessage::MsgVal IpcMessage::get_msg_val(void) const
{
  return msg_val_;
}

//! Returns message timestamp as a string in ISO8601 extended format.
//!
//! This method returns the message timestamp (generated either at message creation or
//! from the parsed content of a JSON message). The message is returned as a string
//! in ISO8601 extended format
//!
//! \return std::string message timestamp in ISO8601 extended format

const std::string IpcMessage::get_msg_timestamp(void) const
{
  return boost::posix_time::to_iso_extended_string(msg_timestamp_);
}

//! Returns message timstamp as tm structure.
//!
//! This method returns a standard tm struture derived from the message timestamp.
//!
//! \return tm struct containing the message timestamp
const struct tm IpcMessage::get_msg_datetime(void) const
{
  return boost::posix_time::to_tm(msg_timestamp_);
}

//! Returns id attribute of message.
//!
//! This method returns the "id" attribute of the message.
//!
//! \return unsigned int message id
const unsigned int IpcMessage::get_msg_id(void) const
{
  return msg_id_;
}

//! Sets the message type attribute.
//!
//! This method sets the type attribute of the message. If strict validation is enabled
//! an IpcMessageException will be thrown if the type is illegal.
//!
//! \param msg_type - MsgType enumerated message type

void IpcMessage::set_msg_type(IpcMessage::MsgType const msg_type)
{
  // TODO validation check
  msg_type_ = msg_type;
}

//! Sets the message type attribute.
//!
//! This method sets the value attribute of the message. If strict validation is enabled
//! an IpcMessageException will be thrown if the value is illegal.
//!
//! \param msg_val - MsgVal enumerated message value

void IpcMessage::set_msg_val(IpcMessage::MsgVal const msg_val)
{
  // TODO validation check
  msg_val_ = msg_val;

}

//! Sets the message id attribute.
//!
//! This method sets the id attribute of the message.
//!
//! \param msg_id - Message id
void IpcMessage::set_msg_id(unsigned int msg_id)
{
  msg_id_ = msg_id;
}

//! Returns a JSON-encoded string of the message.
//!
//! This method returns a JSON-encoded string version of the message, intended for
//! transmission across an IPC message channel.
//!
//! \return JSON encoded message as a null-terminated, string character array

const char* IpcMessage::encode(void)
{

  // Copy the validated attributes into the JSON document ready for encoding
  set_attribute("msg_type", valid_msg_type(msg_type_));
  set_attribute("msg_val", valid_msg_val(msg_val_));
  set_attribute("id", msg_id_);
  set_attribute("timestamp", valid_msg_timestamp(msg_timestamp_));

  // Clear the encoded output buffer otherwise successive encode() calls append
  // the message to the buffer
  encode_buffer_.Clear();

  // Create a writer and associate with the document
  rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<> > writer(encode_buffer_);
  doc_.Accept(writer);

  // Return the encoded buffer string
  return encode_buffer_.GetString();
}

//! Returns a JSON-encoded string of the message parameters at a specified path
//!
//! This method returns a JSON-encoded string version of the parameters associated with the message,
//! or a subset of those at a specified slash-delimited path.
//!
//! \param param_path - JSON pointer-like slash delimited path into the parameter block
//! \return  JSON encoded parameters as a null-terminated, string character array

const char* IpcMessage::encode_params(const std::string& param_path)
{

  // Construct a JSON pointer string to the message parameters. If a path argument was specified
  // append that accordingly.
  std::string path = "/params";
  if (!param_path.empty())
  {
    path = path + "/" + param_path;
  }

  // Resolve the pointer to the appropriate location in the parameters
  const rapidjson::Value* param_ptr = rapidjson::Pointer(path.c_str()).Get(doc_);

  // Clear the encode buffer, create a writer and associate with the parameter pointer
  encode_buffer_.Clear();
  rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<> > writer(encode_buffer_);
  param_ptr->Accept(writer);

  // Return the string encoding of the requested parameters
  return encode_buffer_.GetString();
}

//! Copies parameters at a specified into the specified JSON value object
//!
//! This method copies the message parameters at the specified path into a JSON value object.
//!
//! \param param_obj - reference to JSON value object to receive the parameters
//! \param param_path - path of parameters in the message to copy

void IpcMessage::copy_params(rapidjson::Value& param_obj, const std::string& param_path)
{
  std::string path = "/params";
  if (!param_path.empty())
  {
    path = path + "/" + param_path;
  }

  const rapidjson::Value* param_ptr = rapidjson::Pointer(path.c_str()).Get(doc_);

  param_obj.CopyFrom(*param_ptr, doc_.GetAllocator());
}

//! Overloaded equality relational operator.
//!
//! This function overloads the equality relational operator, allowing two
//! messages to be compared for the same content. All fields in the messages
//! are compared and tested for equality
//!
//! \param lhs_msg - left-hand side message for comparison
//! \param rhs_msg - right-hand side message for comparison
//! \return boolean indicating equality

bool operator ==(IpcMessage const& lhs_msg, IpcMessage const& rhs_msg)
{
  bool areEqual = true;

  // Test equality of message attributes
  areEqual &= (lhs_msg.msg_type_ == rhs_msg.msg_type_);
  areEqual &= (lhs_msg.msg_val_  == rhs_msg.msg_val_);

  // Check both messages have a params block
  areEqual &= (lhs_msg.has_params() == rhs_msg.has_params());

  // Only continue to test equality if true at this point
  if (areEqual)
  {
    // Iterate over params block if present and test equality of all members
    if (lhs_msg.has_params() && rhs_msg.has_params())
    {
      rapidjson::Value const& lhs_params = lhs_msg.doc_["params"];
      rapidjson::Value const& rhs_params = rhs_msg.doc_["params"];

      areEqual &= (lhs_params.MemberCount() == rhs_params.MemberCount());

      for (rapidjson::Value::ConstMemberIterator lhs_itr = lhs_params.MemberBegin();
           areEqual && (lhs_itr != lhs_params.MemberEnd()); lhs_itr++)
      {
        rapidjson::Value::ConstMemberIterator rhs_itr = rhs_params.FindMember(lhs_itr->name.GetString());
        if (rhs_itr != rhs_params.MemberEnd())
        {
          areEqual &= (lhs_itr->value == rhs_itr->value);
        }
        else
        {
          areEqual = false;
        }
      }
    }
  }
  return areEqual;
}

//! Overloaded inequality relational operator.
//!
//! This function overloads the inequality relational operator, allowing two
//! messages to be compared for the different content. All fields in the messages
//! are compared and tested for inequality.
//!
//! \param lhs_msg - left-hand side message for comparison
//! \param rhs_msg - righ-hand side message for comparison
//! \return boolean indicating inequality

bool operator !=(IpcMessage const& lhs_msg, IpcMessage const& rhs_msg)
{
  return !(lhs_msg == rhs_msg);
}

//! Overloaded stream insertion operator.
//!
//! This function overloads the ostream insertion operator, allowing a message
//! to be inserted into a ostream as a JSON-formatted string.
//!
//! \param os      - std::ostream to insert string into
//! \param the_msg - reference to the IpcMessage object to insert

std::ostream& operator <<(std::ostream& os, IpcMessage& the_msg)    {
  os << the_msg.encode();
  return os;
}

//! \privatesection

//! Initialise the internal message type bidirectional map.
//!
//! This function initialises the interal message type bi-directional map for
//! use in the valid_msg_type methods.

void IpcMessage::msg_type_map_init()
{
  msg_type_map_.insert(MsgTypeMapEntry("cmd",    MsgTypeCmd));
  msg_type_map_.insert(MsgTypeMapEntry("ack",    MsgTypeAck));
  msg_type_map_.insert(MsgTypeMapEntry("nack",   MsgTypeNack));
  msg_type_map_.insert(MsgTypeMapEntry("notify", MsgTypeNotify));
}

//! Maps a message type string to a valid enumerated MsgType.
//!
//! This private method maps a string message type to an valid enumerated
//! MsgType. If the string is not valid, MsgTypeIllegal is returned
//!
//! \param msg_type_name - string message type
//! \return MsgType value, MsgTypeIllegal if string is not a valid message type

IpcMessage::MsgType IpcMessage::valid_msg_type(std::string msg_type_name)
{

  MsgType msg_type = MsgTypeIllegal;
  if (msg_type_map_.size() == 0)
  {
    msg_type_map_init();
  }

  if (msg_type_map_.left.count(msg_type_name))
  {
    msg_type = msg_type_map_.left.at(msg_type_name);
  }

  return msg_type;
}

//! Maps an enumerated MsgType message type to the equivalent string.
//!
//! This private method maps an enumnerated MsgType message type to the
//! equivalent string. If the type is no valid, "illegal" is returned.
//!
//! \param msg_type - enumerated MsgType message type
//! \return string containing the message type

std::string IpcMessage::valid_msg_type(IpcMessage::MsgType msg_type)
{
  std::string msg_type_name = "illegal";
  if (msg_type_map_.size() == 0)
  {
    msg_type_map_init();
  }

  if (msg_type_map_.right.count(msg_type))
  {
    msg_type_name = msg_type_map_.right.at(msg_type);
  }

  return msg_type_name;
}

//! Initialise the internal message value bidirectional map.
//!
//! This function initialises the interal message value bi-directional map for
//! use in the valid_msg_val methods.

void IpcMessage::msg_val_map_init()
{
  msg_val_map_.insert(MsgValMapEntry("reset",                    MsgValCmdReset));
  msg_val_map_.insert(MsgValMapEntry("status",                   MsgValCmdStatus));
  msg_val_map_.insert(MsgValMapEntry("configure",                MsgValCmdConfigure));
  msg_val_map_.insert(MsgValMapEntry("request_configuration",    MsgValCmdRequestConfiguration));
  msg_val_map_.insert(MsgValMapEntry("execute",                  MsgValCmdExecute));
  msg_val_map_.insert(MsgValMapEntry("request_commands",         MsgValCmdRequestCommands));
  msg_val_map_.insert(MsgValMapEntry("request_version",          MsgValCmdRequestVersion));
  msg_val_map_.insert(MsgValMapEntry("request_buffer_config",    MsgValCmdBufferConfigRequest));
  msg_val_map_.insert(MsgValMapEntry("request_buffer_precharge", MsgValCmdBufferPrechargeRequest));
  msg_val_map_.insert(MsgValMapEntry("reset_statistics",         MsgValCmdResetStatistics));
  msg_val_map_.insert(MsgValMapEntry("shutdown",                 MsgValCmdShutdown));
  msg_val_map_.insert(MsgValMapEntry("identity",                 MsgValNotifyIdentity));
  msg_val_map_.insert(MsgValMapEntry("frame_ready",              MsgValNotifyFrameReady));
  msg_val_map_.insert(MsgValMapEntry("frame_release",            MsgValNotifyFrameRelease));
  msg_val_map_.insert(MsgValMapEntry("buffer_config",            MsgValNotifyBufferConfig));
  msg_val_map_.insert(MsgValMapEntry("buffer_precharge",         MsgValNotifyBufferPrecharge));
  msg_val_map_.insert(MsgValMapEntry("notify_status",            MsgValNotifyStatus));
}

//! Maps a message value string to a valid enumerated MsgVal.
//!
//! This private method maps a string message value to an valid enumerated
//! MsgVal. If the string is not valid, MsgValIllegal is returned.
//!
//! \param msg_val_name - string message value
//! \return MsgVal value, MsgValIllegal if string is not a valid message value

IpcMessage::MsgVal IpcMessage::valid_msg_val(std::string msg_val_name)
{
  MsgVal msg_val = MsgValIllegal;

  if (msg_val_map_.size() == 0)
  {
    msg_val_map_init();
  }

  if (msg_val_map_.left.count(msg_val_name))
  {
    msg_val = msg_val_map_.left.at(msg_val_name);
  }
  return msg_val;
}

//! Maps an enumerated MsgVal message value to the equivalent string.
//!
//! This private method maps an enumnerated MsgVal message value to the
//! equivalent string. If the value is no valid, "illegal" is returned.
//!
//! \param msg_val - enumerated MsgVal message value
//! \return string containing the message value

std::string IpcMessage::valid_msg_val(IpcMessage::MsgVal msg_val)
{
  std::string msg_val_name = "illegal";
  if (msg_val_map_.size() == 0)
  {
    msg_val_map_init();
  }

  if (msg_val_map_.right.count(msg_val))
  {
    msg_val_name = msg_val_map_.right.at(msg_val);
  }

  return msg_val_name;
}

//! Maps a message timestamp onto a the internal timestamp representation.
//!
//! This private method maps a valid, ISO8601 extended format timestamp string
//! to the internal representation. If the string is not valid, the timestamp
//! returned is set to the boost::posix::not_a_date_time value.
//!
//! \param msg_timestamp_text message timestamp string in ISO8601 extneded format
//! \return boost::posix::ptime internal timestamp representation

boost::posix_time::ptime IpcMessage::valid_msg_timestamp(std::string msg_timestamp_text)
{

  boost::posix_time::ptime pt(boost::posix_time::not_a_date_time);

  try {
    pt = boost::date_time::parse_delimited_time<boost::posix_time::ptime>(msg_timestamp_text, 'T');
  }
  catch (...)
  {
    // Do nothing if parse exception occurred - return value of not_a_date_time indicates error
  }
  return pt;
}

//! Maps an internal message timestamp representation to an ISO8601 extended format string.
//!
//! This private method maps the internal boost::posix_time::ptime timestamp representation
//! to an ISO8601 extended format string, as used in the encoded JSON message.
//!
//! \param msg_timestamp - internal boost::posix_time::ptime timestamp representation
//! return ISO8601 extended format timestamp string

std::string IpcMessage::valid_msg_timestamp(boost::posix_time::ptime msg_timestamp)
{
  // Return message timestamp as string in ISO8601 extended format
  return boost::posix_time::to_iso_extended_string(msg_timestamp_);
}

//! Indicates if the message has a params block.
//!
//! This private method indicates if the message has a valid params block, which may be
//! empty but is required for valid message syntax.
//!
//! \return boolean value, true if message has valid params block

bool IpcMessage::has_params(void) const
{
  bool has_params = false;
  rapidjson::Value::ConstMemberIterator itr = doc_.FindMember("params");
  if (itr != doc_.MemberEnd())
  {
    has_params = itr->value.IsObject();
  }
  return has_params;
}

// Explicit specialisations of the the get_value method, mapping native attribute types to the
// appropriate RapidJSON storage type.

template<> int IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetInt();
}

template<> unsigned int IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetUint();
}

#ifdef __APPLE__

// OS X clang compiler seems to base uint64 on a different base type than gcc on Linux, causing
// linker errors with templated specialisations of get_value and set_value for unsigned long.
template<> unsigned long IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetUint64();
}
#endif

template<> int64_t IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetInt64();
}

template<> uint64_t IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetUint64();
}

template<> double IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetDouble();
}

template<> std::string IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetString();
}

template<> bool IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  return itr->value.GetBool();
}

template<> const rapidjson::Value& IpcMessage::get_value(rapidjson::Value::ConstMemberIterator& itr) const
{
  const rapidjson::Value& val = itr->value;
  return val;
}

// Explicit specialisations of the the set_value method, mapping  RapidJSON storage types
// to the appropriate native type.

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - integer value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, int const& value)
{
  value_obj.SetInt(value);
}

// Explicit specialisations of the the set_value method, mapping  RapidJSON storage types
// to the appropriate native type.

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - boolean value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, bool const& value)
{
  value_obj.SetBool(value);
}

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - unsigned integer value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, unsigned int const& value)
{
  value_obj.SetUint(value);
}

#ifdef __APPLE__

// OS X clang compiler seems to base uint64 on a different base type than gcc on Linux, causing
// linker errors with templated specialisations of get_value and set_value for unsigned long.
template<> void IpcMessage::set_value(rapidjson::Value& value_obj, unsigned long const& value)
{
  value_obj.SetUint64(value);
}
#endif

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - int64_t value to set


template<> void IpcMessage::set_value(rapidjson::Value& value_obj, int64_t const& value)
{
  value_obj.SetInt64(value);
}

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - uint64_t value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, uint64_t const& value)
{
  value_obj.SetUint64(value);
}

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - double value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, double const& value)
{
  value_obj.SetDouble(value);
}

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - string value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, std::string const& value)
{
  value_obj.SetString(value.c_str(), doc_.GetAllocator());
}

//! Sets the value of a message attribute.
//!
//! This explicit specialisation of the private template method sets the value of a
//! message attribute referenced by the RapidJSON value object passed as an argument.
//!
//! \param value_obj - RapidJSON value object to set value of
//! \param value - RapidJSON value to set

template<> void IpcMessage::set_value(rapidjson::Value& value_obj, rapidjson::Value const& value)
{
  value_obj.CopyFrom(value, doc_.GetAllocator());
}

// Definition of static member variables used for type and value mapping
IpcMessage::MsgTypeMap IpcMessage::msg_type_map_;
IpcMessage::MsgValMap IpcMessage::msg_val_map_;

} // namespace OdinData