//! \~\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 . */ #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 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. //! Supports dotted paths, INI-style section prefixes, multiline values and \c include entries resolved during parsing. //! \~russian //! PIConfig предоставляет функциональность для чтения, записи и управления конфигурационными файлами в древовидной структуре. //! Поддерживает точечные пути, префиксы секций в стиле 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 Branch class - container for Entry objects //! \~russian Класс Branch - контейнер для объектов Entry class PIP_EXPORT Branch: public PIVector { 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() { ; } //! \~\brief //! \~english Get value from branch by name with default value //! \~russian Получить значение из ветки по имени со значением по умолчанию //! \~\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(this)->getValue(vname, def, exists); } PICONFIG_GET_VALUE //! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \~english Returns all leaf descendants reachable from this branch. //! \~russian Возвращает все листовые потомки, достижимые из этой ветви. Branch allLeaves(); //! \~english Get all entries with name containing specified substring //! \~russian Получить все записи с именем, содержащим указанную подстроку 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 Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \~\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(this)->getValue(vname, def, exists); } PICONFIG_GET_VALUE //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0) //! \~english Get entry with name "vname" and default value "def" //! \~russian Получить запись с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0) //! \~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(this)->getValue(vname, def, exists); } PICONFIG_GET_VALUE //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const char * def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const PIStringList & def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const bool def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const short def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const int def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const long def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uchar def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ushort def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const uint def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const ulong def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const float def, bool * exists = 0) //! \~english Get top-level entry with name "vname" and default value "def" //! \~russian Получить запись верхнего уровня с именем "vname" и значением по умолчанию "def" //! \fn Entry & getValue(const PIString & vname, const double def, bool * exists = 0) //! \~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 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 T readDeviceSetting(const PIString & name, const T & def, const PIConfig::Entry * em, const PIConfig::Entry * ep) { PIVariant v = PIVariant::fromValue(def); if (ep) { bool ex = false; v.setValueFromString(ep->getValue(name, def, &ex).toString()); if (ex) return v.value(); } v.setValueFromString(em->getValue(name, def).toString()); return v.value(); } #endif // PICONFIG_H