//! \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 . */ #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 { 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(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(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(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 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 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