text:
/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Wed 29 Feb 17:24:10 2012
 *
 * @brief Implements HDF5 groups.
 *
 * Copyright (C) Idiap Research Institute, Martigny, Switzerland
 */

#include <boost/make_shared.hpp>
#include <boost/shared_array.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>

#include <bob.core/logging.h>

#include <bob.io.base/HDF5Group.h>
#include <bob.io.base/HDF5Utils.h>

/**
 * Creates an "auto-destructible" HDF5 Group
 */
static void delete_h5g (hid_t* p) {
  if (*p >= 0) {
    herr_t err = H5Gclose(*p);
    if (err < 0) {
      bob::core::error << "H5Gclose() exited with an error (" << err << "). The stack trace follows:" << std::endl;
      bob::core::error << bob::io::base::format_hdf5_error() << std::endl;
    }
  }
  delete p;
}

static boost::shared_ptr<hid_t> create_new_group(boost::shared_ptr<hid_t> p,
    const std::string& name) {
  boost::shared_ptr<hid_t> retval(new hid_t(-1), std::ptr_fun(delete_h5g));
  *retval = H5Gcreate2(*p, name.c_str(), H5P_DEFAULT, H5P_DEFAULT,
      H5P_DEFAULT);
  if (*retval < 0) {
    boost::format m("call to HDF5 C-function H5Gcreate2() returned error %d. HDF5 error statck follows:\n%s");
    m % *retval % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
  return retval;
}

static boost::shared_ptr<hid_t> open_group(boost::shared_ptr<hid_t> g,
    const char* name) {
  boost::shared_ptr<hid_t> retval(new hid_t(-1), std::ptr_fun(delete_h5g));
  *retval = H5Gopen2(*g, name, H5P_DEFAULT);
  if (*retval < 0) {
    boost::format m("call to HDF5 C-function H5Gopen2() returned error %d. HDF5 error statck follows:\n%s");
    m % *retval % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
  return retval;
}

bob::io::base::detail::hdf5::Group::Group(boost::shared_ptr<Group> parent, const std::string& name):
  m_name(name),
  m_id(create_new_group(parent->location(), name)),
  m_parent(parent)
{
}

/**
 * Simple wrapper to call internal bob::io::base::detail::hdf5::Group::iterate_callback, that can call
 * Group and Dataset constructors. Note that those are private or protected for
 * design reasons.
 */
static herr_t group_iterate_callback(hid_t self, const char *name,
    const H5L_info_t *info, void *object) {
  return static_cast<bob::io::base::detail::hdf5::Group*>(object)->iterate_callback(self, name, info);
}

herr_t bob::io::base::detail::hdf5::Group::iterate_callback(hid_t self, const char *name,
    const H5L_info_t *info) {

  // If we are not looking at a hard link to the data, just ignore
  if (info->type != H5L_TYPE_HARD) {
    TDEBUG1("Ignoring soft-link `" << name << "' in HDF5 file");
    return 0;
  }

  // Get information about the HDF5 object
  H5O_info_t obj_info;
  herr_t status = H5Oget_info_by_name(self, name, &obj_info, H5P_DEFAULT);
  if (status < 0) {
    boost::format m("call to HDF5 C-function H5Oget_info_by_name() returned error %d. HDF5 error statck follows:\n%s");
    m % status % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }

  switch(obj_info.type) {
    case H5O_TYPE_GROUP:
      //creates with recursion
      m_groups[name] = boost::make_shared<bob::io::base::detail::hdf5::Group>(shared_from_this(),
          name, true);
      m_groups[name]->open_recursively();
      break;
    case H5O_TYPE_DATASET:
      m_datasets[name] = boost::make_shared<bob::io::base::detail::hdf5::Dataset>(shared_from_this(),
          std::string(name));
      break;
    default:
      break;
  }

  return 0;
}

bob::io::base::detail::hdf5::Group::Group(boost::shared_ptr<Group> parent,
    const std::string& name, bool):
  m_name(name),
  m_id(open_group(parent->location(), name.c_str())),
  m_parent(parent)
{
  //checks name
  if (!m_name.size() || m_name == "." || m_name == "..") {
    boost::format m("Cannot create group with illegal name `%s' at `%s'");
    m % name % url();
    throw std::runtime_error(m.str());
  }
}

void bob::io::base::detail::hdf5::Group::open_recursively() {
  //iterates over this group only and instantiates what needs to be instantiated
  herr_t status = H5Literate(*m_id, H5_INDEX_NAME,
      H5_ITER_NATIVE, 0, group_iterate_callback, static_cast<void*>(this));
  if (status < 0) {
    boost::format m("Call to HDF5 C-function H5Literate() returned error %d. HDF5 error statck follows:\n%s");
    m % status % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
}

bob::io::base::detail::hdf5::Group::Group(boost::shared_ptr<File> parent):
  m_name(""),
  m_id(open_group(parent->location(), "/")),
  m_parent()
{
}

bob::io::base::detail::hdf5::Group::~Group() { }

const boost::shared_ptr<bob::io::base::detail::hdf5::Group> bob::io::base::detail::hdf5::Group::parent() const {
  return m_parent.lock();
}

boost::shared_ptr<bob::io::base::detail::hdf5::Group> bob::io::base::detail::hdf5::Group::parent() {
  return m_parent.lock();
}

const std::string& bob::io::base::detail::hdf5::Group::filename() const {
  return parent()->filename();
}

std::string bob::io::base::detail::hdf5::Group::path() const {
  return (m_name.size()?parent()->path():"") + "/" + m_name;
}

std::string bob::io::base::detail::hdf5::Group::url() const {
  return filename() + ":" + path();
}

const boost::shared_ptr<bob::io::base::detail::hdf5::File> bob::io::base::detail::hdf5::Group::file() const {
  return parent()->file();
}

boost::shared_ptr<bob::io::base::detail::hdf5::File> bob::io::base::detail::hdf5::Group::file() {
  return parent()->file();
}

boost::shared_ptr<bob::io::base::detail::hdf5::Group> bob::io::base::detail::hdf5::Group::cd(const std::string& dir) {
  //empty dir == void action, return self
  if (!dir.size()) return shared_from_this();

  if (dir[0] == '/') { //absolute path given, apply to root node
    return file()->root()->cd(dir.substr(1));
  }

  //relative path given, start from self
  std::string::size_type pos = dir.find_first_of('/');
  if (pos == std::string::npos) { //it should be one of my children
    if (dir == ".") return shared_from_this();
    if (dir == "..") {
      if (!m_name.size()) { //this is the root group already
        boost::format m("Cannot go beyond root directory at file `%s'");
        m % file()->filename();
        throw std::runtime_error(m.str());
      }
      //else, just return its parent
      return parent();
    }
    if (!has_group(dir)) {
      boost::format m("Cannot find group `%s' at `%s'");
      m % dir % url();
      throw std::runtime_error(m.str());
    }
    //else, just return the named group
    return m_groups[dir];
  }

  //if you get to this point, we are just traversing
  std::string mydir = dir.substr(0, pos);
  if (mydir == ".") return cd(dir.substr(pos+1));
  if (mydir == "..") return parent()->cd(dir.substr(pos+1));
  if (!has_group(mydir)) {
    boost::format m("Cannot find group `%s' at `%s'");
    m % dir % url();
    throw std::runtime_error(m.str());
  }

  //else, just recurse to the next group
  return m_groups[mydir]->cd(dir.substr(pos+1));
}

const boost::shared_ptr<bob::io::base::detail::hdf5::Group> bob::io::base::detail::hdf5::Group::cd(const std::string& dir) const {
  return const_cast<bob::io::base::detail::hdf5::Group*>(this)->cd(dir);
}

boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> bob::io::base::detail::hdf5::Group::operator[] (const std::string& dir) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //search on the current group
    if (!has_dataset(dir)) {
      boost::format m("Cannot find dataset `%s' at `%s'");
      m % dir % url();
      throw std::runtime_error(m.str());
    }
    return m_datasets[dir];
  }

  //if you get to this point, the search routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or raise an exception.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->operator[](dir.substr(pos+1));
}

const boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> bob::io::base::detail::hdf5::Group::operator[] (const std::string& dir) const {
  return const_cast<bob::io::base::detail::hdf5::Group*>(this)->operator[](dir);
}

void bob::io::base::detail::hdf5::Group::reset() {
  typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Group> > group_map_type;
  for (group_map_type::const_iterator it = m_groups.begin();
      it != m_groups.end(); ++it) {
    remove_group(it->first);
  }

  typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> >
    dataset_map_type;
  for (dataset_map_type::const_iterator it = m_datasets.begin();
      it != m_datasets.end(); ++it) {
    remove_dataset(it->first);
  }
}

boost::shared_ptr<bob::io::base::detail::hdf5::Group> bob::io::base::detail::hdf5::Group::create_group(const std::string& dir) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //creates on the current group
    boost::shared_ptr<bob::io::base::detail::hdf5::Group> g =
      boost::make_shared<bob::io::base::detail::hdf5::Group>(shared_from_this(), dir);
    m_groups[dir] = g;
    return g;
  }

  //if you get to this point, the search routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or raise an exception.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->create_group(dir.substr(pos+1));
}

void bob::io::base::detail::hdf5::Group::remove_group(const std::string& dir) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //copy on the current group
    herr_t status = H5Ldelete(*m_id, dir.c_str(), H5P_DEFAULT);
    if (status < 0) {
      boost::format m("Call to HDF5 C-function H5Ldelete() returned error %d. HDF5 error statck follows:\n%s");
      m % status % bob::io::base::format_hdf5_error();
      throw std::runtime_error(m.str());
    }
    typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Group> > map_type;
    map_type::iterator it = m_groups.find(dir);
    m_groups.erase(it);
    return;
  }

  //if you get to this point, the removal routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or raise an exception.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->remove_group(dir.substr(pos+1));
}

/**
 * Opens an "auto-destructible" HDF5 property list
 */
static void delete_h5plist (hid_t* p) {
  if (*p >= 0) {
    herr_t err = H5Pclose(*p);
    if (err < 0) {
      bob::core::error << "H5Pclose() exited with an error (" << err << "). The stack trace follows:" << std::endl;
      bob::core::error << bob::io::base::format_hdf5_error() << std::endl;
    }
  }
  delete p;
}

static boost::shared_ptr<hid_t> open_plist(hid_t classid) {
  boost::shared_ptr<hid_t> retval(new hid_t(-1), std::ptr_fun(delete_h5plist));
  *retval = H5Pcreate(classid);
  if (*retval < 0) {
    boost::format m("call to HDF5 C-function H5Pcreate() returned error %d. HDF5 error statck follows:\n%s");
    m % *retval % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
  return retval;
}

void bob::io::base::detail::hdf5::Group::rename_group(const std::string& from, const std::string& to) {
  boost::shared_ptr<hid_t> create_props = open_plist(H5P_LINK_CREATE);
  H5Pset_create_intermediate_group(*create_props, 1);
  herr_t status = H5Lmove(*m_id, from.c_str(), H5L_SAME_LOC, to.c_str(),
      *create_props, H5P_DEFAULT);
  if (status < 0) {
    boost::format m("Call to HDF5 C-function H5Lmove() returned error %d. HDF5 error statck follows:\n%s");
    m % status % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
}

void bob::io::base::detail::hdf5::Group::copy_group(const boost::shared_ptr<Group> other,
    const std::string& dir) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //copy on the current group
    const char* use_name = dir.size()?dir.c_str():other->name().c_str();
    herr_t status = H5Ocopy(*other->parent()->location(),
        other->name().c_str(), *m_id, use_name, H5P_DEFAULT, H5P_DEFAULT);
    if (status < 0) {
      boost::format m("call to HDF5 C-function H5Ocopy() returned error %d. HDF5 error statck follows:\n%s");
      m % status % bob::io::base::format_hdf5_error();
      throw std::runtime_error(m.str());
    }

    //read new group contents
    boost::shared_ptr<bob::io::base::detail::hdf5::Group> copied =
      boost::make_shared<bob::io::base::detail::hdf5::Group>(shared_from_this(), use_name);
    copied->open_recursively();

    //index it
    m_groups[use_name] = copied;

    return;
  }

  //if you get to this point, the copy routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or return false.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->copy_group(other, dir.substr(pos+1));
}

bool bob::io::base::detail::hdf5::Group::has_group(const std::string& dir) const {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //search on the current group
    if (dir == "." || dir == "..") return true; //special case
    typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Group> > map_type;
    map_type::const_iterator it = m_groups.find(dir);
    return (it != m_groups.end());
  }

  //if you get to this point, the search routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or return false.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->has_group(dir.substr(pos+1));
}

boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> bob::io::base::detail::hdf5::Group::create_dataset
(const std::string& dir, const bob::io::base::HDF5Type& type, bool list,
 size_t compression) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //creates on the current group
    boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> d =
      boost::make_shared<bob::io::base::detail::hdf5::Dataset>(shared_from_this(), dir, type,
          list, compression);
    m_datasets[dir] = d;
    return d;
  }

  //if you get to this point, the search routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or return false.
  std::string dest = dir.substr(0, pos);
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g;
  if (!dest.size()) g = cd("/");
  else {
    //let's make sure the directory exists, or let's create it recursively
    if (!has_group(dest)) g = create_group(dest);
    else g = cd(dest);
  }
  return g->create_dataset(dir.substr(pos+1), type, list, compression);
}

void bob::io::base::detail::hdf5::Group::remove_dataset(const std::string& dir) {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //removes on the current group
    herr_t status = H5Ldelete(*m_id, dir.c_str(), H5P_DEFAULT);
    if (status < 0) {
      boost::format m("Call to HDF5 C-function H5Ldelete() returned error %d. HDF5 error statck follows:\n%s");
      m % status % bob::io::base::format_hdf5_error();
      throw std::runtime_error(m.str());
    }
    typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> > map_type;
    map_type::iterator it = m_datasets.find(dir);
    m_datasets.erase(it);
    return;
  }

  //if you get to this point, the removal routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or raise an exception.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->remove_dataset(dir.substr(pos+1));
}

void bob::io::base::detail::hdf5::Group::rename_dataset(const std::string& from, const std::string& to) {
  boost::shared_ptr<hid_t> create_props = open_plist(H5P_LINK_CREATE);
  H5Pset_create_intermediate_group(*create_props, 1);
  herr_t status = H5Lmove(*m_id, from.c_str(), H5L_SAME_LOC, to.c_str(),
      *create_props, H5P_DEFAULT);
  if (status < 0) {
    boost::format m("Call to HDF5 C-function H5Ldelete() returned error %d. HDF5 error statck follows:\n%s");
    m % status % bob::io::base::format_hdf5_error();
    throw std::runtime_error(m.str());
  }
}

void bob::io::base::detail::hdf5::Group::copy_dataset(const boost::shared_ptr<Dataset> other,
    const std::string& dir) {

  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //search on the current group
    const char* use_name = dir.size()?dir.c_str():other->name().c_str();
    herr_t status = H5Ocopy(*other->parent()->location(),
        other->name().c_str(), *m_id, use_name, H5P_DEFAULT, H5P_DEFAULT);
    if (status < 0) {
      boost::format m("Call to HDF5 C-function H5Ocopy() returned error %d. HDF5 error statck follows:\n%s");
      m % status % bob::io::base::format_hdf5_error();
      throw std::runtime_error(m.str());
    }
    //read new group contents
    m_datasets[use_name] = boost::make_shared<bob::io::base::detail::hdf5::Dataset>(shared_from_this(), use_name);
    return;
  }

  //if you get to this point, the copy routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->copy_dataset(other, dir.substr(pos+1));
}

bool bob::io::base::detail::hdf5::Group::has_dataset(const std::string& dir) const {
  std::string::size_type pos = dir.find_last_of('/');
  if (pos == std::string::npos) { //search on the current group
    typedef std::map<std::string, boost::shared_ptr<bob::io::base::detail::hdf5::Dataset> > map_type;
    map_type::const_iterator it = m_datasets.find(dir);
    return (it != m_datasets.end());
  }

  //if you get to this point, the search routine needs to be performed on
  //another group, indicated by the path. So, we first cd() there and then do
  //the same as we do here. This will recurse through the directory structure
  //until we find the place defined by the user or return false.
  std::string dest = dir.substr(0, pos);
  if (!dest.size()) dest = "/";
  boost::shared_ptr<bob::io::base::detail::hdf5::Group> g = cd(dest);
  return g->has_dataset(dir.substr(pos+1));
}

void bob::io::base::detail::hdf5::Group::gettype_attribute(const std::string& name,
    bob::io::base::HDF5Type& type) const {
  bob::io::base::detail::hdf5::gettype_attribute(m_id, name, type);
}

bool bob::io::base::detail::hdf5::Group::has_attribute(const std::string& name) const {
  return bob::io::base::detail::hdf5::has_attribute(m_id, name);
}

void bob::io::base::detail::hdf5::Group::delete_attribute (const std::string& name) {
  bob::io::base::detail::hdf5::delete_attribute(m_id, name);
}

void bob::io::base::detail::hdf5::Group::read_attribute (const std::string& name,
    const bob::io::base::HDF5Type& dest_type, void* buffer) const {
  bob::io::base::detail::hdf5::read_attribute(m_id, name, dest_type, buffer);
}

void bob::io::base::detail::hdf5::Group::write_attribute (const std::string& name,
    const bob::io::base::HDF5Type& dest_type, const void* buffer) {
  bob::io::base::detail::hdf5::write_attribute(m_id, name, dest_type, buffer);
}

void bob::io::base::detail::hdf5::Group::list_attributes(std::map<std::string, bob::io::base::HDF5Type>& attributes) const {
  bob::io::base::detail::hdf5::list_attributes(m_id, attributes);
}

template <> void bob::io::base::detail::hdf5::Group::set_attribute<std::string>(const std::string& name, const std::string& v) {
  bob::io::base::HDF5Type dest_type(v);
  write_attribute(name, dest_type, reinterpret_cast<const void*>(v.c_str()));
}

template <> std::string bob::io::base::detail::hdf5::Group::get_attribute(const std::string& name) const {
  HDF5Type type;
  gettype_attribute(name, type);
  boost::shared_array<char> v(new char[type.shape()[0]+1]);
  v[type.shape()[0]] = 0; ///< null termination
  read_attribute(name, type, reinterpret_cast<void*>(v.get()));
  std::string retval(v.get());
  return retval;
}

bob::io::base::detail::hdf5::RootGroup::RootGroup(boost::shared_ptr<File> parent):
  bob::io::base::detail::hdf5::Group(parent),
  m_parent(parent)
{
}

bob::io::base::detail::hdf5::RootGroup::~RootGroup() {
}

const std::string& bob::io::base::detail::hdf5::RootGroup::filename() const {
  return m_parent.lock()->filename();
}


text:
#include "ExtractMetaData.h"

#include <boost/algorithm/string.hpp>
#include <boost/iostreams/stream.hpp>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

namespace bio = boost::iostreams;
namespace bpt = boost::property_tree;

namespace mdf {

    static bool parseCommonPropertiesTree(bpt::ptree const& tree, MetadataMap& map, std::string prefix = "");

    static bpt::ptree const& getAttributesTree(bpt::ptree const& tree);

    // Helper to have an empty property tree.
    bpt::ptree const& getEmptyTree() {
        static bpt::ptree t;
        return t;
    }

    bool extractMetadata(std::string const& xml, MetadataMap& metadata) {
        bool result = false;

        bio::stream<bio::array_source> stream(xml.c_str(), xml.length());

        // Parse using boost property tree.
        bpt::ptree propertyTree;
        bpt::read_xml(stream, propertyTree);

        // Look for the tool independent data.
        bpt::ptree const& HDTree = propertyTree.get_child("HDcomment.common_properties", getEmptyTree());

        parseCommonPropertiesTree(HDTree, metadata, "HDComment");

        return result;
    }

    bool extractMetadataHD(std::string const& xml, MetadataMap& metadata) {
        bool result = false;

        bio::stream<bio::array_source> stream(xml.c_str(), xml.length());

        // Parse using boost property tree.
        bpt::ptree propertyTree;
        bpt::read_xml(stream, propertyTree);

        // Look for the tool independent data.
        bpt::ptree const& HDTree = propertyTree.get_child("HDcomment.common_properties", getEmptyTree());

        result = parseCommonPropertiesTree(HDTree, metadata, "HDComment.");

        return result;
    }

    bool extractMetadataSI(std::string const& xml, MetadataMap& metadata) {
        bool result = false;
        std::string prefix = "SIcomment.";

        bio::stream<bio::array_source> stream(xml.c_str(), xml.length());

        // Parse using boost property tree.
        bpt::ptree propertyTree;
        bpt::read_xml(stream, propertyTree);

        // Extract root tag.
        bpt::ptree const& root = propertyTree.get_child("SIcomment", getEmptyTree());

        // Read out TX field.
        bpt::ptree const& txField = root.get_child("TX", getEmptyTree());

        // Read out bus.
        bpt::ptree const& busField = root.get_child("bus", getEmptyTree());
        bpt::ptree const& busFieldAttributes = getAttributesTree(busField);
        std::string bus = busFieldAttributes.get<std::string>("name", "");
        if(!bus.empty()) {
            prefix += bus;
            prefix += ".";
        }

        // Look for the tool independent data.
        bpt::ptree const& common = root.get_child("common_properties", getEmptyTree());
        result = parseCommonPropertiesTree(common, metadata, prefix);

        return result;
    }

    static bool parseCommonPropertiesTree(bpt::ptree const& tree, MetadataMap& map, std::string prefix) {
        for(auto const& child: tree) {
            // Extract attributes.
            bpt::ptree const& attributesTree = getAttributesTree(child.second);

            // Extract key type.
            std::string const& keyType = child.first;

            // Check the tag type. Accepted types are "e" for value and "tree" for nested values.
            if (boost::equals(keyType, "e")) {
                // Extract all attributes for the e field.
                MdfMetadataEntry attributes;

                attributes.name = attributesTree.get<std::string>("name");
                attributes.description = attributesTree.get<std::string>("desc", "");
                attributes.unit = attributesTree.get<std::string>("unit", "");
                attributes.valueType = attributesTree.get<std::string>("type", "");
                attributes.readOnly = attributesTree.get<bool>("ro", false);

                // Value.
                attributes.valueRaw = child.second.get_value<std::string>();
                std::string key = prefix + attributes.name;

                // Check if this already exists in the map.
                auto findIter = map.find(key);
                if(findIter != map.end()) {
                    // Error., key already exists in the tree.
                    return false;
                }

                map.emplace(std::make_pair(key, attributes));
            } else if (boost::equals(keyType, "tree")) {
                // Update prefix.
                std::string treeName = attributesTree.get<std::string>("name");
                prefix += treeName;
                prefix += ".";

                // Tree, recursive parsing. Extract the key to prepend on the values.
                parseCommonPropertiesTree(child.second, map, prefix);
            } else {
                // Unknown, skip.
            }
        }

        return true;
    }

    static bpt::ptree const& getAttributesTree(bpt::ptree const& tree) {
        bpt::ptree const& attributesTree = tree.get_child("<xmlattr>", getEmptyTree());

        return attributesTree;
    }

    /*
    bool getLINInfo(FileInfo &fileInfo, std::string xml) {
        // Parse using tinyxml2.
        XMLDocument doc;
        XMLError errorStatus = doc.Parse(xml.c_str(), xml.length());

        if (errorStatus != XML_SUCCESS) {
            return false;
        }

        // Find the correct node.
        // The path we are looking for is: "SIcomment.common_properties.tree".
        XMLElement const *rootElement = doc.RootElement();
        const char *rootName = rootElement->Name();

        if (strncmp(rootName, "SIcomment", strlen(rootName)) != 0) {
            return false;
        }

        // Lookup common properties.
        XMLElement const *cpElement = rootElement->FirstChildElement("common_properties");
        if (cpElement == nullptr) {
            return false;
        }
        if (cpElement->NoChildren()) {
            return false;
        }

        // Find a tree with the correct name.
        XMLElement const *treeElement = nullptr;

        for (XMLElement const *element = cpElement->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "tree" and the name matches.
            const char *elementName = element->Name();
            if (strncmp(elementName, "tree", strlen(elementName)) != 0) {
                continue;
            }

            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            if (strncmp(attributeName, "Bus Information", strlen(attributeName)) != 0) {
                continue;
            }

            treeElement = element;
        }

        if (treeElement == nullptr) {
            return false;
        }

        // Read out data.
        for (XMLElement const *element = treeElement->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "e".
            const char *elementName = element->Name();
            if (strncmp(elementName, "e", strlen(elementName)) != 0) {
                continue;
            }

            // Get the name and read out data based on this.
            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            const char *elementValue = element->GetText();

            if (strncmp(attributeName, "LIN1 Bit-rate", strlen(attributeName)) == 0) {
                uint32_t bitRate;

                if (elementValue == nullptr) {
                    bitRate = 0;
                } else {
                    bitRate = strtoul(elementValue, nullptr, 10);
                }

                fileInfo.BitrateLIN1 = bitRate;
            } else if (strncmp(attributeName, "LIN2 Bit-rate", strlen(attributeName)) == 0) {
                uint32_t bitRate;

                if (elementValue == nullptr) {
                    bitRate = 0;
                } else {
                    bitRate = strtoul(elementValue, nullptr, 10);
                }

                fileInfo.BitrateLIN2 = bitRate;
            }
        }

        return true;
    }

    bool getCANInfo(FileInfo &fileInfo, std::string xml) {
        // Parse using tinyxml2.
        XMLDocument doc;
        XMLError errorStatus = doc.Parse(xml.c_str(), xml.length());

        if (errorStatus != XML_SUCCESS) {
            return false;
        }

        // Find the correct node.
        // The path we are looking for is: "SIcomment.common_properties.tree".
        XMLElement const *rootElement = doc.RootElement();
        const char *rootName = rootElement->Name();

        if (strncmp(rootName, "SIcomment", strlen(rootName)) != 0) {
            return false;
        }

        // Lookup common properties.
        XMLElement const *cpElement = rootElement->FirstChildElement("common_properties");
        if (cpElement == nullptr) {
            return false;
        }
        if (cpElement->NoChildren()) {
            return false;
        }

        // Find a tree with the correct name.
        XMLElement const *treeElement = nullptr;

        for (XMLElement const *element = cpElement->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "tree" and the name matches.
            const char *elementName = element->Name();
            if (strncmp(elementName, "tree", strlen(elementName)) != 0) {
                continue;
            }

            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            if (strncmp(attributeName, "Bus Information", strlen(attributeName)) != 0) {
                continue;
            }

            treeElement = element;
        }

        if (treeElement == nullptr) {
            return false;
        }

        // Read out data.
        for (XMLElement const *element = treeElement->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "e".
            const char *elementName = element->Name();
            if (strncmp(elementName, "e", strlen(elementName)) != 0) {
                continue;
            }

            // Get the name and read out data based on this.
            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            const char *elementValue = element->GetText();

            if (strncmp(attributeName, "CAN1 Bit-rate", strlen(attributeName)) == 0) {
                uint32_t bitRate;

                if (elementValue == nullptr) {
                    bitRate = 0;
                } else {
                    bitRate = strtoul(elementValue, nullptr, 10);
                }

                fileInfo.BitrateCAN1 = bitRate;
            } else if (strncmp(attributeName, "CAN2 Bit-rate", strlen(attributeName)) == 0) {
                uint32_t bitRate;

                if (elementValue == nullptr) {
                    bitRate = 0;
                } else {
                    bitRate = strtoul(elementValue, nullptr, 10);
                }

                fileInfo.BitrateCAN2 = bitRate;
            }
        }

        return true;
    }

    bool parseDeviceInformation(FileInfo &fileInfo, XMLElement const *treeRoot) {
        // Loop over all the attributes, and map their properties to the file information.
        for (XMLElement const *element = treeRoot->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "e".
            const char *elementName = element->Name();
            if (strncmp(elementName, "e", strlen(elementName)) != 0) {
                continue;
            }

            // Get the name and read out data based on this.
            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            const char *elementValue = element->GetText();

            if (strncmp(attributeName, "firmware version", strlen(attributeName)) == 0) {
                if (elementValue == nullptr) {
                    return false;
                }

                Version fwVersion;
                unsigned int patch;
                auto scanRes = sscanf(elementValue, "%2u.%2u.%2u", &fwVersion.major, &fwVersion.minor, &patch);

                // Ensure the correct number of fields were read.
                if (scanRes != 3) {
                    return false;
                }

                // Insert the optional value.
                fwVersion.patch = patch;

                fileInfo.FW_Version = fwVersion;
            } else if (strncmp(attributeName, "hardware version", strlen(attributeName)) == 0) {
                if (elementValue == nullptr) {
                    return false;
                }

                Version hwVersion;
                auto scanRes = sscanf(elementValue, "%2u.%2u", &hwVersion.major, &hwVersion.minor);

                // Ensure the correct number of fields were read.
                if (scanRes != 2) {
                    return false;
                }

                fileInfo.HW_Version = hwVersion;
            } else if (strncmp(attributeName, "serial number", strlen(attributeName)) == 0) {
                fileInfo.LoggerID = strtoul(elementValue, nullptr, 16);
            } else if (strncmp(attributeName, "device type", strlen(attributeName)) == 0) {
                fileInfo.LoggerType = strtoul(elementValue, nullptr, 16);
            }
        }

        return true;
    }

    bool parseFileInformation(FileInfo &fileInfo, XMLElement const *treeRoot) {
        // Loop over all the attributes, and map their properties to the file information.
        for (XMLElement const *element = treeRoot->FirstChildElement();
             element != nullptr; element = element->NextSiblingElement()) {
            // Ensure the node type is "e".
            const char *elementName = element->Name();
            if (strncmp(elementName, "e", strlen(elementName)) != 0) {
                continue;
            }

            // Get the name and read out data based on this.
            XMLAttribute const *nameAttribute = element->FindAttribute("name");
            if (nameAttribute == nullptr) {
                continue;
            }

            const char *attributeName = nameAttribute->Value();
            const char *elementValue = element->GetText();

            if (strncmp(attributeName, "session", strlen(attributeName)) == 0) {
                if (elementValue == nullptr) {
                    return false;
                }

                fileInfo.Session = strtoul(elementValue, nullptr, 10);
            } else if (strncmp(attributeName, "split", strlen(attributeName)) == 0) {
                if (elementValue == nullptr) {
                    return false;
                }

                fileInfo.Split = strtoul(elementValue, nullptr, 10);
            } else if (strncmp(attributeName, "comment", strlen(attributeName)) == 0) {
                if (elementValue == nullptr) {
                    fileInfo.Comment[0] = '\0';
                } else {
                    strncpy(fileInfo.Comment, elementValue, sizeof(fileInfo.Comment));
                }
            }
        }

        return true;
    }*/

}

ext: cpp


text:
// ***********************************************************************
//                      DO NOT EDIT THIS FILE !!!
// ***********************************************************************
//   This file is automatically generated from the grib_api templates. All
//   changes will be overridden. If you want to do permanent changes then
//   you should write them into the 'CharacterStringProductImpl.*' files.
// ***********************************************************************

#include "CharacterStringProduct.h"
#include "../../common/GeneralDefinitions.h"
#include "../../common/GeneralFunctions.h"
#include <boost/functional/hash.hpp>
#include <iostream>
#include <macgyver/Exception.h>

namespace SmartMet {
namespace GRIB2 {

/*! \brief The constructor of the class. */

CharacterStringProduct::CharacterStringProduct() {
  try {
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The destructor of the class. */

CharacterStringProduct::~CharacterStringProduct() {
}

/*! \brief The method reads and initializes all data related to the current object.

        \param memoryReader  This object controls the access to the memory mapped file.
*/

void CharacterStringProduct::read(MemoryReader &memoryReader) {
  try {
    mParameterCategory = memoryReader.read_UInt8_opt();
    mParameterNumber = memoryReader.read_UInt8_opt();
    mNumberOfCharacters = memoryReader.read_UInt32_opt();
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method writes all data related to the current object.

        \param dataWriter  This object is used for writing the object data.
*/

void CharacterStringProduct::write(DataWriter &dataWriter) {
  try {
    dataWriter << mParameterCategory;
    dataWriter << mParameterNumber;
    dataWriter << mNumberOfCharacters;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method is used for collecting the current class attributeList.

    \param prefix         The prefix of the each attributeList parameter.
    \param attributeList  The attributeList storage.
*/

void CharacterStringProduct::getAttributeList(const std::string &prefix, T::AttributeList &attributeList) const {
  try {
    char name[300];
    sprintf(name, "%sCharacterStringProduct.ParameterCategory", prefix.c_str());
    attributeList.addAttribute(name, toString(mParameterCategory));
    sprintf(name, "%sCharacterStringProduct.ParameterNumber", prefix.c_str());
    attributeList.addAttribute(name, toString(mParameterNumber));
    sprintf(name, "%sCharacterStringProduct.NumberOfCharacters", prefix.c_str());
    attributeList.addAttribute(name, toString(mNumberOfCharacters));
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method is used for getting attribute values by their names.

    \param attributeName  The name of the attribute.
    \param attributeValue The value of the attribute (string).
*/

bool CharacterStringProduct::getAttributeValue(const char *attributeName, std::string &attributeValue) const {
  try {
    if (attributeName == nullptr)
      return false;
    if (strcasecmp(attributeName, "ParameterCategory") == 0) {
      attributeValue = toString(mParameterCategory);
      return true;
    }
    if (strcasecmp(attributeName, "ParameterNumber") == 0) {
      attributeValue = toString(mParameterNumber);
      return true;
    }
    if (strcasecmp(attributeName, "NumberOfCharacters") == 0) {
      attributeValue = toString(mNumberOfCharacters);
      return true;
    }
    return false;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method is used for checking if the attribute value matches to the given value.

    \param attributeName  The name of the attribute.
    \param attributeValue The value of the attribute (string).
*/

bool CharacterStringProduct::hasAttributeValue(const char *attributeName, const char *attributeValue) const {
  try {
    if (attributeName == nullptr || attributeValue == nullptr)
      return false;
    if (strcasecmp(attributeName, "ParameterCategory") == 0 && strcasecmp(attributeValue, toString(mParameterCategory).c_str()) == 0)
      return true;
    if (strcasecmp(attributeName, "ParameterNumber") == 0 && strcasecmp(attributeValue, toString(mParameterNumber).c_str()) == 0)
      return true;
    if (strcasecmp(attributeName, "NumberOfCharacters") == 0 && strcasecmp(attributeValue, toString(mNumberOfCharacters).c_str()) == 0)
      return true;
    return false;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method prints the content of the current object into the given stream.

    \param ostream      The output stream.
    \param level        The print level (used when printing multi-level structures).
    \param optionFlags  The printing options expressed in flag-bits.
*/

void CharacterStringProduct::print(std::ostream &stream, uint level, uint optionFlags) const {
  try {
    stream << space(level) << "CharacterStringProduct\n";
    stream << space(level) << "- ParameterCategory = " << toString(mParameterCategory) << "\n";
    stream << space(level) << "- ParameterNumber = " << toString(mParameterNumber) << "\n";
    stream << space(level) << "- NumberOfCharacters = " << toString(mNumberOfCharacters) << "\n";
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method counts the hash value of the current object. */

T::Hash CharacterStringProduct::countHash() const {
  try {
    std::size_t seed = 0;
    if (mParameterCategory)
      boost::hash_combine(seed, *mParameterCategory);
    if (mParameterNumber)
      boost::hash_combine(seed, *mParameterNumber);
    if (mNumberOfCharacters)
      boost::hash_combine(seed, *mNumberOfCharacters);
    return seed;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method return the template number of the current class. */

uint CharacterStringProduct::getTemplateNumber() const {
  return 254;
}

ProductDefinition *CharacterStringProduct::createProductDefinition() const {
  try {
    return static_cast<ProductDefinition *>(new CharacterStringProduct(*this));
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method returns the value of the {@link mParameterCategory} attribute. */

const T::UInt8_opt &CharacterStringProduct::getParameterCategory() const {
  try {
    return mParameterCategory;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method returns the value of the {@link mParameterNumber} attribute. */

const T::UInt8_opt &CharacterStringProduct::getParameterNumber() const {
  try {
    return mParameterNumber;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

/*! \brief The method returns the value of the {@link mNumberOfCharacters} attribute. */

const T::UInt32_opt &CharacterStringProduct::getNumberOfCharacters() const {
  try {
    return mNumberOfCharacters;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

void CharacterStringProduct::setParameterCategory(T::UInt8_opt parameterCategory) {
  try {
    mParameterCategory = parameterCategory;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

void CharacterStringProduct::setParameterNumber(T::UInt8_opt parameterNumber) {
  try {
    mParameterNumber = parameterNumber;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

void CharacterStringProduct::setNumberOfCharacters(T::UInt32_opt numberOfCharacters) {
  try {
    mNumberOfCharacters = numberOfCharacters;
  } catch (...) {
    throw Fmi::Exception(BCP, "Operation failed", nullptr);
  }
}

} // namespace GRIB2
} // namespace SmartMet

max_stars_repo_name: fmidev/smartmet-library-grid-files

max_issues_repo_name: fmidev/smartmet-library-grid-files

max_forks_repo_name: fmidev/smartmet-library-grid-files


text:
// Copyright (c) 2014-2018 Bauhaus-Universitaet Weimar
// This Software is distributed under the Modified BSD License, see license.txt.
//
// Virtual Reality and Visualization Research Group
// Faculty of Media, Bauhaus-Universitaet Weimar
// http://www.uni-weimar.de/medien/vr

#include "lamure/pvs/utils.h"
#include <boost/filesystem.hpp>

namespace {

scm::math::mat4f loadMatrix(const std::string& filename)
{

    std::ifstream f(filename);
    if (!f.is_open()) {
        std::cerr << "Unable to open transformation file: \""
            << filename << "\"\n";
        return scm::math::mat4f::identity();
    }
    scm::math::mat4f mat = scm::math::mat4f::identity();
    std::string matrix_values_string;
    std::getline(f, matrix_values_string);
    std::stringstream sstr(matrix_values_string);
    for (int i = 0; i < 16; ++i)
        sstr >> mat[i];
    return scm::math::transpose(mat);
}

}

std::pair< std::vector<std::string>, std::vector<scm::math::mat4f> >
read_model_string(std::string const& path_to_resource_file,
                std::set<lamure::model_t>* visible_set,
                std::set<lamure::model_t>* invisible_set)
{
    namespace fs = boost::filesystem;

    std::pair< std::vector<std::string>, std::vector<scm::math::mat4f> > model_vec_pair;

    auto filename = fs::canonical(path_to_resource_file);

    std::ifstream resource_file(filename.string());

    std::string one_line;

    if(resource_file.is_open() )
    {
        lamure::model_t model_id = 0;

        while(std::getline(resource_file, one_line))
        {

            std::istringstream model_ss(one_line);

            if(one_line.length() >= 2)
            {
                if( !(one_line.substr(0,2) == "//") )
                {
                  std::string model_path;
                  scm::math::mat4f model_transf = scm::math::mat4f::identity();

                  model_ss >> model_path;

                  while (model_ss.rdbuf()->in_avail() != 0)
                  {

                    std::string dollar;

                    model_ss >> dollar;

                    if (dollar.size() == 0) {
                        continue;
                    }

                    if (dollar[0] == '$') {
                        switch (dollar[1]) {
                            case 'v':
                                (*visible_set).insert(model_id);
                                break;

                            case 'i':
                                (*invisible_set).insert(model_id);
                                break;

                            default: break;

                        }
                    }
                    else {
                        auto tranf_filename = fs::absolute(fs::path(dollar), filename.parent_path());
                        model_transf = loadMatrix(tranf_filename.string());
                    }
                  }

                  auto model_filename = fs::canonical(fs::path(model_path), filename.parent_path());

                  model_vec_pair.first.push_back(model_filename.string());
                  model_vec_pair.second.push_back(model_transf);

                  ++model_id;
                }
            }

        }

        resource_file.close();
    }
    else
    {
        std::cerr << "Could not open resource file: \""
                  << filename.string() << "\"\n";
    }

    return model_vec_pair;

}

void create_scene_name_from_vector(std::vector<std::string> const& name_vector, std::string& name)
{

    std::string n = "";

    //pattern before and after the real name of the model in the resources file
    std::string pattern_before_substring = "/";
    std::string pattern_after_substring  = ".kdn";

    for(std::vector<std::string>::const_iterator it = name_vector.begin(); it != name_vector.end(); ++it )
    {
        int pos_after_first_pattern= (*it).find_last_of(pattern_before_substring) + 1;
        int substring_length = ( ( (*it).find_last_of(pattern_after_substring) - pattern_after_substring.length() ) + 1 ) - pos_after_first_pattern;

        n += (*it).substr(pos_after_first_pattern , substring_length);

        if(it != (name_vector.end()-1) )
            n += "_&_";
    }

    name = n;
}

void create_scene_name_from_camera_session_file(std::string const& session_file, std::string& name)
{

    //pattern before and after the real name of the model in the resources file
    std::string pattern_before_substring = "/";
    std::string pattern_after_substring  = ".csn";

        int pos_after_first_pattern= session_file.find_last_of(pattern_before_substring) + 1;
        int substring_length = ( ( session_file.find_last_of(pattern_after_substring) - pattern_after_substring.length() ) + 1 ) - pos_after_first_pattern;

    name = (session_file).substr(pos_after_first_pattern , substring_length);
}