Commit 0693cc8a authored by Joseph Mirabel's avatar Joseph Mirabel Committed by Joseph Mirabel
Browse files

Adding a XML parser.

parent b3489362
......@@ -50,11 +50,16 @@ SET(${PROJECT_NAME}_HEADERS
include/hpp/util/portability.hh
include/hpp/util/timer.hh
include/hpp/util/version.hh
include/hpp/util/parser.hh
include/hpp/util/factories/ignoretag.hh
include/hpp/util/factories/sequence.hh
)
# Add Boost path to include directories.
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
ADD_REQUIRED_DEPENDENCY("tinyxml >= 2.6")
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(tests)
......
// Copyright (c) 2014, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-manipulation-urdf.
// hpp-manipulation-urdf is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-manipulation-urdf is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-manipulation-urdf. If not, see <http://www.gnu.org/licenses/>.
#ifndef HPP_UTIL_FACTORIES_IGNORETAG_HH
# define HPP_UTIL_FACTORIES_IGNORETAG_HH
# include "hpp/util/parser.hh"
namespace hpp {
namespace util {
namespace parser {
/// \addtogroup factories
/// \{
/// Class used to ignore a tag.
/// If the parser knows it should ignore a tag, no warning will be
/// printed in the logs. Moreover, its children won't be parsed.
class IgnoreTagFactory : public ObjectFactory {
public:
IgnoreTagFactory (ObjectFactory* parent, const XMLElement* element) :
ObjectFactory (parent, element) {}
bool init ()
{
return false;
}
void impl_write (XMLElement* element) const
{
element->InsertEndChild (XMLComment ("This tag was ignored"));
}
};
/// \}
} // namespace parser
} // namespace manipulation
} // namespace hpp
#endif // HPP_UTIL_FACTORIES_IGNORETAG_HH
// Copyright (c) 2014, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-manipulation-urdf.
// hpp-manipulation-urdf is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-manipulation-urdf is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-manipulation-urdf. If not, see <http://www.gnu.org/licenses/>.
#ifndef HPP_UTIL_FACTORIES_SEQUENCE_HH
# define HPP_UTIL_FACTORIES_SEQUENCE_HH
# include "hpp/util/parser.hh"
# include <vector>
namespace hpp {
namespace util {
namespace parser {
/// \addtogroup factories
/// \{
/// \brief Factory parsing sequence of values.
/// \tparam ValueType one of (bool, int, unsigned int, double, float)
///
/// A std::vector is built from a sequence of values separeted by
/// white spaces.
template <typename ValueType>
class SequenceFactory : public ObjectFactory {
public:
typedef std::vector <ValueType> OutType;
SequenceFactory (ObjectFactory* parent, const XMLElement* element, const unsigned int nbValue = 0) :
ObjectFactory (parent, element), size_ (nbValue)
{}
virtual void addTextChild (const XMLText* text);
const OutType& values () const
{
return values_;
}
SequenceFactory (const std::string& tagName, ObjectFactory* parent = NULL)
: ObjectFactory (tagName, parent)
{}
void values (const OutType& v)
{
values_ = v;
}
protected:
virtual void impl_write (XMLElement* element) const;
private:
std::vector <ValueType> values_;
unsigned int size_;
};
/// \}
} // namespace parser
} // namespace manipulation
} // namespace hpp
#endif // HPP_UTIL_FACTORIES_SEQUENCE_HH
// Copyright (c) 2014, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-manipulation-urdf.
// hpp-manipulation-urdf is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-manipulation-urdf is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-manipulation-urdf. If not, see <http://www.gnu.org/licenses/>.
#ifndef HPP_MANIPULATION_PARSER_HH
# define HPP_MANIPULATION_PARSER_HH
# include <map>
# include <list>
# include <string>
# include <iostream>
# include <tinyxml.h>
namespace hpp {
namespace util {
namespace parser {
typedef TiXmlElement XMLElement;
typedef TiXmlDocument XMLDocument;
typedef TiXmlDeclaration XMLDeclaration;
typedef TiXmlAttribute XMLAttribute;
typedef TiXmlNode XMLNode;
typedef TiXmlText XMLText;
typedef TiXmlComment XMLComment;
typedef TiXmlPrinter XMLPrinter;
class RootFactory;
/// \defgroup factories
///
/// \brief Classes used to build object from XML documents.
///
/// See section \ref hpp_manipulation_urdf_extend_sec for more information
/// about how to extend the parser with factories.
/// \addtogroup factories
/// \{
/// \brief Class that catch XML Parser events for a specific tag and build the corresponding
/// Object.
///
/// Derive this class if you wish to extend the Parser.
/// The event callbacks are called in the following order:
/// \li ObjectFactory::init after having created the object.
/// \li ObjectFactory::setAttribute for each attribute of the tag.
/// \li ObjectFactory::finishAttributes after having processed every attribute.
/// \li ObjectFactory::addTextChild when a child is a text element.
/// \li ObjectFactory::finishTags when all the children have been parsed.
/// \li ObjectFactory::finishFile when the file has been fully parsed.
///
/// \note The derived class must have the following construtor
/// \code
/// DerivedFactory (ObjectFactory* parent, const XMLElement* element) :
/// ObjectFactory (parent, element)
/// {
/// /*
/// * Keep in mind that it might be more convenient
/// * to build objects in an event callback, when some information
/// * has already been parsed.
/// */
/// }
/// \endcode
class ObjectFactory {
public:
typedef std::list <ObjectFactory*> ObjectFactoryList;
ObjectFactory (ObjectFactory* parent = NULL, const XMLElement* element = NULL);
/// \name Events
/// \{
/// Called when the object is created.
/// \return True to continue parsing this tag, False otherwise.
virtual bool init ();
/// Called for each attribute.
/// A few reserved name are automatocally catched. The reserved names are
/// "name" and "id".
/// "name" expects a string.
/// "id" expects an unsigned integer and can be use to define pointers to
/// elements.
void setAttribute (const XMLAttribute* attr);
/// Add Text child.
virtual void addTextChild (const XMLText* text);
/// Called when all the attributes have been processed.
/// \return True to continue parsing this tag, False otherwise.
virtual bool finishAttributes ();
/// Called when all the child tags have been processed.
virtual void finishTags ();
/// Called when parsing is finished.
virtual void finishFile ();
/// \}
/// \name Write to file
/// \{
/// Constructor for writing objects from scratch
ObjectFactory (const std::string& tagName, ObjectFactory* parent = NULL);
/// Add an attribute
void addAttribute (const std::string& name, const std::string& value);
/// Add this factory as child of the node argument.
/// Tags are handled throught children so you should add children
/// before calling this function.
/// If you factory must write something different from tags (XMLText
/// or XMLComment), reimplement method impl_write.
XMLNode* write (XMLNode* node) const;
/// \}
/// \name Accessors
/// \{
/// Return tag name of the element is any.
/// Returns "No element" otherwise.
std::string tagName () const;
/// Return the content of the attribute name, or an
/// empty string.
std::string name () const;
/// Check if an attribute was set.
bool hasAttribute (const std::string& attr) const;
/// Return a given attributes.
std::string getAttribute (const std::string& attr) const;
/// Get a list of ObjectFactory whose tag name is type.
ObjectFactoryList getChildrenOfType (std::string type);
/// Get the ObjectFactory whose tag name is type.
/// \param[out] o Set to the first element of the requested type.
/// \return true if there was only element of the requested type. false if there are more than one.
/// \throws std::invalid_argument if no ObjectFactory of the requested type exists.
bool getChildOfType (std::string type, ObjectFactory*& o);
/// \}
/// Set the name.
/// The default value is the value of the attribute "name"
/// of the XML tag or an empty string if this does not exist.
void name (const std::string& n);
/// See name(const std::string&)
void name (const char* n);
/// Cast this class to any child class.
template <typename T> T* as ()
{
return static_cast <T*> (this);
}
protected:
ObjectFactory (ObjectFactory* root);
ObjectFactory* parent ();
virtual ObjectFactory* root ();
bool hasParent () const;
const XMLElement* XMLelement ();
virtual void impl_setAttribute (const XMLAttribute* attr);
virtual void impl_write (XMLElement* element) const;
void addChild (ObjectFactory* child);
virtual std::ostream& print (std::ostream& os) const;
private:
ObjectFactory* parent_;
ObjectFactory* root_;
typedef std::map <std::string, ObjectFactoryList > ChildrenMap;
ChildrenMap children_;
const XMLElement* element_;
typedef std::map <std::string, std::string> AttributeMap;
AttributeMap attrMap_;
std::string name_, tagName_;
int id_;
friend std::ostream& operator<< (std::ostream&, const ObjectFactory&);
};
/// \}
/// To add a ObjectFactory to the Parser, use:
/// Parser::addObjectFactory (TagName, create <ObjectFactory>)
template <typename T>
ObjectFactory* create (ObjectFactory* parent = NULL, const XMLElement* element = NULL)
{
return new T (parent, element);
}
/// \brief Parse an XML document
///
/// This class uses the tinyXML library and derived classes of ObjectFactory
/// to build object from an XML document.
/// To extend its capabilities, see ObjectFactory.
class Parser {
public:
typedef ObjectFactory* (*FactoryType) (ObjectFactory*, const XMLElement*);
/// Constructor
/// \param defaultFactory The factory used when a tag is not known.
Parser (FactoryType defaultFactory = create <ObjectFactory>);
virtual ~Parser ();
void addObjectFactory (const std::string& tagname, FactoryType factory);
virtual void parse (const char* xmlString);
virtual void parseFile (const std::string& filename);
ObjectFactory* root () const;
private:
XMLDocument doc_;
ObjectFactory* root_;
bool checkError () const;
void loadString (const char* xmlstring);
void parse ();
void parseElement (const XMLElement* element, ObjectFactory* parent);
typedef std::map <std::string, FactoryType> ObjectFactoryMap;
typedef std::pair <std::string, FactoryType> ObjectFactoryPair;
typedef std::pair <ObjectFactoryMap::iterator, bool> ObjectFactoryInsertRet;
ObjectFactoryMap objFactoryMap_;
FactoryType defaultFactory_;
typedef std::list <ObjectFactory*> ObjectFactoryList;
ObjectFactoryList objectFactories_;
std::ostream& print (std::ostream&) const;
friend std::ostream& operator<< (std::ostream&, const Parser&);
};
std::ostream& operator<< (std::ostream&, const ObjectFactory&);
std::ostream& operator<< (std::ostream&, const Parser&);
} // namespace parser
} // namespace manipulation
} // namespace hpp
#endif // HPP_MANIPULATION_PARSER_HH
......@@ -39,6 +39,8 @@ ADD_LIBRARY(hpp-util
indent.cc
timer.cc
version.cc
parser.cc
factories/sequence.cc
)
# Set shared library version.
......@@ -47,4 +49,6 @@ SET_TARGET_PROPERTIES(hpp-util PROPERTIES SOVERSION ${PROJECT_VERSION})
# Link against Boost libraries.
TARGET_LINK_LIBRARIES(hpp-util ${Boost_LIBRARIES})
PKG_CONFIG_USE_DEPENDENCY(hpp-util tinyxml)
INSTALL(TARGETS hpp-util DESTINATION ${CMAKE_INSTALL_LIBDIR})
// Copyright (c) 2014, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-manipulation-urdf.
// hpp-manipulation-urdf is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-manipulation-urdf is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-manipulation-urdf. If not, see <http://www.gnu.org/licenses/>.
#include "hpp/util/factories/sequence.hh"
#include <boost/algorithm/string.hpp>
#include <iostream>
#include "hpp/util/debug.hh"
namespace hpp {
namespace util {
namespace parser {
namespace {
struct StringIsEmpty : public std::unary_function<std::string, bool>{
bool operator () (std::string s) const {return s.empty ();}
};
template <typename ValueType> bool cast (const std::string& str, ValueType* val)
{
hppDout (error, "Unkown type.");
return false;
}
template <> bool cast <int> (const std::string& str, int* val)
{
if ( TIXML_SSCANF (str.c_str (), "%d", val) == 1 )
return true;
*val = 0;
return false;
}
template <> bool cast <unsigned int> (const std::string& str, unsigned int* val)
{
if ( TIXML_SSCANF (str.c_str (), "%u", val) == 1 )
return true;
*val = 0;
return false;
}
template <> bool cast <double> (const std::string& str, double* val)
{
if ( TIXML_SSCANF (str.c_str (), "%lf", val) == 1 )
return true;
*val = 0;
return false;
}
template <> bool cast <float> (const std::string& str, float* val)
{
if ( TIXML_SSCANF (str.c_str (), "%f", val) == 1 )
return true;
*val = 0;
return false;
}
template <> bool cast <bool> (const std::string& str, bool* val)
{
int iVal;
if (cast <int> (str, &iVal)) {
*val = (iVal == 0) ? false : true;
return true;
}
if (str.compare ("true") == 0) {
*val = true;
return true;
}
if (str.compare ("false") == 0) {
*val = false;
return true;
}
*val = 0;
return false;
}
template <> bool cast <std::string> (const std::string& str, std::string* val)
{
*val = str;
return true;
}
}
template <typename ValueType>
void SequenceFactory<ValueType>::addTextChild (const XMLText* text)
{
values_.clear ();
std::string t(text->Value ());
typedef std::list<std::string> StringList;
StringList values;
boost::algorithm::split (values, t,
boost::algorithm::is_any_of (" \n\t\r"),
boost::algorithm::token_compress_on);
values.remove_if (StringIsEmpty());
if (size_ > 0 && values.size () != size_) {
throw std::invalid_argument ("Wrong sequence size");
}
ValueType v;
for (StringList::const_iterator it = values.begin ();
it != values.end (); it++) {
if (!cast <ValueType> (*it, &v)) {
hppDout (error, "could not parse value "<< *it);
}
values_.push_back (v);
}
}
template <typename ValueType>
void SequenceFactory<ValueType>::impl_write (XMLElement* element) const
{
std::stringstream ss;
for (typename OutType::const_iterator it = values_.begin ();
it != values_.end (); ++it) {
ss << *it << " ";
}
element->InsertEndChild (XMLText (ss.str()));
}
template class SequenceFactory <bool>;
template class SequenceFactory <int>;
template class SequenceFactory <unsigned int>;
template class SequenceFactory <double>;
template class SequenceFactory <float>;
template class SequenceFactory <std::string>;
} // namespace parser
} // namespace manipulation
} // namespace hpp
// Copyright (c) 2014, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-manipulation-urdf.
// hpp-manipulation-urdf is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-manipulation-urdf is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-manipulation-urdf. If not, see <http://www.gnu.org/licenses/>.
#include "hpp/util/parser.hh"
#include <stdexcept>
#include "hpp/util/debug.hh"
namespace hpp {
namespace util {
namespace parser {
Parser::Parser (FactoryType defaultFactory)
: root_ (NULL), defaultFactory_ (defaultFactory)
{}
Parser::~Parser ()
{
for (ObjectFactoryList::iterator it = objectFactories_.begin ();
it != objectFactories_.end (); ++it)
delete *it;
if (root_ != NULL) delete root_;
}
void Parser::parse (const char* xmlString)
{
doc_ = XMLDocument ("document_from_string.xml");
doc_.Parse (xmlString);
parse ();
}
void Parser::parseFile (const std::string& filename)
{
doc_ = XMLDocument (filename);
doc_.LoadFile ();
parse ();
}