Commit b256feb9 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Extended TextFileParser to support nested dictionaries.

Adapted CsvdImageReader and MhdImageReader to the new API.
parent be17daa3
......@@ -24,34 +24,43 @@
#include "textfileparser.h"
#include <stack>
#include <sstream>
namespace campvis {
TextFileParser::TextFileParser(const std::string& url, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace /*= " \t"*/)
: _url(url)
TextFileParser::TextFileParser(std::istream& stream, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace /*= " \t"*/)
: _stream(stream)
, _caseSensitiveKeys(caseSensitiveKeys)
, _delimiters(delimiters)
, _whitespace(whitespace)
, _rootGroup(nullptr)
{
}
bool TextFileParser::hasKey(const std::string& key) const {
const TextFileParser::TokenGroup* TextFileParser::getRootGroup() const {
return _rootGroup;
}
TextFileParser::~TextFileParser() {
delete _rootGroup;
}
bool TextFileParser::TokenGroup::hasKey(const std::string& key) const {
return (_tokens.find(key) != _tokens.end());
}
const std::string& TextFileParser::getString(const std::string& key) const throw (cgt::CorruptedFileException) {
const std::string& TextFileParser::TokenGroup::getString(const std::string& key) const throw (cgt::Exception) {
std::map<std::string, std::string>::const_iterator it = (_caseSensitiveKeys ? _tokens.find(key) : _tokens.find(StringUtils::lowercase(key)));
if (it == _tokens.end()) {
throw cgt::CorruptedFileException("No token with key " + key + " found.", _url);
throw cgt::Exception("No token with key " + key + " found.");
}
else {
return it->second;
}
}
bool TextFileParser::getBool(const std::string& key) const throw (cgt::CorruptedFileException) {
bool TextFileParser::TokenGroup::getBool(const std::string& key) const throw (cgt::Exception) {
std::string lc = StringUtils::lowercase(getString(key));
if ((lc == "0") || (lc == "false"))
......@@ -59,144 +68,171 @@ namespace campvis {
else if ((lc == "1") || (lc == "true"))
return true;
else
throw cgt::CorruptedFileException("Error parsing key " + key + " to bool.", _url);
throw cgt::Exception("Error parsing key " + key + " to bool.");
}
int TextFileParser::getInt(const std::string& key) const throw (cgt::CorruptedFileException) {
int TextFileParser::TokenGroup::getInt(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<int>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to int: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to int: " + e.what());
}
}
cgt::ivec2 TextFileParser::getIvec2(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::ivec2 TextFileParser::TokenGroup::getIvec2(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::ivec2>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to ivec2: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to ivec2: " + e.what());
}
}
cgt::ivec3 TextFileParser::getIvec3(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::ivec3 TextFileParser::TokenGroup::getIvec3(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::ivec3>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to ivec3: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to ivec3: " + e.what());
}
}
cgt::ivec4 TextFileParser::getIvec4(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::ivec4 TextFileParser::TokenGroup::getIvec4(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::ivec4>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to ivec4: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to ivec4: " + e.what());
}
}
size_t TextFileParser::getSizeT(const std::string& key) const throw (cgt::CorruptedFileException) {
size_t TextFileParser::TokenGroup::getSizeT(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<size_t>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to size_t: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to size_t: " + e.what());
}
}
cgt::svec2 TextFileParser::getSvec2(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::svec2 TextFileParser::TokenGroup::getSvec2(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::svec2>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to svec2: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to svec2: " + e.what());
}
}
cgt::svec3 TextFileParser::getSvec3(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::svec3 TextFileParser::TokenGroup::getSvec3(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::svec3>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to svec3: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to svec3: " + e.what());
}
}
cgt::svec4 TextFileParser::getSvec4(const std::string& key) const throw (cgt::CorruptedFileException){
cgt::svec4 TextFileParser::TokenGroup::getSvec4(const std::string& key) const throw (cgt::Exception){
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::svec4>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to svec4: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to svec4: " + e.what());
}
}
float TextFileParser::getFloat(const std::string& key) const throw (cgt::CorruptedFileException) {
float TextFileParser::TokenGroup::getFloat(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<float>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to float: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to float: " + e.what());
}
}
cgt::vec2 TextFileParser::getVec2(const std::string& key) const throw (cgt::CorruptedFileException) {
cgt::vec2 TextFileParser::TokenGroup::getVec2(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::vec2>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to vec2: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to vec2: " + e.what());
}
}
cgt::vec3 TextFileParser::getVec3(const std::string& key) const throw (cgt::CorruptedFileException) {
cgt::vec3 TextFileParser::TokenGroup::getVec3(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::vec3>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to vec3: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to vec3: " + e.what());
}
}
cgt::vec4 TextFileParser::getVec4(const std::string& key) const throw (cgt::CorruptedFileException) {
cgt::vec4 TextFileParser::TokenGroup::getVec4(const std::string& key) const throw (cgt::Exception) {
std::string str = getString(key);
try {
return StringUtils::fromString<cgt::vec4>(str);
}
catch (std::exception& e) {
throw cgt::CorruptedFileException("Error parsing key " + key + " to vec4: " + e.what(), _url);
throw cgt::Exception("Error parsing key " + key + " to vec4: " + e.what());
}
}
// ================================================================================================
template<>
std::vector<std::string> TextFileParser::readAndParseItems<TextFileParser::ItemSeparatorLines>() const throw (cgt::FileException, cgt::CorruptedFileException) {
cgt::File* file = FileSys.open(_url);
if (!file || !file->isOpen())
throw cgt::FileException("Could not open file " + _url + " for reading.", _url);
std::vector<std::string> lines;
while (!file->eof()) {
lines.push_back(file->getLine());
TextFileParser::TokenGroup::~TokenGroup() {
for (auto it = _tokenGroups.begin(); it != _tokenGroups.end(); ++it)
delete it->second;
}
TextFileParser::TokenGroup* TextFileParser::ItemSeparatorLines::operator()(std::istream& stream, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace) {
TokenGroup* rootNode = new TokenGroup();
std::stack<TokenGroup*> groupHierarchy;
groupHierarchy.push(rootNode);
std::string currentLine;
while (stream.good() && !stream.eof()) {
std::getline(stream, currentLine);
currentLine = StringUtils::trim(currentLine);
if (currentLine == "")
continue;
if (currentLine.substr(currentLine.length() - 1, 1) == "{") {
std::string groupName = StringUtils::trim(currentLine.substr(0, currentLine.length() - 1));
TokenGroup* tg = new TokenGroup();
groupHierarchy.top()->_tokenGroups.insert(std::make_pair(groupName, tg));
groupHierarchy.push(tg);
}
else if (currentLine == "}") {
groupHierarchy.pop();
}
else {
std::vector<std::string> tokens = StringUtils::split(currentLine, delimiters);
if (tokens.size() == 2) {
if (caseSensitiveKeys)
groupHierarchy.top()->_tokens.insert(std::make_pair(StringUtils::trim(tokens[0], whitespace), StringUtils::trim(tokens[1], whitespace)));
else
groupHierarchy.top()->_tokens.insert(std::make_pair(StringUtils::lowercase(StringUtils::trim(tokens[0], whitespace)), StringUtils::trim(tokens[1], whitespace)));
}
else {
throw cgt::Exception("Error parsing item '" + currentLine + "': expected single delimiter.");
}
}
}
file->close();
delete file;
return lines;
return rootNode;
}
}
\ No newline at end of file
......@@ -34,9 +34,18 @@
#include <map>
#include <functional>
#include <istream>
#include <string>
#include <vector>
namespace {
std::string getLine(std::istream s, char delim) {
std::string toReturn;
std::getline(s, toReturn, delim);
return toReturn;
}
}
namespace campvis {
/**
......@@ -49,23 +58,157 @@ namespace campvis {
*/
class CAMPVIS_CORE_API TextFileParser {
public:
struct CAMPVIS_CORE_API TokenGroup {
public:
~TokenGroup();
/**
* Checks whether there exists a token with the given key \a key.
* \param key The key to search for.
* \return True if a key-value pair with the given key is existent, otherwise false.
*/
bool hasKey(const std::string& key) const;
/**
* Returns the value to the given key \a key.
* \param key The key to search for.
* \return The corresponding value to the given key.
* \throw cgt::Exception if no such key existent.
*/
const std::string& getString(const std::string& key) const throw (cgt::Exception);
/**
* Returns the bool representation of the value for the given key \a key.
* \param key The key to search for.
* \return Boolean representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
bool getBool(const std::string& key) const throw (cgt::Exception);
/**
* Returns the integer representation of the value for the given key \a key.
* \param key The key to search for.
* \return Integer representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
int getInt(const std::string& key) const throw (cgt::Exception);
/**
* Returns the ivec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec2 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::ivec2 getIvec2(const std::string& key) const throw (cgt::Exception);
/**
* Returns the ivec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec3 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::ivec3 getIvec3(const std::string& key) const throw (cgt::Exception);
/**
* Returns the ivec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec4 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::ivec4 getIvec4(const std::string& key) const throw (cgt::Exception);
/**
* Returns the size_t representation of the value for the given key \a key.
* \param key The key to search for.
* \return size_t representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
size_t getSizeT(const std::string& key) const throw (cgt::Exception);
/**
* Returns the svec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec2 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::svec2 getSvec2(const std::string& key) const throw (cgt::Exception);
/**
* Returns the svec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec3 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::svec3 getSvec3(const std::string& key) const throw (cgt::Exception);
/**
* Returns the svec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec4 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::svec4 getSvec4(const std::string& key) const throw (cgt::Exception);
/**
* Returns the float representation of the value for the given key \a key.
* \param key The key to search for.
* \return Float representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
float getFloat(const std::string& key) const throw (cgt::Exception);
/**
* Returns the vec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec2 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::vec2 getVec2(const std::string& key) const throw (cgt::Exception);
/**
* Returns the vec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec3 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::vec3 getVec3(const std::string& key) const throw (cgt::Exception);
/**
* Returns the vec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec4 representation for the corresponding value to the given key.
* \throw cgt::Exception if no such key existent or conversion failed.
*/
cgt::vec4 getVec4(const std::string& key) const throw (cgt::Exception);
std::multimap<std::string, TokenGroup*> _tokenGroups; ///< multimap of token groups
std::map<std::string, std::string> _tokens; ///< map of key-value pairs
private:
bool _caseSensitiveKeys; ///< Flag whether keys are case-sensitive or not
};
/**
* Item separator for letting each line in the file result in one key-value pair item.
* \note The behaviour is not implemented as functor but by template specialization!
*/
struct CAMPVIS_CORE_API ItemSeparatorLines {
// no operator() to implement due to template specialization
TokenGroup* operator()(std::istream& stream, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace);
};
/**
* Creates a new text file parser with the given settings.
* \param url URL of file
* \param stream Input stream to read from
* \param caseSensitiveKeys Flag whether keys are case-sensitive or not
* \param delimiters Set of delimiters for separating key-value pair
* \param whitespace Set of characters identifying whitespace
*/
TextFileParser(const std::string& url, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace = " \t\n\r");
TextFileParser(std::istream& stream, bool caseSensitiveKeys, const std::string& delimiters, const std::string& whitespace = " \t\n\r");
~TextFileParser();
/**
* Performs the parsing of the text file into key-value pairs.
......@@ -74,200 +217,33 @@ namespace campvis {
*
* \sa TextFileParser::ItemSeparatorLines
* \tparam T Functor for splitting the file into items, must implement std::unary_function<std::string, std::vector<std::string> >
* \throw cgt::FileException if file not found/not readable, cgt::CorruptedFileException on parsing errors during key-value pair parsing.
* \throw cgt::FileException if file not found/not readable, cgt::Exception on parsing errors during key-value pair parsing.
*/
template<class T>
void parse() throw (cgt::FileException, cgt::CorruptedFileException);
/**
* Checks whether there exists a token with the given key \a key.
* \param key The key to search for.
* \return True if a key-value pair with the given key is existent, otherwise false.
*/
bool hasKey(const std::string& key) const;
/**
* Returns the value to the given key \a key.
* \param key The key to search for.
* \return The corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent.
*/
const std::string& getString(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the bool representation of the value for the given key \a key.
* \param key The key to search for.
* \return Boolean representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
bool getBool(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the integer representation of the value for the given key \a key.
* \param key The key to search for.
* \return Integer representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
int getInt(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the ivec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec2 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::ivec2 getIvec2(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the ivec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec3 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::ivec3 getIvec3(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the ivec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return ivec4 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::ivec4 getIvec4(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the size_t representation of the value for the given key \a key.
* \param key The key to search for.
* \return size_t representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
size_t getSizeT(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the svec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec2 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::svec2 getSvec2(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the svec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec3 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::svec3 getSvec3(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the svec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return svec4 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::svec4 getSvec4(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the float representation of the value for the given key \a key.
* \param key The key to search for.
* \return Float representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
float getFloat(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the vec2 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec2 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::vec2 getVec2(const std::string& key) const throw (cgt::CorruptedFileException);
/**
* Returns the vec3 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec3 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::vec3 getVec3(const std::string& key) const throw (cgt::CorruptedFileException);
void parse() throw (cgt::FileException, cgt::Exception);
/**
* Returns the vec4 representation of the value for the given key \a key.
* \param key The key to search for.
* \return vec4 representation for the corresponding value to the given key.
* \throw cgt::CorruptedFileException if no such key existent or conversion failed.
*/
cgt::vec4 getVec4(const std::string& key) const throw (cgt::CorruptedFileException);
const TokenGroup* getRootGroup() const;
protected:
/**
* Loads the text file and parses it into items.
* Item parsing is done using the Functor of the template parameter.
*
* \sa TextFileParser::ItemSeparatorLines
* \tparam T Functor for splitting the file into items, must implement std::unary_function<std::string, std::vector<std::string> >
* \throw cgt::FileException if file not found/not readable, cgt::CorruptedFileException on parsing errors during key-value pair parsing.
*/
template<class T>
inline std::vector<std::string> readAndParseItems() const throw (cgt::FileException, cgt::CorruptedFileException) ;
std::string _url; ///< URL of file
std::istream& _stream; ///< Input stream to read from
bool _caseSensitiveKeys; ///< Flag whether keys are case-sensitive or not
std::string _delimiters; ///< Set of delimiters for separating key-value pair
std::string _whitespace; ///< Set of characters identifying whitespace
std::map<std::string, std::string> _tokens; ///< map of key-value pairs
TokenGroup* _rootGroup; ///< root token group
};