Files
pip/libs/main/io_devices/piconfig.h
2026-03-07 17:00:45 +03:00

856 lines
44 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file piconfig.h
* \ingroup IO
* \~\brief
* \~english Configuration files parser and writer
* \~russian Разбор и запись конфигурационных файлов
*/
/*
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
//! \ingroup IO
//! \~\brief
//! \~english Tree-based parser and writer for PIP configuration sources.
//! \~russian Древовидный парсер и записыватель конфигурационных источников PIP.
//! \~\details
//! \~english Supports dotted paths, INI-style section prefixes, multiline values and \c include entries resolved during parsing.
//! \~russian Поддерживает точечные пути, префиксы секций в стиле INI, многострочные значения и записи \c include, разрешаемые при разборе.
class PIP_EXPORT PIConfig {
friend class Entry;
friend class Branch;
public:
NO_COPY_CLASS(PIConfig);
//! \~english Opens and parses configuration file at "path".
//! \~russian Открывает и разбирает файл конфигурации по пути "path".
PIConfig(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Opens and parses configuration stored in "string".
//! \~russian Открывает и разбирает конфигурацию, хранящуюся в "string".
PIConfig(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Opens and parses configuration from custom device "device".
//! \~russian Открывает и разбирает конфигурацию из пользовательского устройства "device".
PIConfig(PIIODevice * device = nullptr, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Destroys the parser and releases owned devices.
//! \~russian Уничтожает парсер и освобождает принадлежащие ему устройства.
~PIConfig();
class Entry;
//! \ingroup IO
//! \~\brief
//! \~english List-like view over a set of configuration entries.
//! \~russian Список-представление набора конфигурационных записей.
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:
//! \~english Constructs an empty branch view.
//! \~russian Создает пустое представление ветви.
Branch() { ; }
//! \~english Resolves descendant "vname" inside this branch.
//! \~russian Разрешает потомка "vname" внутри этой ветви.
//! \~\details
//! \~english If lookup fails, returns a shared default entry filled with "def" and sets \a exists to \b false when provided.
//! \~russian Если поиск не удался, возвращает общий внутренний entry со значением "def" и устанавливает \a exists в \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<Branch *>(this)->getValue(vname, def, exists);
}
PICONFIG_GET_VALUE
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
//! \~english Typed overloads of \a getValue() convert "def" to the config string representation before lookup.
//! \~russian Типизированные перегрузки \a getValue() преобразуют "def" в строковое представление конфигурации перед поиском.
//! \~english Returns all leaf descendants reachable from this branch.
//! \~russian Возвращает все листовые потомки, достижимые из этой ветви.
Branch allLeaves();
//! \~english Returns entries in this branch whose names contain "name".
//! \~russian Возвращает записи этой ветви, чьи имена содержат "name".
Branch getValues(const PIString & name);
//! \~english Returns only entries in this branch that have no children.
//! \~russian Возвращает только записи этой ветви без дочерних элементов.
Branch getLeaves();
//! \~english Returns only entries in this branch that have children.
//! \~russian Возвращает только записи этой ветви, имеющие дочерние элементы.
Branch getBranches();
//! \~english Removes entries whose names do not contain "f".
//! \~russian Удаляет записи, чьи имена не содержат "f".
Branch & filter(const PIString & f);
//! \~english Returns \b true if any entry in this branch or its descendants has name "name".
//! \~russian Возвращает \b true, если какая-либо запись этой ветви или ее потомков имеет имя "name".
bool isEntryExists(const PIString & name) const {
for (const auto * i: *this)
if (entryExists(i, name)) return true;
return false;
}
//! \~english Returns position of entry pointer "e" inside this branch, or -1.
//! \~russian Возвращает позицию указателя на запись "e" в этой ветви или -1.
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;
};
//! \ingroup IO
//! \~\brief
//! \~english Node of the parsed configuration tree.
//! \~russian Узел разобранного дерева конфигурации.
//! \~\details
//! \~english Stores entry name, value, type mark, inline comment and child entries derived from dotted names.
//! \~russian Хранит имя записи, значение, метку типа, встроенный комментарий и дочерние записи, полученные из точечных имен.
class PIP_EXPORT Entry {
friend class PIConfig;
friend class Branch;
public:
//! \~english Constructs an empty detached entry.
//! \~russian Создает пустую отсоединенную запись.
Entry() {
_parent = 0;
_line = -1;
}
//! \~english Returns parent entry, or \c 0 for the root and default placeholder entries.
//! \~russian Возвращает родительскую запись или \c 0 для корня и внутренних placeholder-записей по умолчанию.
Entry * parent() const { return _parent; }
//! \~english Returns direct children count.
//! \~russian Возвращает количество непосредственных дочерних записей.
int childCount() const { return _children.size_s(); }
//! \~english Returns direct children as \a PIConfig::Branch.
//! \~russian Возвращает непосредственных потомков как \a PIConfig::Branch.
Branch & children() const {
_children.delim = delim;
return _children;
}
//! \~english Returns direct child at position "index".
//! \~russian Возвращает непосредственного потомка с позицией "index".
Entry * child(const int index) const { return _children[index]; }
//! \~english Returns first direct child named "name".
//! \~russian Возвращает первого непосредственного потомка с именем "name".
Entry * findChild(const PIString & name) {
for (auto * i: _children)
if (i->_name == name) return i;
return 0;
}
//! \~english Returns first direct child named "name".
//! \~russian Возвращает первого непосредственного потомка с именем "name".
const Entry * findChild(const PIString & name) const {
for (const auto * i: _children)
if (i->_name == name) return i;
return 0;
}
//! \~english Returns \b true when the entry has no children.
//! \~russian Возвращает \b true, когда у записи нет дочерних элементов.
bool isLeaf() const { return _children.isEmpty(); }
//! \~english Returns local entry name without parent prefix.
//! \~russian Возвращает локальное имя записи без родительского префикса.
const PIString & name() const { return _name; }
//! \~english Returns raw stored value.
//! \~russian Возвращает исходное сохраненное значение.
const PIString & value() const { return _value; }
//! \~english Returns one-letter stored type mark.
//! \~russian Возвращает сохраненную однобуквенную метку типа.
const PIString & type() const { return _type; }
//! \~english Returns inline comment stored after the type mark.
//! \~russian Возвращает встроенный комментарий, сохраненный после метки типа.
const PIString & comment() const { return _comment; }
/*!
* \~\brief
* \~english Returns full dotted name as it appears in the tree.
* \~russian Возвращает полное точечное имя в дереве.
*
* \~\details
* \~english Default placeholder entries always have empty full name.
* \~russian У placeholder-записей по умолчанию полное имя всегда пустое.
* \snippet piconfig.cpp fullName
*/
const PIString & fullName() const { return _full_name; }
//! \~english Sets local name to "value" and returns this entry.
//! \~russian Устанавливает локальное имя в "value" и возвращает эту запись.
Entry & setName(const PIString & value) {
_name = value;
return *this;
}
//! \~english Sets stored type mark to "value" and returns this entry.
//! \~russian Устанавливает сохраненную метку типа в "value" и возвращает эту запись.
Entry & setType(const PIString & value) {
_type = value;
return *this;
}
//! \~english Sets inline comment to "value" and returns this entry.
//! \~russian Устанавливает встроенный комментарий в "value" и возвращает эту запись.
Entry & setComment(const PIString & value) {
_comment = value;
return *this;
}
//! \~english Sets raw stored value to "value" and returns this entry.
//! \~russian Устанавливает исходное сохраненное значение в "value" и возвращает эту запись.
Entry & setValue(const PIString & value) {
_value = value;
return *this;
}
//! \~english Stores string list value and marks entry type as "l".
//! \~russian Сохраняет список строк и помечает тип записи как "l".
Entry & setValue(const PIStringList & value) {
setValue(value.join("%|%"));
setType("l");
return *this;
}
//! \~english Stores C-string value and marks entry type as "s".
//! \~russian Сохраняет значение C-строки и помечает тип записи как "s".
Entry & setValue(const char * value) {
setValue(PIString(value));
setType("s");
return *this;
}
//! \~english Stores boolean value and marks entry type as "b".
//! \~russian Сохраняет логическое значение и помечает тип записи как "b".
Entry & setValue(const bool value) {
setValue(PIString::fromBool(value));
setType("b");
return *this;
}
//! \~english Stores character value and marks entry type as "s".
//! \~russian Сохраняет символьное значение и помечает тип записи как "s".
Entry & setValue(const char value) {
setValue(PIString(1, value));
setType("s");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const short value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const int value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const long value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const uchar value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const ushort value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const uint value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores numeric value and marks entry type as "n".
//! \~russian Сохраняет числовое значение и помечает тип записи как "n".
Entry & setValue(const ulong value) {
setValue(PIString::fromNumber(value));
setType("n");
return *this;
}
//! \~english Stores floating-point value and marks entry type as "f".
//! \~russian Сохраняет вещественное значение и помечает тип записи как "f".
Entry & setValue(const float value) {
setValue(PIString::fromNumber(value));
setType("f");
return *this;
}
//! \~english Stores floating-point value and marks entry type as "f".
//! \~russian Сохраняет вещественное значение и помечает тип записи как "f".
Entry & setValue(const double value) {
setValue(PIString::fromNumber(value));
setType("f");
return *this;
}
/*!
* \~\brief
* \~english Resolves descendant "vname" below this entry.
* \~russian Разрешает потомка "vname" ниже этой записи.
*
* \~\details
* \~english If lookup fails, returns a shared default entry filled with "def" and sets \a exists to \b false when provided.
* \~russian Если поиск не удался, возвращает общий внутренний entry со значением "def" и устанавливает \a exists в \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)
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
//! \~english Typed overloads of \a getValue() convert "def" to the stored string form before lookup.
//! \~russian Типизированные перегрузки \a getValue() преобразуют "def" в сохраненную строковую форму перед поиском.
//! \~english Returns direct children whose names contain substring "vname".
//! \~russian Возвращает непосредственных потомков, чьи имена содержат подстроку "vname".
Branch getValues(const PIString & vname);
//! \~english Returns \b true if this entry or any descendant has name "name".
//! \~russian Возвращает \b true, если эта запись или любой ее потомок имеет имя "name".
bool isEntryExists(const PIString & name) const { return entryExists(this, name); }
//! \~english Converts stored value to \c bool.
//! \~russian Преобразует сохраненное значение в \c bool.
bool toBool() const { return _value.toBool(); }
//! \~english Converts stored value to \c char.
//! \~russian Преобразует сохраненное значение в \c char.
char toChar() const { return (_value.isEmpty() ? 0 : _value[0].toAscii()); }
//! \~english Converts stored value to \c short.
//! \~russian Преобразует сохраненное значение в \c short.
short toShort() const { return _value.toShort(); }
//! \~english Converts stored value to \c int.
//! \~russian Преобразует сохраненное значение в \c int.
int toInt() const { return _value.toInt(); }
//! \~english Converts stored value to \c long.
//! \~russian Преобразует сохраненное значение в \c long.
long toLong() const { return _value.toLong(); }
//! \~english Converts stored value to \c uchar.
//! \~russian Преобразует сохраненное значение в \c uchar.
uchar toUChar() const { return _value.toInt(); }
//! \~english Converts stored value to \c ushort.
//! \~russian Преобразует сохраненное значение в \c ushort.
ushort toUShort() const { return _value.toShort(); }
//! \~english Converts stored value to \c uint.
//! \~russian Преобразует сохраненное значение в \c uint.
uint toUInt() const { return _value.toInt(); }
//! \~english Converts stored value to \c ulong.
//! \~russian Преобразует сохраненное значение в \c ulong.
ulong toULong() const { return _value.toLong(); }
//! \~english Converts stored value to \c float.
//! \~russian Преобразует сохраненное значение в \c float.
float toFloat() const { return _value.toFloat(); }
//! \~english Converts stored value to \c double.
//! \~russian Преобразует сохраненное значение в \c double.
double toDouble() const { return _value.toDouble(); }
//! \~english Returns stored value as \a PIString.
//! \~russian Возвращает сохраненное значение как \a PIString.
PIString toString() const { return _value; }
//! \~english Splits stored list value into \a PIStringList using internal list separator.
//! \~russian Разбивает сохраненное списковое значение в \a 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 Opens and parses configuration file at "path".
//! \~russian Открывает и разбирает файл конфигурации по пути "path".
bool open(const PIString & path, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Opens and parses configuration stored in "string".
//! \~russian Открывает и разбирает конфигурацию, хранящуюся в "string".
bool open(PIString * string, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Opens and parses configuration from custom device "device".
//! \~russian Открывает и разбирает конфигурацию из пользовательского устройства "device".
bool open(PIIODevice * device, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);
//! \~english Returns whether a backing device is currently opened.
//! \~russian Возвращает, открыто ли сейчас базовое устройство.
bool isOpened() const;
//! \~english Resolves top-level path "vname".
//! \~russian Разрешает путь верхнего уровня "vname".
//! \~\details
//! \~english If lookup fails, returns a shared default entry filled with "def" and sets \a exists to \b false when provided.
//! \~russian Если поиск не удался, возвращает общий внутренний entry со значением "def" и устанавливает \a exists в \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<PIConfig *>(this)->getValue(vname, def, exists);
}
PICONFIG_GET_VALUE
//! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0)
//! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0)
//! \~english Typed overloads of \a getValue() convert "def" to the stored string form before lookup.
//! \~russian Типизированные перегрузки \a getValue() преобразуют "def" в сохраненную строковую форму перед поиском.
//! \~english Returns top-level entries whose names contain substring "vname".
//! \~russian Возвращает записи верхнего уровня, чьи имена содержат подстроку "vname".
Branch getValues(const PIString & vname);
//! \~english Sets or creates top-level path "name", stores "value", assigns type mark "type" and optionally writes changes immediately.
//! \~russian Устанавливает или создает путь верхнего уровня "name", сохраняет "value", назначает метку типа "type" и при необходимости сразу записывает изменения.
void setValue(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
//! \~english Stores string list and marks type as "l".
//! \~russian Сохраняет список строк и помечает тип как "l".
void setValue(const PIString & name, const PIStringList & value, bool write = true) { setValue(name, value.join("%|%"), "l", write); }
//! \~english Stores C-string and marks type as "s".
//! \~russian Сохраняет C-строку и помечает тип как "s".
void setValue(const PIString & name, const char * value, bool write = true) { setValue(name, PIString(value), "s", write); }
//! \~english Stores boolean value and marks type as "b".
//! \~russian Сохраняет логическое значение и помечает тип как "b".
void setValue(const PIString & name, const bool value, bool write = true) { setValue(name, PIString::fromBool(value), "b", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const short value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const int value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const long value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const uchar value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const ushort value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const uint value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores numeric value and marks type as "n".
//! \~russian Сохраняет числовое значение и помечает тип как "n".
void setValue(const PIString & name, const ulong value, bool write = true) { setValue(name, PIString::fromNumber(value), "n", write); }
//! \~english Stores floating-point value and marks type as "f".
//! \~russian Сохраняет вещественное значение и помечает тип как "f".
void setValue(const PIString & name, const float value, bool write = true) { setValue(name, PIString::fromNumber(value), "f", write); }
//! \~english Stores floating-point value and marks type as "f".
//! \~russian Сохраняет вещественное значение и помечает тип как "f".
void setValue(const PIString & name, const double value, bool write = true) { setValue(name, PIString::fromNumber(value), "f", write); }
//! \~english Returns root entry of the parsed tree.
//! \~russian Возвращает корневую запись разобранного дерева.
Entry & rootEntry() { return root; }
//! \~english Returns total number of parsed entries below the root.
//! \~russian Возвращает общее количество разобранных записей ниже корня.
int entriesCount() const { return childCount(&root); }
//! \~english Returns \b true if any parsed entry path contains name "name".
//! \~russian Возвращает \b true, если среди разобранных путей есть запись с именем "name".
bool isEntryExists(const PIString & name) const { return entryExists(&root, name); }
//! \~english Returns all direct children of the root entry.
//! \~russian Возвращает всех непосредственных потомков корневой записи.
Branch allTree() {
Branch b;
for (auto * i: root._children)
b << i;
b.delim = delim;
return b;
}
//! \~english Returns all stored leaves and valued branch entries sorted by source order.
//! \~russian Возвращает все сохраненные листья и ветви со значением, отсортированные по порядку в источнике.
Branch allLeaves() {
Branch b;
allLeaves(b, &root);
b.sort(Entry::compare);
b.delim = delim;
return b;
}
//! \~english Returns index of path "name" inside \a allLeaves(), or -1.
//! \~russian Возвращает индекс пути "name" внутри \a allLeaves() или -1.
int entryIndex(const PIString & name);
//! \~english Returns entry name by \a allLeaves() index.
//! \~russian Возвращает имя записи по индексу в \a allLeaves().
PIString getName(uint number) { return entryByIndex(number)._name; }
//! \~english Returns entry value by \a allLeaves() index.
//! \~russian Возвращает значение записи по индексу в \a allLeaves().
PIString getValueByIndex(uint number) { return entryByIndex(number)._value; }
//! \~english Returns entry type mark by \a allLeaves() index.
//! \~russian Возвращает метку типа записи по индексу в \a allLeaves().
PIChar getType(uint number) { return entryByIndex(number)._type[0]; }
//! \~english Returns entry comment by \a allLeaves() index.
//! \~russian Возвращает комментарий записи по индексу в \a allLeaves().
PIString getComment(uint number) { return entryByIndex(number)._comment; }
//! \~english Creates new path "name" when it does not already exist and optionally writes changes immediately.
//! \~russian Создает новый путь "name", если он еще не существует, и при необходимости сразу записывает изменения.
void addEntry(const PIString & name, const PIString & value, const PIString & type = "s", bool write = true);
//! \~english Renames entry referenced by \a allLeaves() index "number".
//! \~russian Переименовывает запись, на которую ссылается индекс "number" в \a allLeaves().
void setName(uint number, const PIString & name, bool write = true);
//! \~english Replaces stored value of entry referenced by \a allLeaves() index "number".
//! \~russian Заменяет сохраненное значение записи, на которую ссылается индекс "number" в \a allLeaves().
void setValue(uint number, const PIString & value, bool write = true);
//! \~english Replaces type mark of entry referenced by \a allLeaves() index "number".
//! \~russian Заменяет метку типа записи, на которую ссылается индекс "number" в \a allLeaves().
void setType(uint number, const PIString & type, bool write = true);
//! \~english Replaces comment of entry referenced by \a allLeaves() index "number".
//! \~russian Заменяет комментарий записи, на которую ссылается индекс "number" в \a allLeaves().
void setComment(uint number, const PIString & comment, bool write = true);
//! \~english Removes entry path "name" and its subtree when needed.
//! \~russian Удаляет путь записи "name" и при необходимости его поддерево.
void removeEntry(const PIString & name, bool write = true);
//! \~english Removes entry referenced by \a allLeaves() index "number".
//! \~russian Удаляет запись, на которую ссылается индекс "number" в \a allLeaves().
void removeEntry(uint number, bool write = true);
//! \~english Removes all parsed entries and clears the backing device content.
//! \~russian Удаляет все разобранные записи и очищает содержимое базового устройства.
void clear();
//! \~english Rebuilds internal tree from current device contents.
//! \~russian Перестраивает внутреннее дерево из текущего содержимого устройства.
void readAll();
//! \~english Writes current tree back to the device and reparses it.
//! \~russian Записывает текущее дерево обратно в устройство и разбирает его заново.
void writeAll();
//! \~english Returns current path delimiter, "." by default.
//! \~russian Возвращает текущий разделитель путей, по умолчанию ".".
const PIString & delimiter() const { return delim; }
//! \~english Sets path delimiter for subsequent parsing and reparses the device.
//! \~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
//! \~english Writes branch contents to \a std::ostream in tree form.
//! \~russian Выводит содержимое ветви в \a std::ostream в виде дерева.
PIP_EXPORT std::ostream & operator<<(std::ostream & s, const PIConfig::Branch & v);
//! \~english Writes entry value to \a std::ostream.
//! \~russian Выводит значение записи в \a std::ostream.
PIP_EXPORT std::ostream & operator<<(std::ostream & s, const PIConfig::Entry & v);
#endif
//! \~english Writes branch contents to \a PICout in tree form.
//! \~russian Выводит содержимое ветви в \a PICout в виде дерева.
inline PICout operator<<(PICout s, const PIConfig::Branch & v) {
s.saveAndSetControls(0);
v.piCoutt(s, "");
s.restoreControls();
return s;
}
//! \~english Writes entry value, type and comment to \a PICout.
//! \~russian Выводит значение, тип и комментарий записи в \a PICout.
inline PICout operator<<(PICout s, const PIConfig::Entry & v) {
s << v.value() << "(" << v.type() << v.comment() << ")";
return s;
}
/*!
* \relatesalso PIConfig
* \relatesalso PIIODevice
* \~\brief
* \~english Helper for reading device settings from configuration entries.
* \~russian Вспомогательная функция для чтения настроек устройства из записей конфигурации.
*
* \~\details
* \~english Tries to read "name" from parent section \a ep first, then from local section \a em, and falls back to "def" when neither exists.
* \~russian Сначала пытается прочитать "name" из родительской секции \a ep, затем из локальной секции \a em и возвращает "def", если запись не найдена.
*/
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