818 lines
34 KiB
C++
818 lines
34 KiB
C++
//! \addtogroup IO
|
||
//! \{
|
||
//! \file piconfig.h
|
||
//! \brief Configuration files parser and writer
|
||
//! \~english Parser and writer for configuration files with tree structure support
|
||
//! \~russian Разбор и запись конфигурационных файлов с поддержкой древовидной структуры
|
||
//! \details
|
||
//! \~english PIConfig provides functionality to read, write and manipulate configuration files in a tree-like structure.
|
||
//! \~russian PIConfig предоставляет функциональность для чтения, записи и управления конфигурационными файлами в древовидной структуре.
|
||
//! \}
|
||
/*
|
||
PIP - Platform Independent Primitives
|
||
Configuration parser and writer
|
||
Ivan Pelipenko peri4ko@yandex.ru
|
||
|
||
This program 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.
|
||
|
||
This program 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 Lesser General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Lesser General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#ifndef PICONFIG_H
|
||
#define PICONFIG_H
|
||
|
||
#include "piiodevice.h"
|
||
#include "piiostream.h"
|
||
|
||
// clang-format off
|
||
#define PICONFIG_GET_VALUE \
|
||
Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) {return getValue(vname, PIString(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) {return getValue(vname, def.join("%|%"), exists);} \
|
||
Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) {return getValue(vname, PIString::fromBool(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const short def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const int def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const long def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const float def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const double def, bool * exists = 0) {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
\
|
||
Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) const {return getValue(vname, PIString(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) const {return getValue(vname, def.join("%|%"), exists);} \
|
||
Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) const {return getValue(vname, PIString::fromBool(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const short def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const int def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const long def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const float def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);} \
|
||
Entry & getValue(const PIString & vname, const double def, bool * exists = 0) const {return getValue(vname, PIString::fromNumber(def), exists);}
|
||
// clang-format on
|
||
|
||
//! \~english Main configuration parser and writer class
|
||
//! \~russian Главный класс для разбора и записи конфигурационных файлов
|
||
class PIP_EXPORT PIConfig {
|
||
friend class Entry;
|
||
friend class Branch;
|
||
|
||
public:
|
||
NO_COPY_CLASS(PIConfig);
|
||
|
||
//! Contructs and read configuration file at path "path" in mode "mode"
|
||
PIConfig(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
//! Contructs and read configuration string "string" in mode "mode"
|
||
PIConfig(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
//! Contructs and read configuration from custom device "device" in mode "mode"
|
||
PIConfig(PIIODevice * device = nullptr, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
~PIConfig();
|
||
|
||
class Entry;
|
||
|
||
|
||
//! \~english Branch class - container for Entry objects
|
||
//! \~russian Класс Branch - контейнер для объектов Entry
|
||
class PIP_EXPORT Branch: public PIVector<Entry *> {
|
||
friend class PIConfig;
|
||
friend class Entry;
|
||
#ifdef PIP_STD_IOSTREAM
|
||
friend std::ostream & operator<<(std::ostream & s, const Branch & v);
|
||
#endif
|
||
friend PICout operator<<(PICout s, const Branch & v);
|
||
|
||
public:
|
||
Branch() { ; }
|
||
|
||
//! \~english Get value from branch by name with default value
|
||
//! \~russian Получить значение из ветки по имени со значением по умолчанию
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {
|
||
return const_cast<Branch *>(this)->getValue(vname, def, exists);
|
||
}
|
||
PICONFIG_GET_VALUE
|
||
|
||
//! \~english Get all leaf entries from the entire tree
|
||
//! \~russian Получить все листовые записи из всего дерева
|
||
Branch allLeaves();
|
||
|
||
//! \~english Get all entries with name containing specified substring
|
||
//! \~russian Получить все записи с именем, содержащим указанную подстроку
|
||
Branch getValues(const PIString & name);
|
||
|
||
//! \~english Get all leaf entries (entries without children)
|
||
//! \~russian Получить все листовые записи (записи без детей)
|
||
Branch getLeaves();
|
||
|
||
//! \~english Get all branch entries (entries with children)
|
||
//! \~russian Получить все ветвящиеся записи (записи с детьми)
|
||
Branch getBranches();
|
||
|
||
//! \~english Filter branch by filter string
|
||
//! \~russian Фильтровать ветку по строке фильтра
|
||
Branch & filter(const PIString & f);
|
||
|
||
//! \~english Check if entry with specified name exists
|
||
//! \~russian Проверить, существует ли запись с указанным именем
|
||
bool isEntryExists(const PIString & name) const {
|
||
for (const auto * i: *this)
|
||
if (entryExists(i, name)) return true;
|
||
return false;
|
||
}
|
||
|
||
//! \~english Get index of entry in branch
|
||
//! \~russian Получить индекс записи в ветке
|
||
int indexOf(const Entry * e) {
|
||
for (int i = 0; i < size_s(); ++i)
|
||
if (at(i) == e) return i;
|
||
return -1;
|
||
}
|
||
|
||
private:
|
||
bool entryExists(const Entry * e, const PIString & name) const;
|
||
void allLeaves(Branch & b, Entry * e) {
|
||
for (auto * i: e->_children) {
|
||
if (i->isLeaf())
|
||
b << i;
|
||
else
|
||
allLeaves(b, i);
|
||
}
|
||
}
|
||
#ifdef PIP_STD_IOSTREAM
|
||
void coutt(std::ostream & s, const PIString & p) const {
|
||
for (const auto * i: *this)
|
||
i->coutt(s, p);
|
||
}
|
||
#endif
|
||
void piCoutt(PICout s, const PIString & p) const {
|
||
for (const auto * i: *this)
|
||
i->piCoutt(s, p);
|
||
}
|
||
|
||
static Entry _empty;
|
||
PIString delim;
|
||
};
|
||
|
||
|
||
//! \~english Entry class - represents a single configuration entry
|
||
//! \~russian Класс Entry - представляет отдельную запись конфигурации
|
||
class PIP_EXPORT Entry {
|
||
friend class PIConfig;
|
||
friend class Branch;
|
||
|
||
public:
|
||
Entry() {
|
||
_parent = 0;
|
||
_line = -1;
|
||
}
|
||
|
||
//! Returns parent entry, or 0 if there is no parent (root of default value)
|
||
Entry * parent() const { return _parent; }
|
||
|
||
//! Returns children count
|
||
int childCount() const { return _children.size_s(); }
|
||
|
||
//! Returns children as \a PIConfig::Branch
|
||
Branch & children() const {
|
||
_children.delim = delim;
|
||
return _children;
|
||
}
|
||
|
||
//! Returns child at index "index"
|
||
Entry * child(const int index) const { return _children[index]; }
|
||
|
||
//! Returns first child with name "name"
|
||
Entry * findChild(const PIString & name) {
|
||
for (auto * i: _children)
|
||
if (i->_name == name) return i;
|
||
return 0;
|
||
}
|
||
|
||
//! Returns first child with name "name"
|
||
const Entry * findChild(const PIString & name) const {
|
||
for (const auto * i: _children)
|
||
if (i->_name == name) return i;
|
||
return 0;
|
||
}
|
||
|
||
//! Returns \b true if there is no children
|
||
bool isLeaf() const { return _children.isEmpty(); }
|
||
|
||
|
||
//! Returns name
|
||
const PIString & name() const { return _name; }
|
||
|
||
//! Returns value
|
||
const PIString & value() const { return _value; }
|
||
|
||
//! Returns type
|
||
const PIString & type() const { return _type; }
|
||
|
||
//! Returns comment
|
||
const PIString & comment() const { return _comment; }
|
||
|
||
/** \brief Returns full name, i.e. name as it looks in file
|
||
* \details In case of default entry full name always is empty
|
||
* \snippet piconfig.cpp fullName */
|
||
const PIString & fullName() const { return _full_name; }
|
||
|
||
//! Set name to "value" and returns this
|
||
Entry & setName(const PIString & value) {
|
||
_name = value;
|
||
return *this;
|
||
}
|
||
|
||
//! Set type to "value" and returns this
|
||
Entry & setType(const PIString & value) {
|
||
_type = value;
|
||
return *this;
|
||
}
|
||
|
||
//! Set comment to "value" and returns this
|
||
Entry & setComment(const PIString & value) {
|
||
_comment = value;
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this
|
||
Entry & setValue(const PIString & value) {
|
||
_value = value;
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "l"
|
||
Entry & setValue(const PIStringList & value) {
|
||
setValue(value.join("%|%"));
|
||
setType("l");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "s"
|
||
Entry & setValue(const char * value) {
|
||
setValue(PIString(value));
|
||
setType("s");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "b"
|
||
Entry & setValue(const bool value) {
|
||
setValue(PIString::fromBool(value));
|
||
setType("b");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "s"
|
||
Entry & setValue(const char value) {
|
||
setValue(PIString(1, value));
|
||
setType("s");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const short value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const int value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const long value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const uchar value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const ushort value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const uint value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "n"
|
||
Entry & setValue(const ulong value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("n");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "f"
|
||
Entry & setValue(const float value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("f");
|
||
return *this;
|
||
}
|
||
|
||
//! Set value to "value" and returns this. Type is set to "f"
|
||
Entry & setValue(const double value) {
|
||
setValue(PIString::fromNumber(value));
|
||
setType("f");
|
||
return *this;
|
||
}
|
||
|
||
|
||
/** \brief Returns entry with name "vname" and default value "def"
|
||
* \details If there is no suitable entry found, reference to default internal entry with
|
||
* value = "def" will be returned, and if "exists" not null it will be set to \b false */
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {
|
||
return const_cast<Entry *>(this)->getValue(vname, def, exists);
|
||
}
|
||
PICONFIG_GET_VALUE
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
|
||
//! \brief Returns entry with name "vname" and default value "def"
|
||
|
||
|
||
//! Find all entries with names with substrings "vname" and returns them as \a PIConfig::Branch
|
||
Branch getValues(const PIString & vname);
|
||
|
||
|
||
//! If there is no children returns if name == "name". Else returns if any child has name == "name"
|
||
bool isEntryExists(const PIString & name) const { return entryExists(this, name); }
|
||
|
||
|
||
//! Convertion to boolean
|
||
bool toBool() const { return _value.toBool(); }
|
||
|
||
//! Convertion to char
|
||
char toChar() const { return (_value.isEmpty() ? 0 : _value[0].toAscii()); }
|
||
|
||
//! Convertion to short
|
||
short toShort() const { return _value.toShort(); }
|
||
|
||
//! Convertion to int
|
||
int toInt() const { return _value.toInt(); }
|
||
|
||
//! Convertion to long
|
||
long toLong() const { return _value.toLong(); }
|
||
|
||
//! Convertion to uchar
|
||
uchar toUChar() const { return _value.toInt(); }
|
||
|
||
//! Convertion to ushort
|
||
ushort toUShort() const { return _value.toShort(); }
|
||
|
||
//! Convertion to uint
|
||
uint toUInt() const { return _value.toInt(); }
|
||
|
||
//! Convertion to ulong
|
||
ulong toULong() const { return _value.toLong(); }
|
||
|
||
//! Convertion to float
|
||
float toFloat() const { return _value.toFloat(); }
|
||
|
||
//! Convertion to double
|
||
double toDouble() const { return _value.toDouble(); }
|
||
|
||
//! Convertion to PIString
|
||
PIString toString() const { return _value; }
|
||
|
||
//! Convertion to PIStringList
|
||
PIStringList toStringList() const { return _value.split("%|%"); }
|
||
|
||
private:
|
||
static bool compare(PIConfig::Entry * const & f, PIConfig::Entry * const & s) { return f->_line < s->_line; }
|
||
bool entryExists(const Entry * e, const PIString & name) const;
|
||
void buildLine() { _all = _tab + _full_name + " = " + _value + " #" + _type + " " + _comment; }
|
||
void clear() {
|
||
_children.clear();
|
||
_name = _value = _type = _comment = _all = PIString();
|
||
_line = 0;
|
||
_parent = 0;
|
||
}
|
||
#ifdef PIP_STD_IOSTREAM
|
||
void coutt(std::ostream & s, const PIString & p) const;
|
||
#endif
|
||
void piCoutt(PICout s, const PIString & p) const;
|
||
void deleteBranch() {
|
||
for (auto * i: _children) {
|
||
i->deleteBranch();
|
||
delete i;
|
||
}
|
||
}
|
||
|
||
static Entry _empty;
|
||
Entry * _parent;
|
||
mutable Branch _children;
|
||
PIString _tab;
|
||
PIString _name;
|
||
PIString _value;
|
||
PIString _type;
|
||
PIString _comment;
|
||
PIString _all;
|
||
PIString _full_name;
|
||
PIString delim;
|
||
int _line;
|
||
};
|
||
|
||
|
||
//! \~english Open configuration from file at path in specified mode
|
||
//! \~russian Открыть конфигурацию из файла по указанному пути в указанном режиме
|
||
bool open(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
//! \~english Open configuration from string in specified mode
|
||
//! \~russian Открыть конфигурацию из строки в указанном режиме
|
||
bool open(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
//! \~english Open configuration from custom I/O device in specified mode
|
||
//! \~russian Открыть конфигурацию из пользовательского устройства ввода-вывода в указанном режиме
|
||
bool open(PIIODevice * device, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
|
||
|
||
//! \~english Check if configuration is opened
|
||
//! \~russian Проверить, открыта ли конфигурация
|
||
bool isOpened() const;
|
||
|
||
//! \~english Get top-level entry by name with default value
|
||
//! \~russian Получить запись верхнего уровня по имени со значением по умолчанию
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0);
|
||
Entry & getValue(const PIString & vname, const PIString & def = PIString(), bool * exists = 0) const {
|
||
return const_cast<PIConfig *>(this)->getValue(vname, def, exists);
|
||
}
|
||
|
||
PICONFIG_GET_VALUE
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
|
||
//! \brief Returns top-level entry with name "vname" and default value "def"
|
||
|
||
//! \~english Get all top-level entries with name containing substring
|
||
//! \~russian Получить все записи верхнего уровня с именем, содержащим подстроку
|
||
Branch getValues(const PIString & vname);
|
||
|
||
|
||
//! \~english Set value for top-level entry with name
|
||
//! \~russian Установить значение для записи верхнего уровня с именем
|
||
//! \details
|
||
//! \~english Set top-level entry with name "name" value to "value", type to "type" and if "write" immediate write to file.
|
||
//! Add new entry if there is no suitable exists
|
||
void setValue(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "l" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const PIStringList & value, bool write = true) { setValue(name, value.join("%|%"), "l", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "s" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const char * value, bool write = true) { setValue(name, PIString(value), "s", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "b" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const bool value, bool write = true) { setValue(name, PIString::fromBool(value), "b", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const short value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const int value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const long value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const uchar value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const ushort value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const uint value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "n" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const ulong value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "f" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const float value, bool write = true) { setValue(name, PIString::fromNumber(value), "f", write); }
|
||
|
||
//! Set top-level entry with name "name" value to "value", type to "f" and if "write" immediate write to file. Add new entry if there is
|
||
//! no suitable exists
|
||
void setValue(const PIString & name, const double value, bool write = true) { setValue(name, PIString::fromNumber(value), "f", write); }
|
||
|
||
//! \~english Get root entry of configuration tree
|
||
//! \~russian Получить корневую запись дерева конфигурации
|
||
Entry & rootEntry() { return root; }
|
||
|
||
//! \~english Get count of top-level entries
|
||
//! \~russian Получить количество записей верхнего уровня
|
||
int entriesCount() const { return childCount(&root); }
|
||
|
||
//! \~english Check if top-level entry with name exists
|
||
//! \~russian Проверить, существует ли запись верхнего уровня с указанным именем
|
||
bool isEntryExists(const PIString & name) const { return entryExists(&root, name); }
|
||
|
||
//! \~english Get all top-level entries
|
||
//! \~russian Получить все записи верхнего уровня
|
||
Branch allTree() {
|
||
Branch b;
|
||
for (auto * i: root._children)
|
||
b << i;
|
||
b.delim = delim;
|
||
return b;
|
||
}
|
||
|
||
//! \~english Get all entries without children (leaves)
|
||
//! \~russian Получить все записи без детей (листья)
|
||
Branch allLeaves() {
|
||
Branch b;
|
||
allLeaves(b, &root);
|
||
b.sort(Entry::compare);
|
||
b.delim = delim;
|
||
return b;
|
||
}
|
||
|
||
//! \~english Get index of entry by name
|
||
//! \~russian Получить индекс записи по имени
|
||
int entryIndex(const PIString & name);
|
||
|
||
//! \~english Get entry name by index
|
||
//! \~russian Получить имя записи по индексу
|
||
PIString getName(uint number) { return entryByIndex(number)._name; }
|
||
|
||
//! \~english Get entry value by index
|
||
//! \~russian Получить значение записи по индексу
|
||
PIString getValueByIndex(uint number) { return entryByIndex(number)._value; }
|
||
|
||
//! \~english Get entry type by index
|
||
//! \~russian Получить тип записи по индексу
|
||
PIChar getType(uint number) { return entryByIndex(number)._type[0]; }
|
||
|
||
//! \~english Get entry comment by index
|
||
//! \~russian Получить комментарий записи по индексу
|
||
PIString getComment(uint number) { return entryByIndex(number)._comment; }
|
||
|
||
//! \~english Add new entry with name, value and type
|
||
//! \~russian Добавить новую запись с именем, значением и типом
|
||
void addEntry(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
|
||
|
||
//! \~english Set name of entry by index
|
||
//! \~russian Установить имя записи по индексу
|
||
void setName(uint number, const PIString & name, bool write = true);
|
||
|
||
//! \~english Set value of entry by index
|
||
//! \~russian Установить значение записи по индексу
|
||
void setValue(uint number, const PIString & value, bool write = true);
|
||
|
||
//! \~english Set type of entry by index
|
||
//! \~russian Установить тип записи по индексу
|
||
void setType(uint number, const PIString & type, bool write = true);
|
||
|
||
//! \~english Set comment of entry by index
|
||
//! \~russian Установить комментарий записи по индексу
|
||
void setComment(uint number, const PIString & comment, bool write = true);
|
||
|
||
//! \~english Remove entry by name
|
||
//! \~russian Удалить запись по имени
|
||
void removeEntry(const PIString & name, bool write = true);
|
||
|
||
//! \~english Remove entry by index
|
||
//! \~russian Удалить запись по индексу
|
||
void removeEntry(uint number, bool write = true);
|
||
|
||
//! Remove all tree and device content
|
||
void clear();
|
||
|
||
//! \~english Parse device and build internal tree
|
||
//! \~russian Прочитать устройство и построить внутреннее дерево
|
||
void readAll();
|
||
|
||
//! \~english Write all internal tree to device
|
||
//! \~russian Записать всё внутреннее дерево в устройство
|
||
void writeAll();
|
||
|
||
//! \~english Get current tree delimiter
|
||
//! \~russian Получить текущий разделитель дерева
|
||
const PIString & delimiter() const { return delim; }
|
||
|
||
//! \~english Set tree delimiter
|
||
//! \~russian Установить разделитель дерева
|
||
void setDelimiter(const PIString & d) {
|
||
delim = d;
|
||
setEntryDelim(&root, d);
|
||
readAll();
|
||
}
|
||
|
||
private:
|
||
PIConfig(const PIString & path, PIStringList dirs);
|
||
void _init();
|
||
void _destroy();
|
||
void _setupDev();
|
||
void _clearDev();
|
||
void _flushDev();
|
||
bool _isEndDev();
|
||
void _seekToBeginDev();
|
||
PIString _readLineDev();
|
||
void _writeDev(const PIString & l);
|
||
int childCount(const Entry * e) const {
|
||
int c = 0;
|
||
for (const auto * i: e->_children)
|
||
c += childCount(i);
|
||
c += e->_children.size_s();
|
||
return c;
|
||
}
|
||
bool entryExists(const Entry * e, const PIString & name) const;
|
||
void buildFullNames(Entry * e) {
|
||
for (auto * i: e->_children) {
|
||
if (e != &root)
|
||
i->_full_name = e->_full_name + delim + i->_name;
|
||
else
|
||
i->_full_name = i->_name;
|
||
buildFullNames(i);
|
||
}
|
||
}
|
||
void allLeaves(Branch & b, Entry * e) {
|
||
for (auto * i: e->_children) {
|
||
if ((!i->_value.isEmpty() && !i->isLeaf()) || i->isLeaf()) b << i;
|
||
allLeaves(b, i);
|
||
}
|
||
}
|
||
void setEntryDelim(Entry * e, const PIString & d) {
|
||
for (auto * i: e->_children)
|
||
setEntryDelim(i, d);
|
||
e->delim = d;
|
||
}
|
||
Entry & entryByIndex(const int index) {
|
||
Branch b = allLeaves();
|
||
if (index < 0 || index >= b.size_s()) return empty;
|
||
return *(b[index]);
|
||
}
|
||
void removeEntry(Branch & b, Entry * e);
|
||
void deleteEntry(Entry * e) {
|
||
for (auto * i: e->_children)
|
||
deleteEntry(i);
|
||
delete e;
|
||
}
|
||
PIString getPrefixFromLine(PIString line, bool * exists);
|
||
void updateIncludes();
|
||
PIString parseLine(PIString v);
|
||
void parse();
|
||
|
||
bool own_dev = false, internal = false;
|
||
PIVector<PIConfig *> includes, inc_devs;
|
||
Branch all_includes;
|
||
PIIODevice * dev = nullptr;
|
||
PIIOTextStream * stream = nullptr;
|
||
PIString delim;
|
||
PIStringList incdirs;
|
||
Entry root, empty;
|
||
uint lines = 0;
|
||
PIStringList other;
|
||
};
|
||
|
||
|
||
#ifdef PIP_STD_IOSTREAM
|
||
PIP_EXPORT std::ostream & operator<<(std::ostream & s, const PIConfig::Branch & v);
|
||
PIP_EXPORT std::ostream & operator<<(std::ostream & s, const PIConfig::Entry & v);
|
||
#endif
|
||
|
||
inline PICout operator<<(PICout s, const PIConfig::Branch & v) {
|
||
s.saveAndSetControls(0);
|
||
v.piCoutt(s, "");
|
||
s.restoreControls();
|
||
return s;
|
||
}
|
||
inline PICout operator<<(PICout s, const PIConfig::Entry & v) {
|
||
s << v.value() << "(" << v.type() << v.comment() << ")";
|
||
return s;
|
||
}
|
||
|
||
|
||
//! \~english Service function for reading device settings from configuration
|
||
//! \~russian Сервисная функция для чтения настроек устройства из конфигурации
|
||
//! \note
|
||
//! \~english Useful for implementing PIIODevice::configureDevice()
|
||
//! \~russian Полезно для реализации PIIODevice::configureDevice()
|
||
//! \~\details Function takes entry name "name", default value "def" and two
|
||
//! \a PIConfig::Entry sections: "em" and their parent "ep". If there is no
|
||
//! parent ep = 0. If "ep" is not null and entry "name" exists in "ep" function
|
||
//! returns this value. Else returns value of entry "name" in section "em" or
|
||
//! "def" if entry doesn`t exists. \n This function useful to read settings
|
||
//! from configuration file in implementation \a PIIODevice::configureDevice() function
|
||
//! \relatesalso PIConfig \relatesalso PIIODevice
|
||
template<typename T>
|
||
T readDeviceSetting(const PIString & name, const T & def, const PIConfig::Entry * em, const PIConfig::Entry * ep) {
|
||
PIVariant v = PIVariant::fromValue<T>(def);
|
||
if (ep) {
|
||
bool ex = false;
|
||
v.setValueFromString(ep->getValue(name, def, &ex).toString());
|
||
if (ex) return v.value<T>();
|
||
}
|
||
v.setValueFromString(em->getValue(name, def).toString());
|
||
return v.value<T>();
|
||
}
|
||
|
||
#endif // PICONFIG_H
|