From 0bafd3fa984b72189aeb71b0fc7f7dacce52c46f Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 9 Jul 2024 21:44:30 +0300 Subject: [PATCH] PIValueTree improvements: methods with path (recursive), forEachRecursive() PIValueTreeConvertions::fromTextFile now can include other files and handle ${} substitutions --- .../serialization/pivaluetree_conversions.cpp | 32 ++++++++----- libs/main/types/pivaluetree.cpp | 47 ++++++++++++++++++- libs/main/types/pivaluetree.h | 27 +++++++++++ 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/libs/main/serialization/pivaluetree_conversions.cpp b/libs/main/serialization/pivaluetree_conversions.cpp index 490925ff..2ec6e7e7 100644 --- a/libs/main/serialization/pivaluetree_conversions.cpp +++ b/libs/main/serialization/pivaluetree_conversions.cpp @@ -127,9 +127,10 @@ PIValueTree prepareFromText(const PIValueTree & root) { PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { PIValueTree ret; + PIMap substitutions; if (!device) return ret; PIString base_path; - if (device->isTypeOf()) base_path = PIFile::FileInfo(device->path()).dir(); + if (device->isTypeOf()) base_path = PIFile::FileInfo(device->path()).dir().replaceAll('\\', '/'); PIIOTextStream ts(device); PIString line, comm; PIVariant value; @@ -165,6 +166,7 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { if (ind > 0) if (line[ind - 1] == '\\') continue; cind = ind; + break; } if (cind >= 0) { comm = line.takeMid(cind + 1); @@ -198,26 +200,32 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { path << line.takeLeft(ind).split('.').trim(); line.cutLeft(1).trim(); if (path.front() == "include") { - /*name = line.mid(ind + 1).trimmed(); - PIConfig * iconf = new PIConfig(name, incdirs); - //piCout << "include" << name << iconf->dev; - if (!iconf->dev) { - delete iconf; - } else { - inc_devs << iconf; - includes << iconf << iconf->includes; - updateIncludes(); + PIString include = line.trimmed(); + if (!PIFile::FileInfo(include).isAbsolute()) { + include = base_path + "/" + include.replaceAll('\\', '/'); + include.replaceAll("//", '/'); } - other.back() = src;*/ + PIValueTree inc_vt = PIValueTreeConversions::fromTextFile(include); + inc_vt.forEachRecursive( + [&substitutions](const PIValueTree & v, const PIString & fn) { substitutions[fn] = v.value().toString(); }); + path.clear(); } } else { line.clear(); continue; } // piCout << path << line << comm; + if (path.isEmpty()) { + line.clear(); + continue; + } PIValueTree & leaf(ret[path]); leaf.setComment(comm); - leaf.setValue(unmask(line)); + line = unmask(line); + for (const auto & s: substitutions) + line.replaceAll("${" + s.first + "}", s.second); + leaf.setValue(line); + substitutions[path.join('.')] = leaf.value().toString(); if (!path.contains(_attribute_)) if (!leaf.contains({_attribute_, "type"})) leaf[_attribute_].addChild({"type", type}); line.clear(); diff --git a/libs/main/types/pivaluetree.cpp b/libs/main/types/pivaluetree.cpp index cadce7e9..eaeddb4d 100644 --- a/libs/main/types/pivaluetree.cpp +++ b/libs/main/types/pivaluetree.cpp @@ -125,12 +125,24 @@ void PIValueTree::applyValues(const PIValueTree & root, bool recursive) { PIVariant PIValueTree::childValue(const PIString & child_name, const PIVariant & default_value, bool * exists) const { - if (!contains(child_name)) { + const PIValueTree & node = child(child_name); + if (node.isNull()) { if (exists) *exists = false; return default_value; } - return child(child_name).value(); if (exists) *exists = true; + return node.value(); +} + + +PIVariant PIValueTree::childValue(const PIStringList & child_path, const PIVariant & default_value, bool * exists) const { + const PIValueTree & node = child(child_path); + if (node.isNull()) { + if (exists) *exists = false; + return default_value; + } + if (exists) *exists = true; + return node.value(); } @@ -167,6 +179,15 @@ const PIValueTree & PIValueTree::child(const PIString & name) const { } +const PIValueTree & PIValueTree::child(const PIStringList & path) const { + if (_is_null || path.isEmpty()) return *this; + const PIValueTree * ret = &child(path[0]); + for (int i = 1; i < path.size_s(); ++i) + ret = &child(path[i]); + return *ret; +} + + PIValueTree & PIValueTree::child(const PIString & name) { if (_is_null) return *this; for (auto & c: _children) @@ -175,6 +196,15 @@ PIValueTree & PIValueTree::child(const PIString & name) { } +PIValueTree & PIValueTree::child(const PIStringList & path) { + if (_is_null || path.isEmpty()) return *this; + PIValueTree * ret = &child(path[0]); + for (int i = 1; i < path.size_s(); ++i) + ret = &child(path[i]); + return *ret; +} + + PIValueTree & PIValueTree::insertChild(int index, const PIValueTree & n) { if (_is_null) return *this; for (auto & c: _children) @@ -214,6 +244,11 @@ PIValueTree & PIValueTree::remove(const PIString & name) { } +void PIValueTree::forEachRecursive(std::function func) { + forEachRecursiveInternal(func); +} + + const PIStringList & PIValueTree::standardAttributes() { static PIStringList ret = { // clang-format off @@ -293,3 +328,11 @@ PIValueTree & PIValueTree::nullValue() { ret._is_null = true; return ret; } + + +void PIValueTree::forEachRecursiveInternal(std::function func, PIString prefix) { + for (auto & c: _children) { + func(c, prefix + c.name()); + c.forEachRecursiveInternal(func, c.name() + "."); + } +} diff --git a/libs/main/types/pivaluetree.h b/libs/main/types/pivaluetree.h index bcd091b4..db4e2a1f 100644 --- a/libs/main/types/pivaluetree.h +++ b/libs/main/types/pivaluetree.h @@ -191,6 +191,17 @@ public: //! \param exists Если не равно нулю, будет установлено в true, если дочерний узел существует, false в противном случае. PIVariant childValue(const PIString & child_name, const PIVariant & default_value = PIVariant(), bool * exists = nullptr) const; + //! \~\brief + //! \~english Returns the value of a child node with a given path. + //! \param child_path The path of the child node. + //! \param default_value The default value to be returned if the child node is not found. + //! \param exists If not null, set to true if the child node exists, false otherwise. + //! \~russian Возвращает значение дочернего элемента с заданным путем. + //! \param child_path Путь дочернего узла. + //! \param default_value Значение по умолчанию, которое будет возвращено, если дочерний узел не найден. + //! \param exists Если не равно нулю, будет установлено в true, если дочерний узел существует, false в противном случае. + PIVariant childValue(const PIStringList & child_path, const PIVariant & default_value = PIVariant(), bool * exists = nullptr) const; + //! \~\brief //! \~english Reads the value of a child node with a given name into "read_to". Returns a reference to the current %PIValueTree object. //! \~russian Читает значение дочернего элемента с заданным именем в "read_to". Возвращает ссылку на текущий объект %PIValueTree. @@ -220,11 +231,21 @@ public: //! \~russian Возвращает константную ссылку на дочерний узел с заданным именем или пустой узел, если его нет. const PIValueTree & child(const PIString & name) const; + //! \~\brief + //! \~english Returns a const reference to the child node with a given path, or null node if it doesn`t exists. + //! \~russian Возвращает константную ссылку на дочерний узел с заданным путем или пустой узел, если его нет. + const PIValueTree & child(const PIStringList & path) const; + //! \~\brief //! \~english Returns a reference to the child node with a given name, or null node if it doesn`t exists. //! \~russian Возвращает ссылку на дочерний узел с заданным именем или пустой узел, если его нет. PIValueTree & child(const PIString & name); + //! \~\brief + //! \~english Returns a reference to the child node with a given path, or null node if it doesn`t exists. + //! \~russian Возвращает ссылку на дочерний узел с заданным путем или пустой узел, если его нет. + PIValueTree & child(const PIStringList & path); + //! \~\brief //! \~english Inserts a node at a given index. Returns a reference to the current %PIValueTree object. //! \~russian Вставляет узел в заданном индексе. Возвращает ссылку на текущий объект %PIValueTree. @@ -260,6 +281,11 @@ public: //! \~russian Возвращает константную ссылку на дочерний узел с заданным именем или пустой узел, если его нет. const PIValueTree & operator[](const PIString & name) const { return child(name); } + //! \~\brief + //! \~english Recursive call "func" for every child. "full_name" is a name with path, joined with ".". + //! \~russian Рекурсивно выполняет "func" для каждого узла. "full_name" - это имя с путём, соединены ".". + void forEachRecursive(std::function func); + //! \~\brief //! \~english Returns a list of standard attribute names. //! \~russian Возвращает список стандартных имен атрибутов. @@ -268,6 +294,7 @@ public: private: static void print(PIString & s, const PIValueTree & v, PIString tab); static PIValueTree & nullValue(); + void forEachRecursiveInternal(std::function func, PIString prefix = {}); PIString _name; PIString _comment;