/* PIP - Platform Independent Primitives PIValueTree conversions 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 . */ #include "pivaluetree_conversions.h" #include "pipropertystorage.h" #include "pijson.h" #include "pifile.h" #include "piiostring.h" #include "piiostream.h" const char _attribute_[] = "_attribute_"; PIString unmask(const PIString & str) { PIString ret(str); ret.unmask("#\\"); if (ret.isNotEmpty()) if (ret[0] == '\\') ret.remove(0); return ret; } PIString mask(const PIString & str) { PIString ret = str; if (ret.isEmpty()) return ret; ret.mask("#\\"); if (ret[0].isSpace()) ret.insert(0, '\\'); return ret; } PIValueTree PIValueTreeConversions::fromPropertyStorage(const PIPropertyStorage & ps) { PIValueTree ret; for (const auto & i: ps) { PIValueTree v; v.setName(i.name); v.setValue(i.value); v.setComment(i.comment); ret.addChild(v); } return ret; } PIValueTree PIValueTreeConversions::fromVariantMap(const PIVariantMap & vm) { PIValueTree ret; for (const auto & i: vm) { PIValueTree v; v.setName(i.first); v.setValue(i.second); ret.addChild(v); } return ret; } PIValueTree fromJSONTree(const PIJSON & json) { PIValueTree ret; ret.setName(json["name"].toString()); ret.setComment(json["comment"].toString()); PIString value_str = json["value"].toString(); PIVariant value = PIVariant::fromType(json["type"].toString()); if (value_str.isNotEmpty()) { if (value.isValid()) value.setValueFromString(value_str); else value = value_str; } ret.setValue(value); const PIJSON & attr(json["attributes"]); for (const auto & i: attr.object()) ret.attributes()[i.first] = i.second.value(); bool is_array = ret.isArray(); int chindex = 0; const PIJSON & chl(json["children"]); for (const auto & i: chl.array()) { PIValueTree c = fromJSONTree(i); if (is_array) c.setName(PIString::fromNumber(chindex++)); ret.addChild(c); } return ret; } PIValueTree PIValueTreeConversions::fromJSON(const PIJSON & json) { PIValueTree ret; if (json.isObject()) ret = fromJSONTree(json); if (json.isArray()) { for (const auto & i: json.array()) ret.addChild(fromJSONTree(i)); } return ret; } 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; PIString base_path; if (device->isTypeOf()) base_path = PIFile::FileInfo(device->path()).dir(); PIIOTextStream ts(device); PIString line, comm; PIVariant value; PIString type; PIStringList prefix, path; while (!ts.isEnd()) { PIString l = ts.readLine().trim(); if (l.endsWith(" \\")) { line += l.cutRight(2).trim() + "\n"; continue; } line += l; //piCout << "L" << line.replacedAll("\n", "\\n"); if (line.size() < 2) { line.clear(); continue; } if (line.startsWith('[')) { prefix = line.inBrackets('[', ']').trim().split('.'); line.clear(); continue; } if (line.startsWith('#')) { line.clear(); continue; } type = PIVariant::typeName(); comm.clear(); int ind = -1, cind = -1; for (;;) { ind = line.find('#', ind + 1); if (ind < 0) break; if (ind > 0) if (line[ind - 1] == '\\') continue; cind = ind; } if (cind >= 0) { comm = line.takeMid(cind + 1); bool typed = false; if (comm.isNotEmpty()) { if (comm.size() == 1) typed = true; else if (comm[0].isAlpha() && comm[1].isSpace()) typed = true; } if (typed) { 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(); } ind = line.find('='); if (ind > 0) { path = prefix; 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(); } other.back() = src;*/ } } else { line.clear(); continue; } //piCout << path << line << comm; PIValueTree & leaf(ret[path]); leaf.setComment(comm); leaf.setValue(unmask(line)); if (!path.contains(_attribute_)) if (!leaf.contains({_attribute_, "type"})) leaf[_attribute_].addChild({"type", type}); line.clear(); } return prepareFromText(ret); //return ret; } PIJSON toJSONTree(const PIValueTree & root, PIValueTreeConversions::Options options) { PIJSON ret = PIJSON::newObject(); if (root.name().isNotEmpty()) ret["name"] = root.name(); if (root.comment().isNotEmpty() && options[PIValueTreeConversions::WithComment]) ret["comment"] = root.comment(); if (root.value().isValid()) { if (options[PIValueTreeConversions::WithType]) ret["type"] = root.value().typeName(); ret["value"] = root.value().toString(); } if (root.attributes().isNotEmpty() && options[PIValueTreeConversions::WithAttributes]) { PIJSON j; for (const auto & a: root.attributes()) j[a.first] = a.second.toString(); ret["attributes"] = j; } if (root.hasChildren()) { PIJSON j; for (const auto & c: root.children()) j << toJSONTree(c, options); ret["children"] = j; } return ret; } PIJSON PIValueTreeConversions::toJSON(const PIValueTree & root, Options options) { PIJSON ret = PIJSON::newArray(); for (const auto & c: root.children()) ret << toJSONTree(c, options); return ret; } 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); }