322 lines
8.8 KiB
C++
322 lines
8.8 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "pivaluetree_conversions.h"
|
|
|
|
#include "pifile.h"
|
|
#include "piiostream.h"
|
|
#include "piiostring.h"
|
|
#include "pijson.h"
|
|
#include "pipropertystorage.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<PIString>()).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<PIFile>()) 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<PIString>();
|
|
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<bool>(); break;
|
|
case 'n': type = PIVariant::typeName<int>(); break;
|
|
case 'f': type = PIVariant::typeName<double>(); break;
|
|
case 'l': type = PIVariant::typeName<PIStringList>(); break;
|
|
case 'c': type = PIVariant::typeName<PIVariantTypes::Color>(); break;
|
|
case 'p':
|
|
case 'v': type = PIVariant::typeName<PIPointd>(); break;
|
|
case 'r':
|
|
case 'a': type = PIVariant::typeName<PIRectd>(); 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) {
|
|
if (options[IncludeRoot]) return toJSONTree(root, 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);
|
|
}
|