diff --git a/libs/main/serialization/pivaluetree_conversions.cpp b/libs/main/serialization/pivaluetree_conversions.cpp index cc275542..810c255b 100644 --- a/libs/main/serialization/pivaluetree_conversions.cpp +++ b/libs/main/serialization/pivaluetree_conversions.cpp @@ -23,6 +23,40 @@ #include "piiostring.h" #include "piiostream.h" +const char _attribute_[] = "_attribute_"; + +PIString unmask(const PIString & str) { + PIString ret; + for (int i = 0; i < str.size_s(); ++i) { + if (str[i] == '\\') { + if (i < str.size_s() - 1) { + ++i; + ret += str[i]; + continue; + } + } + ret += str[i]; + } + return ret; +} + +PIString mask(const PIString & str) { + PIString ret = str; + if (ret.isEmpty()) return ret; + int start = 0; + if (ret[0].isSpace()) { + ret.insert(0, '\\'); + start = 1; + } + for (int i = start; i < ret.size_s(); ++i) { + if (ret[i] == '\\') { + ret.insert(i, '\\'); + ++i; + } + } + return ret; +} + PIValueTree PIValueTreeConversions::fromPropertyStorage(const PIPropertyStorage & ps) { PIValueTree ret; @@ -88,6 +122,25 @@ PIValueTree PIValueTreeConversions::fromJSON(const PIJSON & json) { } +PIValueTree prepareFromText(const PIValueTree & root) { + PIValueTree ret; + ret.setName(root.name()); + ret.setComment(root.comment()); + if (root.contains(_attribute_)) { + for (const auto & a: root.child(_attribute_).children()) + ret.attributes()[a.name()] = a.value().toString(); + } + PIVariant value = PIVariant::fromType(ret.attribute("type", PIVariant::typeName()).toString()); + value.setValueFromString(root.value().toString()); + ret.setValue(value); + for (const auto & c: root.children()) { + if (c.name() != _attribute_) + ret[c.name()] = prepareFromText(c); + } + return ret; +} + + PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { PIValueTree ret; if (!device) return ret; @@ -96,7 +149,7 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { PIIOTextStream ts(device); PIString line, comm; PIVariant value; - PIChar type; + PIString type; PIStringList prefix, path; while (!ts.isEnd()) { PIString l = ts.readLine().trim(); @@ -115,7 +168,7 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { line.clear(); continue; } - type = 's'; + type = PIVariant::typeName(); comm.clear(); int ind = line.find('#'); if (ind >= 0) { @@ -126,7 +179,18 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { else if (comm[0].isAlpha() && comm[1].isSpace()) typed = true; } if (typed) { - type = comm.takeLeft(1)[0]; + switch (comm.takeLeft(1)[0].toAscii()) { + case 'b': type = PIVariant::typeName(); break; + case 'n': type = PIVariant::typeName(); break; + case 'f': type = PIVariant::typeName(); break; + case 'l': type = PIVariant::typeName(); break; + case 'c': type = PIVariant::typeName(); break; + case 'p': + case 'v': type = PIVariant::typeName(); break; + case 'r': + case 'a': type = PIVariant::typeName(); break; + default: break; + } } comm.trim(); line.cutRight(1).trim(); @@ -134,7 +198,7 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { ind = line.find('='); if ((ind > 0) && (line[0] != '#')) { path = prefix; - path << line.takeLeft(ind).trim().split('.'); + path << line.takeLeft(ind).split('.').trim(); line.cutLeft(1).trim(); if (path.front() == "include") { /*name = line.mid(ind + 1).trimmed(); @@ -151,39 +215,31 @@ PIValueTree PIValueTreeConversions::fromText(PIIODevice * device) { } } //piCout << path << line << comm; - switch (type.toAscii()) { - case 'b': value = line.toBool(); break; - case 'n': value = line.toInt(); break; - case 'f': value = line.toDouble(); break; - case 'l': value = line.split("%|%"); break; - case 'c': value = PIVariantTypes::Color(line.toUInt()); break; - case 'p': - case 'v': value = PIVariant(line).toPoint(); break; - case 'r': - case 'a': value = PIVariant(line).toRect(); break; - default: value = line; - } PIValueTree & leaf(ret[path]); - value.setValueFromString(line); leaf.setComment(comm); - leaf.setValue(value); + leaf.setValue(unmask(line)); + if (!path.contains(_attribute_)) + if (!leaf.contains({_attribute_, "type"})) + leaf[_attribute_].addChild({"type", type}); line.clear(); } - return ret; + return prepareFromText(ret); + //return ret; } -PIJSON toJSONTree(const PIValueTree & root) { +PIJSON toJSONTree(const PIValueTree & root, PIValueTreeConversions::Options options) { PIJSON ret = PIJSON::newObject(); if (root.name().isNotEmpty()) ret["name"] = root.name(); - if (root.comment().isNotEmpty()) + if (root.comment().isNotEmpty() && options[PIValueTreeConversions::WithComment]) ret["comment"] = root.comment(); if (root.value().isValid()) { - ret["type"] = root.value().typeName(); + if (options[PIValueTreeConversions::WithType]) + ret["type"] = root.value().typeName(); ret["value"] = root.value().toString(); } - if (root.attributes().isNotEmpty()) { + if (root.attributes().isNotEmpty() && options[PIValueTreeConversions::WithAttributes]) { PIJSON j; for (const auto & a: root.attributes()) j[a.first] = a.second.toString(); @@ -192,22 +248,79 @@ PIJSON toJSONTree(const PIValueTree & root) { if (root.hasChildren()) { PIJSON j; for (const auto & c: root.children()) - j << toJSONTree(c); + j << toJSONTree(c, options); ret["children"] = j; } return ret; } -PIJSON PIValueTreeConversions::toJSON(const PIValueTree & root) { +PIJSON PIValueTreeConversions::toJSON(const PIValueTree & root, Options options) { PIJSON ret = PIJSON::newArray(); for (const auto & c: root.children()) - ret << toJSONTree(c); + ret << toJSONTree(c, options); return ret; } -PIString PIValueTreeConversions::toText(const PIValueTree & root) { +PIString toTextTreeAttributes(const PIValueTree & root, PIString prefix, PIValueTreeConversions::Options options) { PIString ret; + for (const auto & a: root.attributes()) { + if (a.first == "type") continue; + ret += prefix + _attribute_ + "." + a.first; + ret += " = " + mask(a.second.toString()); + ret += '\n'; + } + if (options[PIValueTreeConversions::WithType]) { + ret += prefix + _attribute_ + ".type"; + ret += " = " + root.value().typeName(); + ret += '\n'; + } return ret; } + + +PIString toTextTree(const PIValueTree & root, PIString prefix, PIValueTreeConversions::Options options) { + PIString ret; + if (prefix.isNotEmpty()) prefix += '.'; + prefix += root.name(); + if (root.hasChildren()) { + ret += "\n[" + prefix + "]\n"; + if (root.isArray() && options[PIValueTreeConversions::WithAttributes]) + ret += toTextTreeAttributes(root, "", options); + for (const auto & c: root.children()) { + PIString cp = prefix; + ret += toTextTree(c, prefix, options); + } + } else { + if (root.value().isValid()) { + ret += root.name() + " = "; + ret += mask(root.value().toString()).replacedAll('\n', " \\\n"); + } + if (root.comment().isNotEmpty() && options[PIValueTreeConversions::WithComment]) { + ret += " # "; + ret += root.comment(); + } + ret += "\n"; + if (options[PIValueTreeConversions::WithAttributes]) + ret += toTextTreeAttributes(root, root.name() + ".", options); + } + + return ret; +} + + +PIString PIValueTreeConversions::toText(const PIValueTree & root, Options options) { + PIString ret; + for (const auto & c: root.children()) { + ret += toTextTree(c, PIString(), options); + } + + return ret; +} + + +PIValueTree PIValueTreeConversions::fromText(const PIString & str) { + PIIOString dev(str); + return fromText(&dev); +} diff --git a/libs/main/serialization/pivaluetree_conversions.h b/libs/main/serialization/pivaluetree_conversions.h index 429b917b..69de4922 100644 --- a/libs/main/serialization/pivaluetree_conversions.h +++ b/libs/main/serialization/pivaluetree_conversions.h @@ -33,12 +33,24 @@ class PIJSON; class PIIODevice; namespace PIValueTreeConversions { + + enum Option { + WithAttributes = 0x1, + WithComment = 0x2, + WithType = 0x4, + WithAll = 0xFFFFFF, + Default = WithAll + }; + typedef PIFlags