1
0
mirror of https://github.com/aportelli/LatAnalyze.git synced 2025-06-17 14:57:05 +01:00

reintegration of LatCore & folder restructuration

This commit is contained in:
2019-02-10 00:23:36 +00:00
parent 6addec5e14
commit 83d5428c3a
111 changed files with 6974 additions and 315 deletions

212
lib/Io/AsciiFile.cpp Normal file
View File

@ -0,0 +1,212 @@
/*
* AsciiFile.cpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/AsciiFile.hpp>
#include <LatAnalyze/includes.hpp>
using namespace std;
using namespace Latan;
/******************************************************************************
* AsciiFile implementation *
******************************************************************************/
// AsciiParserState constructor ////////////////////////////////////////////////
AsciiFile::AsciiParserState::AsciiParserState(istream *stream, string *name,
IoDataTable *data)
: ParserState<IoDataTable>(stream, name, data)
{
initScanner();
}
// AsciiParserState destructor /////////////////////////////////////////////////
AsciiFile::AsciiParserState::~AsciiParserState(void)
{
destroyScanner();
}
// constructor /////////////////////////////////////////////////////////////////
AsciiFile::AsciiFile(const string &name, const unsigned int mode)
{
open(name, mode);
}
// destructor //////////////////////////////////////////////////////////////////
AsciiFile::~AsciiFile(void)
{
close();
}
// access //////////////////////////////////////////////////////////////////////
void AsciiFile::save(const DMat &m, const std::string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
const auto defaultPrec = fileStream_.precision(defaultDoublePrec);
checkWritability();
isParsed_ = false;
fileStream_ << "#L latan_begin mat " << name << endl;
fileStream_ << m.cols() << endl;
fileStream_ << scientific << m << endl;
fileStream_ << "#L latan_end mat " << endl;
fileStream_.precision(defaultPrec);
}
void AsciiFile::save(const DSample &ds, const std::string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
checkWritability();
isParsed_ = false;
fileStream_ << "#L latan_begin rs_sample " << name << endl;
fileStream_ << ds.size() << endl;
save(ds.matrix(), name + "_data");
fileStream_ << "#L latan_end rs_sample " << endl;
}
void AsciiFile::save(const DMatSample &ms, const std::string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
checkWritability();
isParsed_ = false;
fileStream_ << "#L latan_begin rs_sample " << name << endl;
fileStream_ << ms.size() << endl;
save(ms[central], name + "_C");
for (Index i = 0; i < ms.size(); ++i)
{
save(ms[i], name + "_S_" + strFrom(i));
}
fileStream_ << "#L latan_end rs_sample " << endl;
}
// read first name ////////////////////////////////////////////////////////////
string AsciiFile::getFirstName(void)
{
return load();
}
// tests ///////////////////////////////////////////////////////////////////////
bool AsciiFile::isOpen() const
{
return fileStream_.is_open();
}
// IO //////////////////////////////////////////////////////////////////////////
void AsciiFile::close(void)
{
state_.reset();
if (isOpen())
{
fileStream_.close();
}
name_ = "";
mode_ = Mode::null;
isParsed_ = false;
deleteData();
}
void AsciiFile::open(const string &name, const unsigned int mode)
{
if (isOpen())
{
LATAN_ERROR(Io, "file already opened with name '" + name_ + "'");
}
else
{
ios_base::openmode stdMode = static_cast<ios_base::openmode>(0);
name_ = name;
mode_ = mode;
if (mode & Mode::write)
{
stdMode |= ios::out|ios::trunc;
}
if (mode & Mode::read)
{
stdMode |= ios::in;
}
if (mode & Mode::append)
{
stdMode |= ios::out|ios::app;
}
isParsed_ = false;
fileStream_.open(name_.c_str(), stdMode);
if (mode_ & Mode::read)
{
state_.reset(new AsciiParserState(&fileStream_, &name_, &data_));
}
else
{
state_.reset();
}
}
}
std::string AsciiFile::load(const string &name)
{
if ((mode_ & Mode::read) and (isOpen()))
{
if (!isParsed_)
{
state_->isFirst = true;
parse();
}
if (name.empty())
{
return state_->first;
}
else
{
return name;
}
}
else
{
if (isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened in read mode");
}
else
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened");
}
}
}
// parser //////////////////////////////////////////////////////////////////////
//// Bison/Flex parser declaration
int _Ascii_parse(AsciiFile::AsciiParserState *state);
void AsciiFile::parse()
{
fileStream_.seekg(0);
_Ascii_parse(state_.get());
isParsed_ = true;
}

92
lib/Io/AsciiFile.hpp Normal file
View File

@ -0,0 +1,92 @@
/*
* AsciiFile.hpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Latan_AsciiFile_hpp_
#define Latan_AsciiFile_hpp_
#include <LatAnalyze/Global.hpp>
#include <LatAnalyze/Io/File.hpp>
#include <LatAnalyze/Core/Mat.hpp>
#include <LatAnalyze/Statistics/MatSample.hpp>
#include <LatAnalyze/Core/ParserState.hpp>
BEGIN_LATAN_NAMESPACE
/******************************************************************************
* ASCII datafile class *
******************************************************************************/
class AsciiFile: public File
{
public:
class AsciiParserState: public ParserState<IoDataTable>
{
public:
// constructor
AsciiParserState(std::istream *stream, std::string *name,
IoDataTable *data);
// destructor
virtual ~AsciiParserState(void);
// first element reference
bool isFirst;
std::string first;
// parsing buffers
int intBuf;
DSample dSampleBuf;
DMatSample dMatSampleBuf;
std::queue<DMat> dMatQueue;
std::queue<double> doubleQueue;
private:
// allocation/deallocation functions defined in IoAsciiLexer.lpp
virtual void initScanner(void);
virtual void destroyScanner(void);
};
public:
// constructors
AsciiFile(void) = default;
AsciiFile(const std::string &name, const unsigned int mode);
// destructor
virtual ~AsciiFile(void);
// access
virtual void save(const DMat &m, const std::string &name);
virtual void save(const DSample &ds, const std::string &name);
virtual void save(const DMatSample &ms, const std::string &name);
// read first name
virtual std::string getFirstName(void);
// tests
virtual bool isOpen(void) const;
// IO
virtual void close(void);
virtual void open(const std::string &name, const unsigned int mode);
public:
// default ASCII precision
static const unsigned int defaultDoublePrec = 15;
private:
// IO
virtual std::string load(const std::string &name = "");
// parser
void parse(void);
private:
std::fstream fileStream_;
bool isParsed_{false};
std::unique_ptr<AsciiParserState> state_{nullptr};
};
END_LATAN_NAMESPACE
#endif // Latan_AsciiFile_hpp_

91
lib/Io/AsciiLexer.lpp Normal file
View File

@ -0,0 +1,91 @@
/*
* AsciiLexer.lpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
%option reentrant
%option prefix="_Ascii_"
%option bison-bridge
%option bison-locations
%option noyywrap
%option yylineno
%{
#include <LatAnalyze/Io/AsciiFile.hpp>
#include "AsciiParser.hpp"
using namespace std;
using namespace Latan;
#define YY_EXTRA_TYPE AsciiFile::AsciiParserState*
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line = yylineno;\
yylloc->first_column = yylloc->last_column + 1;\
yylloc->last_column = yylloc->first_column + yyleng - 1;
#define YY_INPUT(buf, result, max_size) \
{ \
(*yyextra->stream).read(buf,max_size);\
result = (*yyextra->stream).gcount();\
}
#define YY_DEBUG 0
#if (YY_DEBUG == 1)
#define RETTOK(tok) cout << #tok << "(" << yytext << ")" << endl; return tok
#else
#define RETTOK(tok) return tok
#endif
%}
DIGIT [0-9]
ALPHA [a-zA-Z_+./-]
SIGN \+|-
EXP e|E
INT {SIGN}?{DIGIT}+
FLOAT {SIGN}?(({DIGIT}+\.{DIGIT}*)|({DIGIT}*\.{DIGIT}+))({EXP}{SIGN}?{INT}+)?
LMARK #L
BLANK [ \t]
%x MARK TYPE
%%
{LMARK} {BEGIN(MARK);}
{INT} {yylval->val_int = strTo<long int>(yytext); RETTOK(INT);}
{FLOAT} {yylval->val_double = strTo<double>(yytext); RETTOK(FLOAT);}
({ALPHA}|{DIGIT})+ {strcpy(yylval->val_str,yytext); RETTOK(ID);}
<MARK>latan_begin {BEGIN(TYPE); RETTOK(OPEN);}
<MARK>latan_end {BEGIN(TYPE); RETTOK(CLOSE);}
<TYPE>mat {BEGIN(INITIAL); RETTOK(MAT);}
<TYPE>rs_sample {BEGIN(INITIAL); RETTOK(SAMPLE);}
<TYPE>rg_state {BEGIN(INITIAL); RETTOK(RG_STATE);}
<*>\r*\n {yylloc->last_column = 0;}
<*>[ \t]
<*>. {yylval->val_char = yytext[0]; RETTOK(ERR);}
%%
void AsciiFile::AsciiParserState::initScanner()
{
yylex_init(&scanner);
yyset_extra(this, scanner);
}
void AsciiFile::AsciiParserState::destroyScanner()
{
yylex_destroy(scanner);
}

183
lib/Io/AsciiParser.ypp Normal file
View File

@ -0,0 +1,183 @@
/*
* AsciiParser.ypp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
%{
#include <LatAnalyze/Global.hpp>
#include <LatAnalyze/Io/AsciiFile.hpp>
#include <LatAnalyze/Core/Mat.hpp>
#include <LatAnalyze/Statistics/MatSample.hpp>
using namespace std;
using namespace Latan;
#define TEST_FIRST(name) \
if (state->isFirst)\
{\
state->first = (name);\
state->isFirst = false;\
}
%}
%pure-parser
%name-prefix "_Ascii_"
%locations
%defines
%error-verbose
%parse-param { Latan::AsciiFile::AsciiParserState* state }
%initial-action {yylloc.last_column = 0;}
%lex-param { void* scanner }
%union
{
long int val_int;
double val_double;
char val_char;
char val_str[256];
}
%token <val_char> ERR
%token <val_double> FLOAT
%token <val_int> INT
%token <val_str> ID
%token OPEN CLOSE MAT SAMPLE RG_STATE
%type <val_str> mat matsample dsample
%{
int _Ascii_lex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner);
void _Ascii_error(YYLTYPE* locp, AsciiFile::AsciiParserState* state,
const char* err)
{
stringstream buf;
buf << *state->streamName << ":" << locp->first_line << ":"\
<< locp->first_column << ": " << err;
LATAN_ERROR(Parsing, buf.str());
}
#define scanner state->scanner
%}
%%
datas:
/* empty string */
| datas data
;
data:
mat
{
TEST_FIRST($1);
(*state->data)[$1].reset(new DMat(state->dMatQueue.front()));
state->dMatQueue.pop();
}
| dsample
{
TEST_FIRST($1);
(*state->data)[$1].reset(new DSample(state->dSampleBuf));
}
| matsample
{
TEST_FIRST($1);
(*state->data)[$1].reset(new DMatSample(state->dMatSampleBuf));
}
;
mat:
OPEN MAT ID INT floats CLOSE MAT
{
const unsigned int nRow = state->doubleQueue.size()/$4, nCol = $4;
Index i, j, r = 0;
if (state->doubleQueue.size() != nRow*nCol)
{
LATAN_ERROR(Size, "matrix '" + *state->streamName + ":" + $3 +
"' has a wrong size");
}
state->dMatQueue.push(DMat(nRow, nCol));
while (!state->doubleQueue.empty())
{
j = r % nCol;
i = (r - j)/nCol;
state->dMatQueue.back()(i, j) = state->doubleQueue.front();
state->doubleQueue.pop();
r++;
}
strcpy($$, $3);
}
;
dsample:
OPEN SAMPLE ID INT mat CLOSE SAMPLE
{
const unsigned int nSample = $4, os = DMatSample::offset;
DMat &m = state->dMatQueue.front();
if (m.rows() != nSample + os)
{
LATAN_ERROR(Size, "double sample '" + *state->streamName + ":"
+ $3 + "' has a wrong size");
}
if (m.cols() != 1)
{
LATAN_ERROR(Size, "double sample '" + *state->streamName + ":"
+ $3 + "' is not stored as a column vector");
}
state->dSampleBuf = m.array();
state->dMatQueue.pop();
strcpy($$, $3);
}
;
matsample:
OPEN SAMPLE ID INT mat mats CLOSE SAMPLE
{
const unsigned int nSample = $4, os = DMatSample::offset;
if (state->dMatQueue.size() != nSample + os)
{
LATAN_ERROR(Size, "matrix sample '" + *state->streamName + ":"
+ $3 + "' has a wrong size");
}
state->dMatSampleBuf.resize(nSample);
state->dMatSampleBuf[central] = state->dMatQueue.front();
state->dMatQueue.pop();
for (unsigned int i = 0; i < nSample; ++i)
{
state->dMatSampleBuf[i] = state->dMatQueue.front();
state->dMatQueue.pop();
}
strcpy($$, $3);
}
;
mats:
mats mat
| mat
;
floats:
floats FLOAT {state->doubleQueue.push($2);}
| floats INT {state->doubleQueue.push(static_cast<double>($2));}
| FLOAT {state->doubleQueue.push($1);}
| INT {state->doubleQueue.push(static_cast<double>($1));}
;

81
lib/Io/BinReader.cpp Normal file
View File

@ -0,0 +1,81 @@
/*
* BinReader.cpp, part of LatAnalyze
*
* Copyright (C) 2015 Antonin Portelli
*
* LatAnalyze is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/BinReader.hpp>
#include <LatAnalyze/includes.hpp>
#if (defined __GNUC__)||(defined __clang__)
#pragma GCC diagnostic ignored "-Wunreachable-code"
#endif
using namespace std;
using namespace Latan;
BinIO::BinIO(string msg, string loc)
: runtime_error("Binary reader error: " + msg + " (" + loc + ")")
{}
/******************************************************************************
* BinReader implementation *
******************************************************************************/
// constructor /////////////////////////////////////////////////////////////////
BinReader::BinReader(const string fileName, const uint32_t endianness,
const bool isColMaj)
{
open(fileName, endianness, isColMaj);
}
// I/O /////////////////////////////////////////////////////////////////////////
void BinReader::open(const string fileName, const uint32_t endianness,
const bool isColMaj)
{
fileName_ = fileName;
endianness_ = endianness;
isColMaj_ = isColMaj;
file_.reset(new ifstream(fileName_, ios::in|ios::binary|ios::ate));
if (!file_->is_open())
{
LATAN_ERROR(Io, "impossible to open file '" + fileName_ + "'");
}
size_ = static_cast<size_t>(file_->tellg());
file_->seekg(0, ios::beg);
}
void BinReader::close(void)
{
file_.reset(nullptr);
}
template <>
std::string BinReader::read(void)
{
std::string s;
char c = 'a';
while (c != '\n')
{
c = read<char>();
if (c != '\n')
{
s.push_back(c);
}
}
return s;
}

196
lib/Io/BinReader.hpp Normal file
View File

@ -0,0 +1,196 @@
/*
* BinReader.hpp, part of LatAnalyze
*
* Copyright (C) 2015 Antonin Portelli
*
* LatAnalyze is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LatAnalyze_BinReader_hpp_
#define LatAnalyze_BinReader_hpp_
#include <LatAnalyze/Global.hpp>
BEGIN_LATAN_NAMESPACE
// I/O exception
class BinIO: public std::runtime_error
{
public:
BinIO(std::string msg, std::string loc);
};
/******************************************************************************
* Byte manipulation utilities *
******************************************************************************/
class Endianness
{
public:
enum: uint32_t
{
little = 0x00000001,
big = 0x01000000,
unknown = 0xffffffff
};
};
class ByteManip
{
public:
static constexpr uint32_t getHostEndianness(void)
{
return ((0xffffffff & 1) == Endianness::little) ? Endianness::little
: (((0xffffffff & 1) == Endianness::big) ? Endianness::big
: Endianness::unknown);
}
template <typename T>
static T swapBytes(const T);
};
/******************************************************************************
* template implementation *
******************************************************************************/
template <typename T>
T ByteManip::swapBytes(const T u)
{
static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");
union
{
T u;
unsigned char u8[sizeof(T)];
} source, dest;
source.u = u;
for (size_t k = 0; k < sizeof(T); ++k)
{
dest.u8[k] = source.u8[sizeof(T) - k - 1];
}
return dest.u;
}
/******************************************************************************
* Utility to read binary files *
******************************************************************************/
class BinReader
{
public:
// constructor
BinReader(void) = default;
BinReader(const std::string fileName,
const uint32_t endianness = ByteManip::getHostEndianness(),
const bool isColMaj = false);
// destructor
virtual ~BinReader(void) = default;
// I/O
void open(const std::string fileName,
const uint32_t endianness = ByteManip::getHostEndianness(),
const bool isColMaj = false);
void close(void);
template <typename T>
void read(T *pt, Index size);
template <typename T>
T read(void);
template <typename T>
MatBase<T> read(const Index nRow, const Index nCol);
private:
std::unique_ptr<std::ifstream> file_{nullptr};
std::string fileName_;
size_t size_;
uint32_t endianness_;
bool isColMaj_;
};
/******************************************************************************
* template implementation *
******************************************************************************/
template <typename T>
void BinReader::read(T *pt, Index n)
{
if (file_ != nullptr)
{
file_->read(reinterpret_cast<char *>(pt),
static_cast<long>(sizeof(T))*n);
if (endianness_ != ByteManip::getHostEndianness())
{
for (Index i = 0; i < n; ++i)
{
pt[i] = ByteManip::swapBytes(pt[i]);
}
}
}
else
{
LATAN_ERROR(Io, "file is not opened");
}
}
template <typename T>
T BinReader::read(void)
{
T x;
if (file_ != nullptr)
{
read(&x, 1);
}
else
{
LATAN_ERROR(Io, "file is not opened");
}
return x;
}
template <>
std::string BinReader::read(void);
template <typename T>
MatBase<T> BinReader::read(const Index nRow, const Index nCol)
{
MatBase<T> m;
// Eigen matrices use column-major ordering
if (isColMaj_)
{
m.resize(nRow, nCol);
}
else
{
m.resize(nCol, nRow);
}
if (file_ != nullptr)
{
read(m.data(), nRow*nCol);
}
else
{
LATAN_ERROR(Io, "file is not opened");
}
if (isColMaj_)
{
return m;
}
else
{
return m.transpose();
}
}
END_LATAN_NAMESPACE
#endif // LatAnalyze_BinReader_hpp_

64
lib/Io/File.cpp Normal file
View File

@ -0,0 +1,64 @@
/*
* File.cpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/File.hpp>
#include <LatAnalyze/includes.hpp>
using namespace std;
using namespace Latan;
/******************************************************************************
* File implementation *
******************************************************************************/
// constructors ////////////////////////////////////////////////////////////////
File::File(const string &name, const unsigned int mode)
: name_(name)
, mode_(mode)
{}
// destructor //////////////////////////////////////////////////////////////////
File::~File(void)
{
deleteData();
}
// access //////////////////////////////////////////////////////////////////////
const string & File::getName(void) const
{
return name_;
}
unsigned int File::getMode(void) const
{
return mode_;
}
// internal functions //////////////////////////////////////////////////////////
void File::deleteData(void)
{
data_.clear();
}
void File::checkWritability(void)
{
if (!((mode_ & Mode::write) or (mode_ & Mode::append)) or !isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not writable");
}
}

123
lib/Io/File.hpp Normal file
View File

@ -0,0 +1,123 @@
/*
* File.hpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Latan_File_hpp_
#define Latan_File_hpp_
#include <LatAnalyze/Global.hpp>
#include <LatAnalyze/Io/IoObject.hpp>
#include <LatAnalyze/Core/Mat.hpp>
#include <LatAnalyze/Statistics/MatSample.hpp>
BEGIN_LATAN_NAMESPACE
/******************************************************************************
* Abstract datafile class *
******************************************************************************/
typedef std::unordered_map<std::string, std::unique_ptr<IoObject>> IoDataTable;
class File
{
public:
class Mode
{
public:
enum
{
null = 0,
write = 1 << 0,
read = 1 << 1,
append = 1 << 2
};
};
public:
// constructors
File(void) = default;
File(const std::string &name, const unsigned int mode);
// destructor
virtual ~File(void);
// access
const std::string & getName(void) const;
unsigned int getMode(void) const;
template <typename IoT>
const IoT & read(const std::string &name = "");
virtual void save(const DMat &m, const std::string &name) = 0;
virtual void save(const DSample &ds, const std::string &name) = 0;
virtual void save(const DMatSample &ms, const std::string &name) = 0;
// read first name
virtual std::string getFirstName(void) = 0;
// tests
virtual bool isOpen(void) const = 0;
// IO
virtual void close(void) = 0;
virtual void open(const std::string &name, const unsigned int mode) = 0;
protected:
// access
void setName(const std::string &name);
void setMode(const unsigned int mode);
// data access
void deleteData(void);
// error checking
void checkWritability(void);
private:
// data access
template <typename IoT>
const IoT& getData(const std::string &name = "") const;
// IO
virtual std::string load(const std::string &name = "") = 0;
protected:
std::string name_{""};
unsigned int mode_{Mode::null};
IoDataTable data_;
};
// Template implementations
template <typename IoT>
const IoT& File::read(const std::string &name)
{
std::string dataName;
dataName = load(name);
return getData<IoT>(dataName);
}
template <typename IoT>
const IoT& File::getData(const std::string &name) const
{
try
{
return dynamic_cast<const IoT &>(*(data_.at(name)));
}
catch(std::out_of_range)
{
LATAN_ERROR(Definition, "no data with name '" + name + "' in file "
+ name_);
}
catch(std::bad_cast)
{
LATAN_ERROR(Definition, "data with name '" + name + "' in file "
+ name_ + " does not have type '" + typeid(IoT).name()
+ "'");
}
}
END_LATAN_NAMESPACE
#endif

355
lib/Io/Hdf5File.cpp Normal file
View File

@ -0,0 +1,355 @@
/*
* Hdf5File.cpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli, Matt Spraggs
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/Hdf5File.hpp>
#include <LatAnalyze/Io/IoObject.hpp>
#include <LatAnalyze/includes.hpp>
using namespace std;
using namespace Latan;
#ifndef H5_NO_NAMESPACE
using namespace H5NS;
#endif
constexpr unsigned int maxGroupNameSize = 1024u;
const short dMatType = static_cast<short>(IoObject::IoType::dMat);
const short dSampleType = static_cast<short>(IoObject::IoType::dSample);
const short dMatSampleType = static_cast<short>(IoObject::IoType::dMatSample);
/******************************************************************************
* Hdf5File implementation *
******************************************************************************/
// constructors ////////////////////////////////////////////////////////////////
Hdf5File::Hdf5File(void)
{}
Hdf5File::Hdf5File(const std::string &name, const unsigned int mode)
{
open(name, mode);
}
// destructor //////////////////////////////////////////////////////////////////
Hdf5File::~Hdf5File(void)
{
close();
}
// access //////////////////////////////////////////////////////////////////////
void Hdf5File::save(const DMat &m, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim[2] = {static_cast<hsize_t>(m.rows()),
static_cast<hsize_t>(m.cols())};
hsize_t attrDim = 1;
DataSpace dataSpace(2, dim), attrSpace(1, &attrDim);
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &dMatType);
dataset = group.createDataSet("data", PredType::NATIVE_DOUBLE, dataSpace);
dataset.write(m.data(), PredType::NATIVE_DOUBLE);
}
void Hdf5File::save(const DSample &ds, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim = static_cast<hsize_t>(ds.size() + 1);
hsize_t attrDim = 1;
DataSpace dataSpace(1, &dim), attrSpace(1, &attrDim);
const long int nSample = ds.size();
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &dSampleType);
attr = group.createAttribute("nSample", PredType::NATIVE_LONG, attrSpace);
attr.write(PredType::NATIVE_LONG, &nSample);
dataset = group.createDataSet("data", PredType::NATIVE_DOUBLE, dataSpace);
dataset.write(ds.data(), PredType::NATIVE_DOUBLE);
}
void Hdf5File::save(const DMatSample &ms, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim[2] = {static_cast<hsize_t>(ms[central].rows()),
static_cast<hsize_t>(ms[central].cols())};
hsize_t attrDim = 1;
DataSpace dataSpace(2, dim), attrSpace(1, &attrDim);
const long int nSample = ms.size();
string datasetName;
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &dMatSampleType);
attr = group.createAttribute("nSample", PredType::NATIVE_LONG, attrSpace);
attr.write(PredType::NATIVE_LONG, &nSample);
FOR_STAT_ARRAY(ms, s)
{
datasetName = (s == central) ? "data_C" : ("data_S_" + strFrom(s));
dataset = group.createDataSet(datasetName.c_str(),
PredType::NATIVE_DOUBLE,
dataSpace);
dataset.write(ms[s].data(), PredType::NATIVE_DOUBLE);
}
}
// read first name ////////////////////////////////////////////////////////////
string Hdf5File::getFirstName(void)
{
return getFirstGroupName();
}
// tests ///////////////////////////////////////////////////////////////////////
bool Hdf5File::isOpen(void) const
{
return (h5File_ != nullptr);
}
// check names for forbidden characters ////////////////////////////////////////
size_t Hdf5File::nameOffset(const string &name)
{
size_t ret = 0;
string badChars = "/";
for (auto c : badChars)
{
size_t pos = name.rfind(c);
if (pos != string::npos and pos > ret)
{
ret = pos;
}
}
return ret;
}
// IO //////////////////////////////////////////////////////////////////////////
void Hdf5File::close(void)
{
if (isOpen())
{
h5File_->close();
}
h5File_.reset(nullptr);
name_ = "";
mode_ = Mode::null;
deleteData();
}
void Hdf5File::open(const string &name, const unsigned int mode)
{
if (isOpen())
{
LATAN_ERROR(Io, "file already opened with name '" + name_ + "'");
}
else
{
unsigned int h5Mode = 0;
name_ = name;
mode_ = mode;
if (mode & Mode::write)
{
h5Mode |= H5F_ACC_TRUNC;
}
if (mode & Mode::read)
{
h5Mode |= H5F_ACC_RDONLY;
}
if (mode & Mode::append)
{
h5Mode |= H5F_ACC_RDWR|H5F_ACC_CREAT;
}
h5File_.reset(new H5File(name_.c_str(), h5Mode));
}
}
string Hdf5File::getFirstGroupName(void)
{
string res;
if ((mode_ & Mode::read) and (isOpen()))
{
auto firstGroupName = [](hid_t loc_id, const char *name, void *fname)
{
H5G_stat_t statbuf;
H5Gget_objinfo(loc_id, name, 0, &statbuf);
if ((statbuf.type == H5G_GROUP) and (strlen((char *)fname) == 0))
{
strncpy((char *)fname, name, maxGroupNameSize);
}
return 0;
};
char groupName[maxGroupNameSize] = "";
h5File_->iterateElems("/", nullptr, firstGroupName, groupName);
res = groupName;
}
else
{
if (isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened in read mode");
}
else
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened");
}
return "";
}
return res;
}
void Hdf5File::load(DMat &m, const DataSet &d)
{
DataSpace dataspace;
hsize_t dim[2];
dataspace = d.getSpace();
dataspace.getSimpleExtentDims(dim);
m.resize(dim[0], dim[1]);
d.read(m.data(), PredType::NATIVE_DOUBLE);
}
void Hdf5File::load(DSample &ds, const DataSet &d)
{
DataSpace dataspace;
hsize_t dim[1];
dataspace = d.getSpace();
dataspace.getSimpleExtentDims(dim);
ds.resize(dim[0] - 1);
d.read(ds.data(), PredType::NATIVE_DOUBLE);
}
string Hdf5File::load(const string &name)
{
if ((mode_ & Mode::read) and (isOpen()))
{
string groupName;
Group group;
Attribute attribute;
DataSet dataset;
IoObject::IoType type;
groupName = (name.empty()) ? getFirstGroupName() : name;
if (groupName.empty())
{
LATAN_ERROR(Io, "file '" + name_ + "' is empty");
}
group = h5File_->openGroup(groupName.c_str());
attribute = group.openAttribute("type");
attribute.read(PredType::NATIVE_SHORT, &type);
switch (type)
{
case IoObject::IoType::dMat:
{
DMat *pt = new DMat;
data_[groupName].reset(pt);
dataset = group.openDataSet("data");
load(*pt, dataset);
break;
}
case IoObject::IoType::dSample:
{
DSample *pt = new DSample;
data_[groupName].reset(pt);
dataset = group.openDataSet("data");
load(*pt, dataset);
break;
}
case IoObject::IoType::dMatSample:
{
DMatSample *pt = new DMatSample;
long int nSample;
data_[groupName].reset(pt);
attribute = group.openAttribute("nSample");
attribute.read(PredType::NATIVE_LONG, &nSample);
pt->resize(nSample);
FOR_STAT_ARRAY(*pt, s)
{
if (s == central)
{
dataset = group.openDataSet("data_C");
}
else
{
dataset =
group.openDataSet(("data_S_" + strFrom(s)).c_str());
}
load((*pt)[s], dataset);
}
break;
}
default:
{
LATAN_ERROR(Io, "unknown data type ("
+ strFrom(static_cast<int>(type)) + ") "
" (" + name_ + ":" + groupName + ")");
break;
}
}
return groupName;
}
else
{
if (isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened in read mode");
}
else
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened");
}
return "";
}
}

73
lib/Io/Hdf5File.hpp Normal file
View File

@ -0,0 +1,73 @@
/*
* Hdf5File.hpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli, Matt Spraggs
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Latan_Hdf5File_hpp_
#define Latan_Hdf5File_hpp_
#include <LatAnalyze/Global.hpp>
#include <LatAnalyze/Io/File.hpp>
#include <LatAnalyze/Core/Mat.hpp>
#include <LatAnalyze/Statistics/MatSample.hpp>
#include <H5Cpp.h>
BEGIN_LATAN_NAMESPACE
#ifndef H5_NO_NAMESPACE
#define H5NS H5
#endif
/******************************************************************************
* HDF5 datafile class *
******************************************************************************/
class Hdf5File: public File
{
public:
// constructors
Hdf5File(void);
Hdf5File(const std::string &name, const unsigned int mode);
// destructor
virtual ~Hdf5File(void);
// access
virtual void save(const DMat &m, const std::string &name);
virtual void save(const DSample &ds, const std::string &name);
virtual void save(const DMatSample &ms, const std::string &name);
// read first name
virtual std::string getFirstName(void);
// tests
virtual bool isOpen(void) const;
// IO
virtual void close(void);
virtual void open(const std::string &name, const unsigned int mode);
private:
// IO
std::string getFirstGroupName(void);
virtual std::string load(const std::string &name = "");
void load(DMat &m, const H5NS::DataSet &d);
void load(DSample &ds, const H5NS::DataSet &d);
void load(DMatSample &s, const H5NS::DataSet &d);
// check name for forbidden characters
static size_t nameOffset(const std::string &name);
private:
// file name
std::unique_ptr<H5NS::H5File> h5File_{nullptr};
};
END_LATAN_NAMESPACE
#endif // Latan_Hdf5File_hpp_

51
lib/Io/Io.cpp Normal file
View File

@ -0,0 +1,51 @@
/*
* Io.cpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/Io.hpp>
#include <LatAnalyze/includes.hpp>
#include <LatAnalyze/Io/AsciiFile.hpp>
#include <LatAnalyze/Io/Hdf5File.hpp>
using namespace std;
using namespace Latan;
string Io::getFirstName(const string &fileName)
{
std::unique_ptr<File> file = open(fileName);
return file->getFirstName();
}
unique_ptr<File> Io::open(const std::string &fileName, const unsigned int mode)
{
string ext = extension(fileName);
if ((ext == "dat") or (ext == "sample") or (ext == "seed"))
{
return unique_ptr<File>(new AsciiFile(fileName, mode));
}
else if (ext == "h5")
{
return unique_ptr<File>(new Hdf5File(fileName, mode));
}
else
{
LATAN_ERROR(Io, "unknown file extension '" + ext + "'");
}
}

100
lib/Io/Io.hpp Normal file
View File

@ -0,0 +1,100 @@
/*
* Io.hpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Latan_Io_hpp_
#define Latan_Io_hpp_
#include <LatAnalyze/Global.hpp>
#include <LatAnalyze/Io/File.hpp>
BEGIN_LATAN_NAMESPACE
/******************************************************************************
* Static IO functions *
******************************************************************************/
class Io
{
public:
template <typename IoT, typename FileType>
static IoT load(const std::string &fileName, const std::string &name = "");
template <typename IoT>
static IoT load(const std::string &fileName, const std::string &name = "");
template <typename IoT, typename FileType>
static void save(const IoT &data, const std::string &fileName,
const unsigned int mode = File::Mode::write,
const std::string &name = "");
template <typename IoT>
static void save(const IoT &data, const std::string &fileName,
const unsigned int mode = File::Mode::write,
const std::string &name = "");
template <typename FileType>
static std::string getFirstName(const std::string &fileName);
static std::string getFirstName(const std::string &fileName);
static std::unique_ptr<File> open(const std::string &fileName,
const unsigned int mode = File::Mode::read);
};
// template implementation /////////////////////////////////////////////////////
template <typename IoT, typename FileType>
IoT Io::load(const std::string &fileName, const std::string &name)
{
FileType file(fileName, File::Mode::read);
return file.template read<IoT>(name);
}
template <typename IoT>
IoT Io::load(const std::string &fileName, const std::string &name)
{
std::unique_ptr<File> file = open(fileName);
return file->read<IoT>(name);
}
template <typename IoT, typename FileType>
void Io::save(const IoT &data, const std::string &fileName,
const unsigned int mode, const std::string &name)
{
FileType file(fileName, mode);
std::string realName = (name.empty()) ? fileName : name;
file.save(data, realName);
}
template <typename IoT>
void Io::save(const IoT &data, const std::string &fileName,
const unsigned int mode, const std::string &name)
{
std::unique_ptr<File> file = open(fileName, mode);
std::string realName = (name.empty()) ? fileName : name;
file->save(data, realName);
}
template <typename FileType>
std::string Io::getFirstName(const std::string &fileName)
{
FileType file(fileName, File::Mode::read);
return file.getFirstName();
}
END_LATAN_NAMESPACE
#endif // Latan_Io_hpp_

50
lib/Io/IoObject.hpp Normal file
View File

@ -0,0 +1,50 @@
/*
* IoObject.hpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2016 Antonin Portelli
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Latan_IoObject_hpp_
#define Latan_IoObject_hpp_
#include <LatAnalyze/Global.hpp>
BEGIN_LATAN_NAMESPACE
/******************************************************************************
* Abstract class for IO objects *
******************************************************************************/
class IoObject
{
public:
// conserve order for datafile retro-compatibility!
enum class IoType: short int
{
noType = 0,
dMat = 1,
dMatSample = 2,
dSample = 3
};
public:
// destructor
virtual ~IoObject(void) = default;
// access
virtual IoType getType(void) const = 0;
};
END_LATAN_NAMESPACE
#endif // Latan_IoObject_hpp_

2791
lib/Io/Xml/tinyxml2.cpp Normal file

File diff suppressed because it is too large Load Diff

2272
lib/Io/Xml/tinyxml2.hpp Normal file

File diff suppressed because it is too large Load Diff

84
lib/Io/XmlReader.cpp Normal file
View File

@ -0,0 +1,84 @@
/*
* XmlReader.cpp, part of LatAnalyze
*
* Copyright (C) 2013 - 2014 Antonin Portelli
*
* LatAnalyze is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze. If not, see <http://www.gnu.org/licenses/>.
*/
#include <LatAnalyze/Io/XmlReader.hpp>
#include <LatAnalyze/includes.hpp>
using namespace std;
using namespace Latan;
XmlParsing::XmlParsing(string msg, string loc)
: runtime_error("XML reader error: " + msg + " (" + loc + ")")
{}
/******************************************************************************
* XmlReader implementation *
******************************************************************************/
// constructor /////////////////////////////////////////////////////////////////
XmlReader::XmlReader(const string &fileName)
{
open(fileName);
}
// IO //////////////////////////////////////////////////////////////////////////
void XmlReader::open(const string &fileName)
{
name_ = fileName;
doc_.LoadFile(name_.c_str());
if (doc_.Error())
{
string errMsg;
if (doc_.ErrorStr())
{
errMsg = doc_.ErrorStr();
}
LATAN_ERROR(Io, "cannot open file " + fileName + " [tinyxml2 code "
+ strFrom(doc_.ErrorID()) + ": " + errMsg + "]");
}
root_ = doc_.RootElement();
}
// XML structure access ////////////////////////////////////////////////////////
const XmlNode * XmlReader::getNextNode(const XmlNode *node,
const string &nodeName)
{
const char *pt = (nodeName.empty()) ? nullptr : nodeName.c_str();
if (node)
{
return node->NextSiblingElement(pt);
}
else
{
return nullptr;
}
}
const XmlNode * XmlReader::getNextSameNode(const XmlNode *node)
{
if (node)
{
return getNextNode(node, node->Name());
}
else
{
return nullptr;
}
}

235
lib/Io/XmlReader.hpp Normal file
View File

@ -0,0 +1,235 @@
/*
* XmlReader.hpp, part of LatAnalyze
*
* Copyright (C) 2013 - 2015 Antonin Portelli
*
* LatAnalyze is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LatAnalyze_XmlReader_hpp_
#define LatAnalyze_XmlReader_hpp_
#include <LatAnalyze/Global.hpp>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#include <LatAnalyze/Io/Xml/tinyxml2.hpp>
#pragma GCC diagnostic pop
BEGIN_LATAN_NAMESPACE
// parsing exception
class XmlParsing: public std::runtime_error
{
public:
XmlParsing(std::string msg, std::string loc);
};
/******************************************************************************
* XML parameter file reader *
******************************************************************************/
typedef tinyxml2::XMLElement XmlNode;
class XmlReader
{
public:
// constructor
XmlReader(void) = default;
explicit XmlReader(const std::string &fileName);
// destructor
virtual ~XmlReader(void) = default;
// IO
void open(const std::string &fileName);
// XML structure access
template <typename... Strs>
static const XmlNode * getFirstNode(const XmlNode *startNode,
const std::string &nodeName,
Strs... nodeNames);
template <typename... Strs>
const XmlNode * getFirstNode(const std::string &nodeName,
Strs... nodeNames) const;
static const XmlNode * getNextNode(const XmlNode *node,
const std::string &nodeName = "");
static const XmlNode * getNextSameNode(const XmlNode *node);
template <typename T>
static T getValue(const XmlNode *node);
template <typename T, typename... Strs>
static T getFirstValue(const XmlNode *startNode,
const std::string &nodeName, Strs... nodeNames);
template <typename T, typename... Strs>
T getFirstValue(const std::string &nodeName, Strs... nodeNames) const;
template <typename T, typename... Strs>
static std::vector<T> getAllValues(const XmlNode *startNode,
const std::string &nodeName,
Strs... nodeNames);
template <typename T, typename... Strs>
std::vector<T> getAllValues(const std::string &nodeName,
Strs... nodeNames) const;
// XML structure test
template <typename... Strs>
static bool hasNode(const XmlNode *startNode, const std::string &nodeName,
Strs... nodeNames);
template <typename... Strs>
bool hasNode(const std::string &nodeName, Strs... nodeNames) const;
private:
std::string name_;
tinyxml2::XMLDocument doc_;
XmlNode *root_{nullptr};
};
/******************************************************************************
* XmlReader template implementation *
******************************************************************************/
// XML structure access ////////////////////////////////////////////////////////
template <typename... Strs>
const XmlNode * XmlReader::getFirstNode(const XmlNode *startNode,
const std::string &nodeName,
Strs... nodeNames)
{
static_assert(static_or<std::is_assignable<std::string, Strs>::value...>::value,
"getFirstNode arguments are not compatible with std::string");
const unsigned int nName = sizeof...(nodeNames) + 1;
const std::string name[] = {nodeName, nodeNames...};
const XmlNode *node = startNode;
if (!node)
{
LATAN_ERROR(Io, "root node is null, no XML file opened");
}
for (unsigned int i = 0; i < nName; ++i)
{
node = node->FirstChildElement(name[i].c_str());
if (!node)
{
LATAN_ERROR(Io, "XML node " + name[i] + " not found");
}
}
return node;
}
template <typename... Strs>
const XmlNode * XmlReader::getFirstNode(const std::string &nodeName,
Strs... nodeNames) const
{
if (!root_)
{
LATAN_ERROR(Io, "root node is null, no XML file opened");
}
return getFirstNode(root_, nodeName, nodeNames...);
}
template <typename T>
T XmlReader::getValue(const XmlNode *node)
{
if (node)
{
if (node->GetText())
{
return Latan::strTo<T>(node->GetText());
}
else
{
return T();
}
}
else
{
return T();
}
}
template <typename T, typename... Strs>
T XmlReader::getFirstValue(const XmlNode *startNode,
const std::string &nodeName, Strs... nodeNames)
{
const XmlNode *node = getFirstNode(startNode, nodeName, nodeNames...);
return getValue<T>(node);
}
template <typename T, typename... Strs>
T XmlReader::getFirstValue(const std::string &nodeName, Strs... nodeNames) const
{
return getFirstValue<T>(root_, nodeName, nodeNames...);
}
template <typename T, typename... Strs>
std::vector<T> XmlReader::getAllValues(const XmlNode *startNode,
const std::string &nodeName,
Strs... nodeNames)
{
const XmlNode *node = getFirstNode(startNode, nodeName, nodeNames...);
std::vector<T> value;
while (node)
{
value.push_back(getValue<T>(node));
node = getNextSameNode(node);
}
return value;
}
template <typename T, typename... Strs>
std::vector<T> XmlReader::getAllValues(const std::string &nodeName,
Strs... nodeNames) const
{
return getAllValues<T>(root_, nodeName, nodeNames...);
}
// XML structure test //////////////////////////////////////////////////////////
template <typename... Strs>
bool XmlReader::hasNode(const XmlNode *startNode, const std::string &nodeName,
Strs... nodeNames)
{
static_assert(static_or<std::is_assignable<std::string, Strs>::value...>::value,
"hasNode arguments are not compatible with std::string");
const unsigned int nName = sizeof...(nodeNames) + 1;
const std::string name[] = {nodeName, nodeNames...};
const XmlNode *node = startNode;
if (!node)
{
LATAN_ERROR(Io, "root node is null, no XML file opened");
}
for (unsigned int i = 0; i < nName; ++i)
{
node = node->FirstChildElement(name[i].c_str());
if (!node)
{
return false;
}
}
return true;
}
template <typename... Strs>
bool XmlReader::hasNode(const std::string &nodeName, Strs... nodeNames) const
{
if (!root_)
{
LATAN_ERROR(Io, "root node is null, no XML file opened");
}
return hasNode(root_, nodeName, nodeNames...);
}
END_LATAN_NAMESPACE
#endif // LatAnalyze_XmlReader_hpp_