|
|
|
@@ -17,13 +17,15 @@
|
|
|
|
|
*/
|
|
|
|
|
#include "qetcontainerdb.h"
|
|
|
|
|
|
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
#include "qetcontainer.h"
|
|
|
|
|
|
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
#include <QHash>
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QSet>
|
|
|
|
|
#include <QTextStream>
|
|
|
|
|
#include <QUuid>
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
|
|
|
|
@@ -31,9 +33,9 @@
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
const QString KeyAttribute = QStringLiteral("qetz-key");
|
|
|
|
|
const QString FolioAttribute = QStringLiteral("qetz-folio");
|
|
|
|
|
//A folio written as it was read (stage D4): its part
|
|
|
|
|
const QString KeptPartAttribute = QStringLiteral("qetz-kept-part");
|
|
|
|
|
const QString FolioAttribute = QStringLiteral("qetz-folio");
|
|
|
|
|
//What a moved block leaves where it was. Removing it instead would leave
|
|
|
|
|
//the whitespace before and after it side by side, read back as one text
|
|
|
|
|
//node: the parent would come back a child short.
|
|
|
|
@@ -63,39 +65,63 @@ const QStringList FolioData = {
|
|
|
|
|
QStringLiteral("title"), QStringLiteral("author"), QStringLiteral("date"),
|
|
|
|
|
QStringLiteral("folio"), QStringLiteral("indexrev"), QStringLiteral("filename"),
|
|
|
|
|
QStringLiteral("locmach"), QStringLiteral("plant"), QStringLiteral("auto_page_num")};
|
|
|
|
|
//From format 3 on an item's uuid is its row's key, not a column of its
|
|
|
|
|
//own: it stays in the folio file, where it says which row is the item's
|
|
|
|
|
const QStringList ElementData = {
|
|
|
|
|
QStringLiteral("uuid"), QStringLiteral("type"), QStringLiteral("prefix"),
|
|
|
|
|
QStringLiteral("freezeLabel")};
|
|
|
|
|
QStringLiteral("type"), QStringLiteral("prefix"), QStringLiteral("freezeLabel")};
|
|
|
|
|
const QStringList ConductorData = {
|
|
|
|
|
QStringLiteral("uuid"), QStringLiteral("element1"), QStringLiteral("terminal1"),
|
|
|
|
|
QStringLiteral("element1"), QStringLiteral("terminal1"),
|
|
|
|
|
QStringLiteral("element2"), QStringLiteral("terminal2"), QStringLiteral("num"),
|
|
|
|
|
QStringLiteral("formula"), QStringLiteral("function"), QStringLiteral("tension_protocol"),
|
|
|
|
|
QStringLiteral("conductor_color"), QStringLiteral("conductor_section"),
|
|
|
|
|
QStringLiteral("cable"), QStringLiteral("bus"), QStringLiteral("freezeLabel")};
|
|
|
|
|
|
|
|
|
|
//Attributes held as 0 or 1 (format 3 on). A value QElectroTech does not
|
|
|
|
|
//write, "yes" say, stays in the folio file as it was.
|
|
|
|
|
const QStringList BooleanData = {QStringLiteral("freezeLabel")};
|
|
|
|
|
|
|
|
|
|
QString columns(const QStringList &names)
|
|
|
|
|
{
|
|
|
|
|
QStringList quoted;
|
|
|
|
|
for (const QString &n : names) quoted << QStringLiteral("\"%1\" TEXT").arg(n);
|
|
|
|
|
for (const QString &n : names)
|
|
|
|
|
quoted << QStringLiteral("\"%1\" %2").arg(n, BooleanData.contains(n) ? QStringLiteral("INTEGER")
|
|
|
|
|
: QStringLiteral("TEXT"));
|
|
|
|
|
return quoted.join(QStringLiteral(", "));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@p text as attribute @p name is stored, or invalid if it cannot be
|
|
|
|
|
QVariant stored(const QString &name, const QString &text)
|
|
|
|
|
{
|
|
|
|
|
if (!BooleanData.contains(name)) return text;
|
|
|
|
|
if (text == QLatin1String("true")) return 1;
|
|
|
|
|
if (text == QLatin1String("false")) return 0;
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//A stored value as attribute @p name writes it
|
|
|
|
|
QString attributeText(const QString &name, const QVariant &value)
|
|
|
|
|
{
|
|
|
|
|
if (BooleanData.contains(name) && value.typeId() == QMetaType::Int)
|
|
|
|
|
return value.toInt() ? QStringLiteral("true") : QStringLiteral("false");
|
|
|
|
|
return value.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString schema()
|
|
|
|
|
{
|
|
|
|
|
return QStringLiteral(
|
|
|
|
|
"CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);"
|
|
|
|
|
"CREATE TABLE project_property (ord INTEGER PRIMARY KEY, name TEXT, value TEXT, attrs TEXT);"
|
|
|
|
|
"CREATE TABLE folio (id TEXT PRIMARY KEY, pos INTEGER NOT NULL UNIQUE, %1,"
|
|
|
|
|
"CREATE TABLE folio (uuid TEXT PRIMARY KEY, pos INTEGER NOT NULL UNIQUE, %1,"
|
|
|
|
|
" props_at INTEGER, props_attrs TEXT);"
|
|
|
|
|
"CREATE TABLE folio_property (folio_id TEXT NOT NULL REFERENCES folio, ord INTEGER NOT NULL,"
|
|
|
|
|
" name TEXT, value TEXT, attrs TEXT, PRIMARY KEY (folio_id, ord));"
|
|
|
|
|
"CREATE TABLE element (key TEXT PRIMARY KEY, folio_id TEXT NOT NULL REFERENCES folio, %2,"
|
|
|
|
|
"CREATE TABLE folio_property (folio_uuid TEXT NOT NULL REFERENCES folio, ord INTEGER NOT NULL,"
|
|
|
|
|
" name TEXT, value TEXT, attrs TEXT, PRIMARY KEY (folio_uuid, ord));"
|
|
|
|
|
"CREATE TABLE element (uuid TEXT PRIMARY KEY, folio_uuid TEXT NOT NULL REFERENCES folio, %2,"
|
|
|
|
|
" info_at INTEGER, info_attrs TEXT, links_at INTEGER, links_attrs TEXT);"
|
|
|
|
|
"CREATE TABLE element_info (element_key TEXT NOT NULL REFERENCES element, ord INTEGER NOT NULL,"
|
|
|
|
|
" name TEXT, value TEXT, attrs TEXT, PRIMARY KEY (element_key, ord));"
|
|
|
|
|
"CREATE TABLE link (element_key TEXT NOT NULL REFERENCES element, ord INTEGER NOT NULL,"
|
|
|
|
|
" linked_uuid TEXT, attrs TEXT, PRIMARY KEY (element_key, ord));"
|
|
|
|
|
"CREATE TABLE conductor (key TEXT PRIMARY KEY, folio_id TEXT NOT NULL REFERENCES folio, %3);"
|
|
|
|
|
"CREATE TABLE element_info (element_uuid TEXT NOT NULL REFERENCES element, ord INTEGER NOT NULL,"
|
|
|
|
|
" name TEXT, value TEXT, show INTEGER, attrs TEXT, PRIMARY KEY (element_uuid, ord));"
|
|
|
|
|
"CREATE TABLE link (element_uuid TEXT NOT NULL REFERENCES element, ord INTEGER NOT NULL,"
|
|
|
|
|
" linked_uuid TEXT, attrs TEXT, PRIMARY KEY (element_uuid, ord));"
|
|
|
|
|
"CREATE TABLE conductor (uuid TEXT PRIMARY KEY, folio_uuid TEXT NOT NULL REFERENCES folio, %3);"
|
|
|
|
|
"CREATE TABLE terminal_strip (uuid TEXT PRIMARY KEY, ord INTEGER NOT NULL UNIQUE,"
|
|
|
|
|
" installation TEXT, location TEXT, name TEXT, comment TEXT, description TEXT);"
|
|
|
|
|
"CREATE TABLE strip_terminal (strip_uuid TEXT NOT NULL REFERENCES terminal_strip,"
|
|
|
|
@@ -108,9 +134,9 @@ QString schema()
|
|
|
|
|
.arg(columns(FolioData), columns(ElementData), columns(ConductorData));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//The tables of a document's engineering data. From format 3 on they are
|
|
|
|
|
//named doc_<name>, beside the project database's own tables in the same
|
|
|
|
|
//file (stage D3)
|
|
|
|
|
//The tables of a document's engineering data. While the project
|
|
|
|
|
//database's own tables were in the same file they were named
|
|
|
|
|
//doc_<name> (stage D3)
|
|
|
|
|
const QStringList DocTables = {
|
|
|
|
|
QStringLiteral("strip_bridge_terminal"), QStringLiteral("strip_bridge"),
|
|
|
|
|
QStringLiteral("strip_terminal"), QStringLiteral("terminal_strip"),
|
|
|
|
@@ -119,13 +145,15 @@ const QStringList DocTables = {
|
|
|
|
|
QStringLiteral("conductor"), QStringLiteral("meta")};
|
|
|
|
|
|
|
|
|
|
//Owns an sqlite3 handle; its SQL names the document's tables by their
|
|
|
|
|
//format 2 names, given their prefix here
|
|
|
|
|
//plain names, given their prefix here, and its columns as the format of
|
|
|
|
|
//the database read names them
|
|
|
|
|
class Database
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
~Database() { if (m_db) sqlite3_close(m_db); }
|
|
|
|
|
sqlite3 *m_db = nullptr;
|
|
|
|
|
QString m_prefix;
|
|
|
|
|
int m_format = QetContainer::Format;
|
|
|
|
|
QString sql(const QString &text) const {
|
|
|
|
|
if (m_prefix.isEmpty()) return text;
|
|
|
|
|
static const QRegularExpression names(QStringLiteral("(?<![\"\\w])(%1)(?![\\w\"])")
|
|
|
|
@@ -138,6 +166,17 @@ class Database
|
|
|
|
|
return sqlite3_exec(m_db, sql(text).toUtf8().constData(), nullptr, nullptr, nullptr) == SQLITE_OK;
|
|
|
|
|
}
|
|
|
|
|
QString error() const { return QString::fromUtf8(sqlite3_errmsg(m_db)); }
|
|
|
|
|
//Format 2 (QElectroTech 0.200.1 development builds) keyed its
|
|
|
|
|
//rows apart from the items' uuids: "f1", "e1", "c1" where an item
|
|
|
|
|
//had none of its own, and every item's key in the folio file
|
|
|
|
|
bool v2() const { return m_format == 2; }
|
|
|
|
|
QString folioKey() const { return v2() ? QStringLiteral("id") : QStringLiteral("uuid"); }
|
|
|
|
|
QString folioRef() const { return v2() ? QStringLiteral("folio_id") : QStringLiteral("folio_uuid"); }
|
|
|
|
|
QString itemKey() const { return v2() ? QStringLiteral("key") : QStringLiteral("uuid"); }
|
|
|
|
|
QString elementRef() const { return v2() ? QStringLiteral("element_key") : QStringLiteral("element_uuid"); }
|
|
|
|
|
QString showColumn() const { return v2() ? QStringLiteral("NULL") : QStringLiteral("show"); }
|
|
|
|
|
QStringList elementData() const { return v2() ? QStringList{QStringLiteral("uuid")} + ElementData : ElementData; }
|
|
|
|
|
QStringList conductorData() const { return v2() ? QStringList{QStringLiteral("uuid")} + ConductorData : ConductorData; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//Owns a prepared statement; binds and reads QVariants (null = SQL NULL)
|
|
|
|
@@ -196,15 +235,16 @@ QString placeholders(int n)
|
|
|
|
|
return q.join(QLatin1Char(','));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//The attributes of @p e but @p skip, and its tag, as JSON
|
|
|
|
|
QVariant attributesJson(const QDomElement &e, const QString &skip = QString(), bool with_tag = false)
|
|
|
|
|
//The attributes of @p e but @p skip as JSON, and its tag if it is not
|
|
|
|
|
//@p usual_tag
|
|
|
|
|
QVariant attributesJson(const QDomElement &e, const QStringList &skip, const QString &usual_tag)
|
|
|
|
|
{
|
|
|
|
|
QJsonObject o;
|
|
|
|
|
if (with_tag) o.insert(QStringLiteral("tag"), e.tagName());
|
|
|
|
|
if (e.tagName() != usual_tag) o.insert(QStringLiteral("tag"), e.tagName());
|
|
|
|
|
const QDomNamedNodeMap map = e.attributes();
|
|
|
|
|
for (int i = 0 ; i < map.count() ; ++i) {
|
|
|
|
|
const QDomAttr a = map.item(i).toAttr();
|
|
|
|
|
if (a.name() != skip) o.insert(a.name(), a.value());
|
|
|
|
|
if (!skip.contains(a.name())) o.insert(a.name(), a.value());
|
|
|
|
|
}
|
|
|
|
|
if (o.isEmpty()) return QVariant();
|
|
|
|
|
return QString::fromUtf8(QJsonDocument(o).toJson(QJsonDocument::Compact));
|
|
|
|
@@ -297,12 +337,15 @@ int childIndex(const QDomNode &child)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//The attributes @p names of @p e, taken out of it; one that cannot be
|
|
|
|
|
//stored stays, its value null
|
|
|
|
|
QVariantList take(QDomElement e, const QStringList &names)
|
|
|
|
|
{
|
|
|
|
|
QVariantList values;
|
|
|
|
|
for (const QString &n : names) {
|
|
|
|
|
values << (e.hasAttribute(n) ? QVariant(e.attribute(n)) : QVariant());
|
|
|
|
|
e.removeAttribute(n);
|
|
|
|
|
const QVariant v = e.hasAttribute(n) ? stored(n, e.attribute(n)) : QVariant();
|
|
|
|
|
values << (v.isValid() ? v : QVariant());
|
|
|
|
|
if (v.isValid()) e.removeAttribute(n);
|
|
|
|
|
}
|
|
|
|
|
return values;
|
|
|
|
|
}
|
|
|
|
@@ -310,7 +353,8 @@ QVariantList take(QDomElement e, const QStringList &names)
|
|
|
|
|
void put(QDomElement e, const QStringList &names, const QVariantList &values, int from)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0 ; i < names.size() ; ++i)
|
|
|
|
|
if (!values.at(from + i).isNull()) e.setAttribute(names.at(i), values.at(from + i).toString());
|
|
|
|
|
if (!values.at(from + i).isNull())
|
|
|
|
|
e.setAttribute(names.at(i), attributeText(names.at(i), values.at(from + i)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Same tags, attributes, text and child order
|
|
|
|
@@ -448,31 +492,52 @@ QList<QDomElement> children(const QDomElement &parent, const QString &tag)
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//uuid when present and unique among @p items, else prefix + count
|
|
|
|
|
QStringList keys(const QList<QDomElement> &items, const QString &prefix)
|
|
|
|
|
//The key of each of @p items: its uuid, when it has one no other of
|
|
|
|
|
//@p items has and @p reserved does not hold, so the uuid the folio file
|
|
|
|
|
//already holds says which row is the item's (@p own true). Else (F100:
|
|
|
|
|
//symbols copied with their uuid) a uuid made from the item itself --
|
|
|
|
|
//the same on every save, never from where the item is -- kept in the
|
|
|
|
|
//folio file as qetz-key. @p reserved: the keys of rows copied from
|
|
|
|
|
//another database (a folio kept as it was)
|
|
|
|
|
QStringList keys(const QList<QDomElement> &items, QList<bool> *own,
|
|
|
|
|
const QSet<QString> &reserved = QSet<QString>())
|
|
|
|
|
{
|
|
|
|
|
static const QUuid key_namespace(QStringLiteral("{3f0c6c1e-5b8e-4b8a-9d55-6b2f5f0f9a41}"));
|
|
|
|
|
QHash<QString, int> seen;
|
|
|
|
|
for (const QDomElement &e : items)
|
|
|
|
|
if (e.hasAttribute(QStringLiteral("uuid"))) ++seen[e.attribute(QStringLiteral("uuid"))];
|
|
|
|
|
if (!QUuid(e.attribute(QStringLiteral("uuid"))).isNull()) ++seen[e.attribute(QStringLiteral("uuid"))];
|
|
|
|
|
QSet<QString> taken(seen.keyBegin(), seen.keyEnd());
|
|
|
|
|
taken.unite(reserved);
|
|
|
|
|
QHash<QString, int> twins;
|
|
|
|
|
QStringList out;
|
|
|
|
|
int made_up = 0;
|
|
|
|
|
for (const QDomElement &e : items) {
|
|
|
|
|
const QString uuid = e.attribute(QStringLiteral("uuid"));
|
|
|
|
|
out << ((!uuid.isEmpty() && seen.value(uuid) == 1)
|
|
|
|
|
? uuid : prefix + QString::number(++made_up));
|
|
|
|
|
if (seen.value(uuid) == 1 && !reserved.contains(uuid)) {
|
|
|
|
|
out << uuid;
|
|
|
|
|
*own << true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
QString text;
|
|
|
|
|
QTextStream stream(&text);
|
|
|
|
|
e.save(stream, -1);
|
|
|
|
|
stream.flush();
|
|
|
|
|
//Items alike to the byte: how many came before tells them apart
|
|
|
|
|
const int twin = twins[text]++;
|
|
|
|
|
QString key;
|
|
|
|
|
for (int salt = 0 ; key.isEmpty() || taken.contains(key) ; ++salt)
|
|
|
|
|
key = QUuid::createUuidV5(key_namespace, QStringLiteral("%1\n%2\n%3").arg(text).arg(twin).arg(salt))
|
|
|
|
|
.toString();
|
|
|
|
|
taken.insert(key);
|
|
|
|
|
out << key;
|
|
|
|
|
*own << false;
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
//@p database opened read only in @p db, its tables checked to be plain
|
|
|
|
|
//tables: a file is untrusted
|
|
|
|
|
//Every table of @p db written again without its keys and indexes, in
|
|
|
|
|
//the order of its rows, and the file compacted: the file a .qetz holds
|
|
|
|
|
//is read whole, its lookups indexed once read (openDatabase(),
|
|
|
|
|
//projectDataBase::fillFromIndex()), and keys and indexes of text uuids
|
|
|
|
|
//were half of it (stage D3b)
|
|
|
|
|
//is read whole, its lookups indexed once read (openDatabase()), and keys
|
|
|
|
|
//and indexes of text uuids were half of it (stage D3b)
|
|
|
|
|
bool plainTables(sqlite3 *db)
|
|
|
|
|
{
|
|
|
|
|
QStringList tables;
|
|
|
|
@@ -498,6 +563,8 @@ bool plainTables(sqlite3 *db)
|
|
|
|
|
return ok && run(QStringLiteral("VACUUM"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@p database opened in @p db, its tables checked to be plain tables and
|
|
|
|
|
//its format one this reads: a file is untrusted
|
|
|
|
|
bool openDatabase(Database &db, const QByteArray &database, QString *error)
|
|
|
|
|
{
|
|
|
|
|
if (sqlite3_open(":memory:", &db.m_db) != SQLITE_OK) {
|
|
|
|
@@ -516,7 +583,8 @@ bool openDatabase(Database &db, const QByteArray &database, QString *error)
|
|
|
|
|
//A file is untrusted: nothing in its schema may run, and every name
|
|
|
|
|
//read must be a plain table.
|
|
|
|
|
db.exec(QStringLiteral("PRAGMA trusted_schema = OFF"));
|
|
|
|
|
//Format 3 names the document's tables doc_<name>, format 2 not
|
|
|
|
|
//Beside the project database's own tables the document's are
|
|
|
|
|
//named doc_<name>
|
|
|
|
|
{
|
|
|
|
|
Statement doc(db, QStringLiteral("SELECT count(*) FROM sqlite_master WHERE name = 'doc_meta'"));
|
|
|
|
|
if (doc.next() && doc.value(0).toInt() > 0) db.m_prefix = QStringLiteral("doc_");
|
|
|
|
@@ -530,11 +598,24 @@ bool openDatabase(Database &db, const QByteArray &database, QString *error)
|
|
|
|
|
setError(error, QStringLiteral("the project database has no table %1").arg(QLatin1String(table)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
Statement format(db, QStringLiteral("SELECT value FROM meta WHERE key = 'format'"));
|
|
|
|
|
const QString text = format.next() ? format.value(0).toString() : QString();
|
|
|
|
|
db.m_format = text.toInt();
|
|
|
|
|
if (db.m_format < 2 || db.m_format > QetContainer::Format) {
|
|
|
|
|
setError(error, QStringLiteral("the project database is of format %1, this reads 2 to %2")
|
|
|
|
|
.arg(text).arg(QetContainer::Format));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//What the folios are read by: a file holds no index (plainTables())
|
|
|
|
|
for (const char *index : {"folio(id)", "folio_property(folio_id)", "element(key)", "element(folio_id)",
|
|
|
|
|
"element_info(element_key)", "link(element_key)", "conductor(key)", "meta(key)"}) {
|
|
|
|
|
const QString on = QString::fromLatin1(index);
|
|
|
|
|
const QStringList indexes = {
|
|
|
|
|
QStringLiteral("folio(%1)").arg(db.folioKey()), QStringLiteral("folio_property(%1)").arg(db.folioRef()),
|
|
|
|
|
QStringLiteral("element(%1)").arg(db.itemKey()), QStringLiteral("element(%1)").arg(db.folioRef()),
|
|
|
|
|
QStringLiteral("element_info(%1)").arg(db.elementRef()), QStringLiteral("link(%1)").arg(db.elementRef()),
|
|
|
|
|
QStringLiteral("conductor(%1)").arg(db.itemKey()), QStringLiteral("meta(key)")};
|
|
|
|
|
for (const QString &on : indexes) {
|
|
|
|
|
const QString name = QStringLiteral("qetz_read_") + QString(on).replace(QLatin1Char('('), QLatin1Char('_')).remove(QLatin1Char(')'));
|
|
|
|
|
if (!db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS %1 ON %2").arg(name, on))) {
|
|
|
|
|
setError(error, QStringLiteral("the project database cannot be read: %1").arg(db.error()));
|
|
|
|
@@ -546,29 +627,46 @@ bool openDatabase(Database &db, const QByteArray &database, QString *error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDomElement entryElement(QDomDocument &document, const QVariant &name_attr, const QString &name,
|
|
|
|
|
const QVariant &value, const QVariant &attrs, const QString &default_tag)
|
|
|
|
|
const QVariant &value, const QVariant &attrs, const QString &default_tag,
|
|
|
|
|
const QVariant &show = QVariant())
|
|
|
|
|
{
|
|
|
|
|
const QJsonObject o = QJsonDocument::fromJson(attrs.toString().toUtf8()).object();
|
|
|
|
|
QDomElement e = document.createElement(o.value(QStringLiteral("tag")).toString(default_tag));
|
|
|
|
|
if (!name_attr.isNull()) e.setAttribute(name, name_attr.toString());
|
|
|
|
|
if (!show.isNull()) e.setAttribute(QStringLiteral("show"), show.toString());
|
|
|
|
|
setAttributesJson(e, attrs);
|
|
|
|
|
if (!value.isNull()) e.appendChild(document.createTextNode(value.toString()));
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//The engineering data of the folio @p d (its qetz-folio id) back from @p db
|
|
|
|
|
//An item's key: the one it was given, else (format 3 on) its uuid
|
|
|
|
|
QString keyOf(const Database &db, QDomElement e, const QString &attribute)
|
|
|
|
|
{
|
|
|
|
|
if (e.hasAttribute(attribute)) {
|
|
|
|
|
const QString key = e.attribute(attribute);
|
|
|
|
|
e.removeAttribute(attribute);
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
return db.v2() ? QString() : e.attribute(QStringLiteral("uuid"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString quoted(const QStringList &names)
|
|
|
|
|
{
|
|
|
|
|
return QStringLiteral("\"") + names.join(QStringLiteral("\", \"")) + QStringLiteral("\"");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//The engineering data of the folio @p d back from @p db
|
|
|
|
|
bool restoreDiagram(Database &db, QDomElement d, QString *error)
|
|
|
|
|
{
|
|
|
|
|
QDomDocument document = d.ownerDocument();
|
|
|
|
|
auto entry = [&document](const QVariant &name_attr, const QString &name, const QVariant &value,
|
|
|
|
|
const QVariant &attrs, const QString &default_tag) {
|
|
|
|
|
return entryElement(document, name_attr, name, value, attrs, default_tag);
|
|
|
|
|
const QVariant &attrs, const QString &default_tag, const QVariant &show = QVariant()) {
|
|
|
|
|
return entryElement(document, name_attr, name, value, attrs, default_tag, show);
|
|
|
|
|
};
|
|
|
|
|
const QString id = d.attribute(FolioAttribute);
|
|
|
|
|
const QString id = keyOf(db, d, FolioAttribute);
|
|
|
|
|
if (id.isEmpty()) return true;
|
|
|
|
|
d.removeAttribute(FolioAttribute);
|
|
|
|
|
Statement f(db, QStringLiteral("SELECT %1, props_at, props_attrs FROM folio WHERE id = ?")
|
|
|
|
|
.arg(QStringLiteral("\"") + FolioData.join(QStringLiteral("\", \"")) + QStringLiteral("\"")));
|
|
|
|
|
Statement f(db, QStringLiteral("SELECT %1, props_at, props_attrs FROM folio WHERE %2 = ?")
|
|
|
|
|
.arg(quoted(FolioData), db.folioKey()));
|
|
|
|
|
f.bind({id});
|
|
|
|
|
if (!f.next()) { setError(error, QStringLiteral("folio %1 is not in the database").arg(id)); return false; }
|
|
|
|
|
QVariantList values;
|
|
|
|
@@ -577,7 +675,8 @@ bool restoreDiagram(Database &db, QDomElement d, QString *error)
|
|
|
|
|
if (!values.at(FolioData.size()).isNull()) {
|
|
|
|
|
QDomElement props = document.createElement(QStringLiteral("properties"));
|
|
|
|
|
setAttributesJson(props, values.at(FolioData.size() + 1));
|
|
|
|
|
Statement p(db, QStringLiteral("SELECT name, value, attrs FROM folio_property WHERE folio_id = ? ORDER BY ord"));
|
|
|
|
|
Statement p(db, QStringLiteral("SELECT name, value, attrs FROM folio_property WHERE %1 = ? ORDER BY ord")
|
|
|
|
|
.arg(db.folioRef()));
|
|
|
|
|
p.bind({id});
|
|
|
|
|
while (p.next())
|
|
|
|
|
props.appendChild(entry(p.value(0), QStringLiteral("name"), p.value(1), p.value(2),
|
|
|
|
@@ -589,34 +688,37 @@ bool restoreDiagram(Database &db, QDomElement d, QString *error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QStringList element_data = db.elementData();
|
|
|
|
|
const QStringList conductor_data = db.conductorData();
|
|
|
|
|
for (const QDomElement &list : children(d, QStringLiteral("elements"))) {
|
|
|
|
|
for (QDomElement e : children(list, QStringLiteral("element"))) {
|
|
|
|
|
if (!e.hasAttribute(KeyAttribute)) continue;
|
|
|
|
|
const QString key = e.attribute(KeyAttribute);
|
|
|
|
|
e.removeAttribute(KeyAttribute);
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT %1, info_at, info_attrs, links_at, links_attrs FROM element WHERE key = ?")
|
|
|
|
|
.arg(QStringLiteral("\"") + ElementData.join(QStringLiteral("\", \"")) + QStringLiteral("\"")));
|
|
|
|
|
const QString key = keyOf(db, e, KeyAttribute);
|
|
|
|
|
if (key.isEmpty()) continue;
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT %1, info_at, info_attrs, links_at, links_attrs FROM element WHERE %2 = ?")
|
|
|
|
|
.arg(quoted(element_data), db.itemKey()));
|
|
|
|
|
s.bind({key});
|
|
|
|
|
if (!s.next()) { setError(error, QStringLiteral("symbol %1 is not in the database").arg(key)); return false; }
|
|
|
|
|
QVariantList v;
|
|
|
|
|
for (int i = 0 ; i < ElementData.size() + 4 ; ++i) v << s.value(i);
|
|
|
|
|
put(e, ElementData, v, 0);
|
|
|
|
|
const int n = ElementData.size();
|
|
|
|
|
for (int i = 0 ; i < element_data.size() + 4 ; ++i) v << s.value(i);
|
|
|
|
|
put(e, element_data, v, 0);
|
|
|
|
|
const int n = element_data.size();
|
|
|
|
|
QDomElement info, links;
|
|
|
|
|
if (!v.at(n).isNull()) {
|
|
|
|
|
info = document.createElement(QStringLiteral("elementInformations"));
|
|
|
|
|
setAttributesJson(info, v.at(n + 1));
|
|
|
|
|
Statement i(db, QStringLiteral("SELECT name, value, attrs FROM element_info WHERE element_key = ? ORDER BY ord"));
|
|
|
|
|
Statement i(db, QStringLiteral("SELECT name, value, attrs, %1 FROM element_info WHERE %2 = ? ORDER BY ord")
|
|
|
|
|
.arg(db.showColumn(), db.elementRef()));
|
|
|
|
|
i.bind({key});
|
|
|
|
|
while (i.next())
|
|
|
|
|
info.appendChild(entry(i.value(0), QStringLiteral("name"), i.value(1), i.value(2),
|
|
|
|
|
QStringLiteral("elementInformation")));
|
|
|
|
|
QStringLiteral("elementInformation"), i.value(3)));
|
|
|
|
|
restoreWhitespace(info, v.at(n + 1));
|
|
|
|
|
}
|
|
|
|
|
if (!v.at(n + 2).isNull()) {
|
|
|
|
|
links = document.createElement(QStringLiteral("links_uuids"));
|
|
|
|
|
setAttributesJson(links, v.at(n + 3));
|
|
|
|
|
Statement l(db, QStringLiteral("SELECT linked_uuid, attrs FROM link WHERE element_key = ? ORDER BY ord"));
|
|
|
|
|
Statement l(db, QStringLiteral("SELECT linked_uuid, attrs FROM link WHERE %1 = ? ORDER BY ord")
|
|
|
|
|
.arg(db.elementRef()));
|
|
|
|
|
l.bind({key});
|
|
|
|
|
while (l.next())
|
|
|
|
|
links.appendChild(entry(l.value(0), QStringLiteral("uuid"), QVariant(), l.value(1),
|
|
|
|
@@ -632,16 +734,15 @@ bool restoreDiagram(Database &db, QDomElement d, QString *error)
|
|
|
|
|
}
|
|
|
|
|
for (const QDomElement &list : children(d, QStringLiteral("conductors"))) {
|
|
|
|
|
for (QDomElement c : children(list, QStringLiteral("conductor"))) {
|
|
|
|
|
if (!c.hasAttribute(KeyAttribute)) continue;
|
|
|
|
|
const QString key = c.attribute(KeyAttribute);
|
|
|
|
|
c.removeAttribute(KeyAttribute);
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT %1 FROM conductor WHERE key = ?")
|
|
|
|
|
.arg(QStringLiteral("\"") + ConductorData.join(QStringLiteral("\", \"")) + QStringLiteral("\"")));
|
|
|
|
|
const QString key = keyOf(db, c, KeyAttribute);
|
|
|
|
|
if (key.isEmpty()) continue;
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT %1 FROM conductor WHERE %2 = ?")
|
|
|
|
|
.arg(quoted(conductor_data), db.itemKey()));
|
|
|
|
|
s.bind({key});
|
|
|
|
|
if (!s.next()) { setError(error, QStringLiteral("wire %1 is not in the database").arg(key)); return false; }
|
|
|
|
|
QVariantList v;
|
|
|
|
|
for (int i = 0 ; i < ConductorData.size() ; ++i) v << s.value(i);
|
|
|
|
|
put(c, ConductorData, v, 0);
|
|
|
|
|
for (int i = 0 ; i < conductor_data.size() ; ++i) v << s.value(i);
|
|
|
|
|
put(c, conductor_data, v, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
@@ -658,14 +759,14 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
const QByteArray &kept_from)
|
|
|
|
|
{
|
|
|
|
|
Database db;
|
|
|
|
|
db.m_prefix = QStringLiteral("doc_");
|
|
|
|
|
if (sqlite3_open(":memory:", &db.m_db) != SQLITE_OK) {
|
|
|
|
|
setError(error, QStringLiteral("cannot create the project database"));
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
//The project database's own tables first, when given: one file
|
|
|
|
|
//holds both (stage D3)
|
|
|
|
|
//holds both (stage D3), the document's named doc_<name>
|
|
|
|
|
if (!index.isEmpty()) {
|
|
|
|
|
db.m_prefix = QStringLiteral("doc_");
|
|
|
|
|
unsigned char *copy = static_cast<unsigned char *>(sqlite3_malloc64(sqlite3_uint64(index.size())));
|
|
|
|
|
if (!copy) { setError(error, QStringLiteral("out of memory")); return {}; }
|
|
|
|
|
memcpy(copy, index.constData(), size_t(index.size()));
|
|
|
|
@@ -680,7 +781,7 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
//The database of the .qetz this project was read from, for the folios
|
|
|
|
|
//kept as they were (stage D4)
|
|
|
|
|
//kept as they were (stage D4): of this format (currentFormat())
|
|
|
|
|
if (!kept_from.isEmpty()) {
|
|
|
|
|
unsigned char *copy = static_cast<unsigned char *>(sqlite3_malloc64(sqlite3_uint64(kept_from.size())));
|
|
|
|
|
if (!copy) { setError(error, QStringLiteral("out of memory")); return {}; }
|
|
|
|
@@ -708,13 +809,14 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
Statement folio(db, QStringLiteral("INSERT INTO folio VALUES (?,?,%1,?,?)").arg(placeholders(FolioData.size())));
|
|
|
|
|
Statement folio_property(db, QStringLiteral("INSERT INTO folio_property VALUES (?,?,?,?,?)"));
|
|
|
|
|
Statement element(db, QStringLiteral("INSERT INTO element VALUES (?,?,%1,?,?,?,?)").arg(placeholders(ElementData.size())));
|
|
|
|
|
Statement element_info(db, QStringLiteral("INSERT INTO element_info VALUES (?,?,?,?,?)"));
|
|
|
|
|
Statement element_info(db, QStringLiteral("INSERT INTO element_info VALUES (?,?,?,?,?,?)"));
|
|
|
|
|
Statement link(db, QStringLiteral("INSERT INTO link VALUES (?,?,?,?)"));
|
|
|
|
|
Statement conductor(db, QStringLiteral("INSERT INTO conductor VALUES (?,?,%1)").arg(placeholders(ConductorData.size())));
|
|
|
|
|
bool ok = meta.ok() && project_property.ok() && folio.ok() && folio_property.ok()
|
|
|
|
|
&& element.ok() && element_info.ok() && link.ok() && conductor.ok();
|
|
|
|
|
|
|
|
|
|
ok = ok && meta.run({QStringLiteral("format"), QStringLiteral("3")});
|
|
|
|
|
ok = ok && meta.run({QStringLiteral("format"), QString::number(QetContainer::Format)})
|
|
|
|
|
&& db.exec(QStringLiteral("PRAGMA user_version = %1").arg(QetContainer::Format));
|
|
|
|
|
|
|
|
|
|
//<properties> of a block, as rows of @p rows (with @p prefix values)
|
|
|
|
|
auto moveProperties = [&](QDomElement block, Statement &rows, const QVariantList &prefix) {
|
|
|
|
@@ -724,7 +826,24 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
simpleValue(p, &value);
|
|
|
|
|
ok = ok && rows.run(QVariantList(prefix) << ord++ << (p.hasAttribute(QStringLiteral("name"))
|
|
|
|
|
? QVariant(p.attribute(QStringLiteral("name"))) : QVariant())
|
|
|
|
|
<< value << attributesJson(p, QStringLiteral("name"), true));
|
|
|
|
|
<< value << attributesJson(p, {QStringLiteral("name")}, QStringLiteral("property")));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
//The <elementInformation> entries of @p block, as the rows of the
|
|
|
|
|
//symbol @p key
|
|
|
|
|
auto moveInformation = [&](const QDomElement &block, const QString &key) {
|
|
|
|
|
int ord = 0;
|
|
|
|
|
for (QDomElement i : children(block, QStringLiteral("elementInformation"))) {
|
|
|
|
|
QVariant value;
|
|
|
|
|
simpleValue(i, &value);
|
|
|
|
|
const QString show = i.attribute(QStringLiteral("show"));
|
|
|
|
|
const bool show_stored = show == QLatin1String("0") || show == QLatin1String("1");
|
|
|
|
|
QStringList kept{QStringLiteral("name")};
|
|
|
|
|
if (show_stored) kept << QStringLiteral("show");
|
|
|
|
|
ok = ok && element_info.run({key, ord++, i.hasAttribute(QStringLiteral("name"))
|
|
|
|
|
? QVariant(i.attribute(QStringLiteral("name"))) : QVariant(),
|
|
|
|
|
value, show_stored ? QVariant(show.toInt()) : QVariant(),
|
|
|
|
|
attributesJson(i, kept, QStringLiteral("elementInformation"))});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -739,7 +858,7 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
simpleValue(p, &value);
|
|
|
|
|
ok = ok && project_property.run({ord++, p.hasAttribute(QStringLiteral("name"))
|
|
|
|
|
? QVariant(p.attribute(QStringLiteral("name"))) : QVariant(),
|
|
|
|
|
value, attributesJson(p, QStringLiteral("name"), true)});
|
|
|
|
|
value, attributesJson(p, {QStringLiteral("name")}, QStringLiteral("property"))});
|
|
|
|
|
}
|
|
|
|
|
leaveMarker(project_props);
|
|
|
|
|
}
|
|
|
|
@@ -752,87 +871,78 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
for (const QDomElement &list : children(d, QStringLiteral("conductors")))
|
|
|
|
|
all_conductors += children(list, QStringLiteral("conductor"));
|
|
|
|
|
}
|
|
|
|
|
const QStringList element_keys = keys(all_elements, QStringLiteral("e"));
|
|
|
|
|
const QStringList conductor_keys = keys(all_conductors, QStringLiteral("c"));
|
|
|
|
|
//The keys of the rows the folios kept as they were bring, which no
|
|
|
|
|
//other item may have
|
|
|
|
|
QSet<QString> kept_elements, kept_conductors;
|
|
|
|
|
for (const QDomElement &d : diagrams) {
|
|
|
|
|
if (!d.hasAttribute(KeptPartAttribute)) continue;
|
|
|
|
|
Statement e(db, QStringLiteral("SELECT uuid FROM old.element WHERE folio_uuid = ?"));
|
|
|
|
|
e.bind({d.attribute(QStringLiteral("uuid"))});
|
|
|
|
|
while (e.next()) kept_elements.insert(e.value(0).toString());
|
|
|
|
|
Statement c(db, QStringLiteral("SELECT uuid FROM old.conductor WHERE folio_uuid = ?"));
|
|
|
|
|
c.bind({d.attribute(QStringLiteral("uuid"))});
|
|
|
|
|
while (c.next()) kept_conductors.insert(c.value(0).toString());
|
|
|
|
|
}
|
|
|
|
|
QList<bool> folio_own, element_own, conductor_own;
|
|
|
|
|
const QStringList folio_keys = keys(diagrams, &folio_own);
|
|
|
|
|
const QStringList element_keys = keys(all_elements, &element_own, kept_elements);
|
|
|
|
|
const QStringList conductor_keys = keys(all_conductors, &conductor_own, kept_conductors);
|
|
|
|
|
int e_index = 0, c_index = 0;
|
|
|
|
|
|
|
|
|
|
//A folio's id is its uuid when every folio has one of its own, so
|
|
|
|
|
//that a folio kept as it was keeps its rows (format 3, D4)
|
|
|
|
|
bool uuid_ids = true;
|
|
|
|
|
{
|
|
|
|
|
QSet<QString> seen;
|
|
|
|
|
for (const QDomElement &d : diagrams) {
|
|
|
|
|
const QString uuid = d.attribute(QStringLiteral("uuid"));
|
|
|
|
|
if (QUuid(uuid).isNull() || seen.contains(uuid)) { uuid_ids = false; break; }
|
|
|
|
|
seen.insert(uuid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (uuid_ids) {
|
|
|
|
|
ok = ok && meta.run({QStringLiteral("folio_ids"), QStringLiteral("uuid")});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
|
for (QDomElement d : diagrams)
|
|
|
|
|
{
|
|
|
|
|
const QString id = folio_keys.at(pos);
|
|
|
|
|
const bool own_id = folio_own.at(pos);
|
|
|
|
|
++pos;
|
|
|
|
|
const QString id = uuid_ids ? d.attribute(QStringLiteral("uuid")) : QStringLiteral("f%1").arg(pos);
|
|
|
|
|
//Kept as it was: its rows from the database it was read from
|
|
|
|
|
//Kept as it was: its rows from the database it was read from,
|
|
|
|
|
//under its uuid
|
|
|
|
|
if (d.hasAttribute(KeptPartAttribute)) {
|
|
|
|
|
if (kept_from.isEmpty() || !uuid_ids) {
|
|
|
|
|
if (kept_from.isEmpty() || !own_id) {
|
|
|
|
|
setError(error, QStringLiteral("a folio kept as it was, without its database"));
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
Statement copy(db, QStringLiteral("INSERT INTO folio SELECT id, ?, %1, props_at, props_attrs "
|
|
|
|
|
"FROM old.folio WHERE id = ?")
|
|
|
|
|
.arg(QStringLiteral("\"") + FolioData.join(QStringLiteral("\", \"")) + QStringLiteral("\"")));
|
|
|
|
|
Statement copy(db, QStringLiteral("INSERT INTO folio SELECT uuid, ?, %1, props_at, props_attrs "
|
|
|
|
|
"FROM old.folio WHERE uuid = ?").arg(quoted(FolioData)));
|
|
|
|
|
ok = ok && copy.run({pos, id}) && sqlite3_changes(db.m_db) == 1;
|
|
|
|
|
for (const QString &sql : {
|
|
|
|
|
QStringLiteral("INSERT INTO folio_property SELECT * FROM old.folio_property WHERE folio_id = ? ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO element SELECT * FROM old.element WHERE folio_id = ? ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO element_info SELECT * FROM old.element_info WHERE element_key IN "
|
|
|
|
|
"(SELECT key FROM old.element WHERE folio_id = ?) ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO link SELECT * FROM old.link WHERE element_key IN "
|
|
|
|
|
"(SELECT key FROM old.element WHERE folio_id = ?) ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO conductor SELECT * FROM old.conductor WHERE folio_id = ? ORDER BY rowid")}) {
|
|
|
|
|
QStringLiteral("INSERT INTO folio_property SELECT * FROM old.folio_property WHERE folio_uuid = ? ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO element SELECT * FROM old.element WHERE folio_uuid = ? ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO element_info SELECT * FROM old.element_info WHERE element_uuid IN "
|
|
|
|
|
"(SELECT uuid FROM old.element WHERE folio_uuid = ?) ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO link SELECT * FROM old.link WHERE element_uuid IN "
|
|
|
|
|
"(SELECT uuid FROM old.element WHERE folio_uuid = ?) ORDER BY rowid"),
|
|
|
|
|
QStringLiteral("INSERT INTO conductor SELECT * FROM old.conductor WHERE folio_uuid = ? ORDER BY rowid")}) {
|
|
|
|
|
Statement rows(db, sql);
|
|
|
|
|
ok = ok && rows.run({id});
|
|
|
|
|
}
|
|
|
|
|
//Its symbols whose information changed since (QETProject::toXml()):
|
|
|
|
|
//their information rows written as a symbol's block writes them
|
|
|
|
|
for (const QDomElement &edited : children(d, QStringLiteral("qetz-edited-element"))) {
|
|
|
|
|
Statement find(db, QStringLiteral("SELECT key FROM element WHERE folio_id = ? AND uuid = ? AND info_at IS NOT NULL"));
|
|
|
|
|
Statement find(db, QStringLiteral("SELECT uuid FROM element WHERE folio_uuid = ? AND uuid = ? AND info_at IS NOT NULL"));
|
|
|
|
|
find.bind({id, edited.attribute(QStringLiteral("uuid"))});
|
|
|
|
|
if (!find.next()) {
|
|
|
|
|
setError(error, QStringLiteral("a symbol changed on folio %1 kept as it was is not in its database").arg(id));
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const QString key = find.value(0).toString();
|
|
|
|
|
Statement drop(db, QStringLiteral("DELETE FROM element_info WHERE element_key = ?"));
|
|
|
|
|
Statement drop(db, QStringLiteral("DELETE FROM element_info WHERE element_uuid = ?"));
|
|
|
|
|
ok = ok && drop.run({key});
|
|
|
|
|
int ord = 0;
|
|
|
|
|
for (QDomElement i : children(edited.firstChildElement(QStringLiteral("elementInformations")),
|
|
|
|
|
QStringLiteral("elementInformation"))) {
|
|
|
|
|
QVariant value;
|
|
|
|
|
simpleValue(i, &value);
|
|
|
|
|
ok = ok && element_info.run({key, ord++, i.hasAttribute(QStringLiteral("name"))
|
|
|
|
|
? QVariant(i.attribute(QStringLiteral("name"))) : QVariant(),
|
|
|
|
|
value, attributesJson(i, QStringLiteral("name"), true)});
|
|
|
|
|
}
|
|
|
|
|
moveInformation(edited.firstChildElement(QStringLiteral("elementInformations")), key);
|
|
|
|
|
}
|
|
|
|
|
//Its wires whose properties changed since: their columns
|
|
|
|
|
for (const QDomElement &edited : children(d, QStringLiteral("qetz-edited-conductor"))) {
|
|
|
|
|
QStringList set;
|
|
|
|
|
QVariantList values;
|
|
|
|
|
for (const QString &column : ConductorData) {
|
|
|
|
|
if (column == QLatin1String("uuid") || column.startsWith(QLatin1String("element"))
|
|
|
|
|
|| column.startsWith(QLatin1String("terminal")) || column == QLatin1String("freezeLabel")
|
|
|
|
|
|| !edited.hasAttribute(column))
|
|
|
|
|
if (column.startsWith(QLatin1String("element")) || column.startsWith(QLatin1String("terminal"))
|
|
|
|
|
|| BooleanData.contains(column) || !edited.hasAttribute(column))
|
|
|
|
|
continue;
|
|
|
|
|
set << QStringLiteral("\"%1\" = ?").arg(column);
|
|
|
|
|
values << edited.attribute(column);
|
|
|
|
|
}
|
|
|
|
|
values << id << edited.attribute(QStringLiteral("uuid"));
|
|
|
|
|
Statement update(db, QStringLiteral("UPDATE conductor SET %1 WHERE folio_id = ? AND uuid = ?")
|
|
|
|
|
Statement update(db, QStringLiteral("UPDATE conductor SET %1 WHERE folio_uuid = ? AND uuid = ?")
|
|
|
|
|
.arg(set.join(QStringLiteral(", "))));
|
|
|
|
|
ok = ok && !set.isEmpty() && update.run(values) && sqlite3_changes(db.m_db) == 1;
|
|
|
|
|
}
|
|
|
|
@@ -853,11 +963,12 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
moveProperties(props, folio_property, {id});
|
|
|
|
|
leaveMarker(props);
|
|
|
|
|
}
|
|
|
|
|
d.setAttribute(FolioAttribute, id);
|
|
|
|
|
if (!own_id) d.setAttribute(FolioAttribute, id);
|
|
|
|
|
|
|
|
|
|
for (const QDomElement &list : children(d, QStringLiteral("elements"))) {
|
|
|
|
|
for (QDomElement e : children(list, QStringLiteral("element"))) {
|
|
|
|
|
const QString key = element_keys.at(e_index++);
|
|
|
|
|
const QString key = element_keys.at(e_index);
|
|
|
|
|
const bool own_key = element_own.at(e_index++);
|
|
|
|
|
QVariantList erow{key, id};
|
|
|
|
|
erow += take(e, ElementData);
|
|
|
|
|
QDomElement info = e.firstChildElement(QStringLiteral("elementInformations"));
|
|
|
|
@@ -871,35 +982,28 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
|
|
|
|
<< (links_at < 0 ? QVariant() : QVariant(links_at))
|
|
|
|
|
<< (links_at < 0 ? QVariant() : blockJson(links));
|
|
|
|
|
ok = ok && element.run(erow);
|
|
|
|
|
if (info_at >= 0) {
|
|
|
|
|
int ord = 0;
|
|
|
|
|
for (QDomElement i : children(info, QStringLiteral("elementInformation"))) {
|
|
|
|
|
QVariant value;
|
|
|
|
|
simpleValue(i, &value);
|
|
|
|
|
ok = ok && element_info.run({key, ord++, i.hasAttribute(QStringLiteral("name"))
|
|
|
|
|
? QVariant(i.attribute(QStringLiteral("name"))) : QVariant(),
|
|
|
|
|
value, attributesJson(i, QStringLiteral("name"), true)});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (info_at >= 0)
|
|
|
|
|
moveInformation(info, key);
|
|
|
|
|
if (links_at >= 0) {
|
|
|
|
|
int ord = 0;
|
|
|
|
|
for (QDomElement l : children(links, QStringLiteral("link_uuid")))
|
|
|
|
|
ok = ok && link.run({key, ord++, l.hasAttribute(QStringLiteral("uuid"))
|
|
|
|
|
? QVariant(l.attribute(QStringLiteral("uuid"))) : QVariant(),
|
|
|
|
|
attributesJson(l, QStringLiteral("uuid"), true)});
|
|
|
|
|
attributesJson(l, {QStringLiteral("uuid")}, QStringLiteral("link_uuid"))});
|
|
|
|
|
}
|
|
|
|
|
if (info_at >= 0) leaveMarker(info);
|
|
|
|
|
if (links_at >= 0) leaveMarker(links);
|
|
|
|
|
e.setAttribute(KeyAttribute, key);
|
|
|
|
|
if (!own_key) e.setAttribute(KeyAttribute, key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const QDomElement &list : children(d, QStringLiteral("conductors"))) {
|
|
|
|
|
for (QDomElement c : children(list, QStringLiteral("conductor"))) {
|
|
|
|
|
const QString key = conductor_keys.at(c_index++);
|
|
|
|
|
const QString key = conductor_keys.at(c_index);
|
|
|
|
|
const bool own_key = conductor_own.at(c_index++);
|
|
|
|
|
QVariantList crow{key, id};
|
|
|
|
|
crow += take(c, ConductorData);
|
|
|
|
|
ok = ok && conductor.run(crow);
|
|
|
|
|
c.setAttribute(KeyAttribute, key);
|
|
|
|
|
if (!own_key) c.setAttribute(KeyAttribute, key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -955,16 +1059,6 @@ bool QetContainerDb::restore(QDomElement project, const QByteArray &database, QS
|
|
|
|
|
if (!openDatabase(db, database, error))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
auto entry = [&document](const QVariant &name_attr, const QString &name, const QVariant &value,
|
|
|
|
|
const QVariant &attrs, const QString &default_tag) {
|
|
|
|
|
const QJsonObject o = QJsonDocument::fromJson(attrs.toString().toUtf8()).object();
|
|
|
|
|
QDomElement e = document.createElement(o.value(QStringLiteral("tag")).toString(default_tag));
|
|
|
|
|
if (!name_attr.isNull()) e.setAttribute(name, name_attr.toString());
|
|
|
|
|
setAttributesJson(e, attrs);
|
|
|
|
|
if (!value.isNull()) e.appendChild(document.createTextNode(value.toString()));
|
|
|
|
|
return e;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//Project properties
|
|
|
|
|
QHash<QString, QVariant> meta;
|
|
|
|
|
{
|
|
|
|
@@ -976,8 +1070,8 @@ bool QetContainerDb::restore(QDomElement project, const QByteArray &database, QS
|
|
|
|
|
setAttributesJson(props, meta.value(QStringLiteral("props_attrs")));
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT name, value, attrs FROM project_property ORDER BY ord"));
|
|
|
|
|
while (s.next())
|
|
|
|
|
props.appendChild(entry(s.value(0), QStringLiteral("name"), s.value(1), s.value(2),
|
|
|
|
|
QStringLiteral("property")));
|
|
|
|
|
props.appendChild(entryElement(document, s.value(0), QStringLiteral("name"), s.value(1), s.value(2),
|
|
|
|
|
QStringLiteral("property")));
|
|
|
|
|
restoreWhitespace(props, meta.value(QStringLiteral("props_attrs")));
|
|
|
|
|
if (!replaceMarker(project, props)) {
|
|
|
|
|
setError(error, QStringLiteral("the project's properties have no place"));
|
|
|
|
@@ -1064,16 +1158,13 @@ bool QetContainerDb::FolioReader::restore(QDomElement diagram, QString *error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@brief QetContainerDb::foliosByUuid
|
|
|
|
|
@return true if @p database, a format 3 project.sqlite, names its
|
|
|
|
|
folios by their uuid: a folio written as it was read keeps its rows
|
|
|
|
|
under it (stage D4)
|
|
|
|
|
@brief QetContainerDb::currentFormat
|
|
|
|
|
@return true if @p database, a project.sqlite, is of the format this
|
|
|
|
|
writes: a folio written as it was read keeps its rows, copied as they
|
|
|
|
|
are (stage D4)
|
|
|
|
|
*/
|
|
|
|
|
bool QetContainerDb::foliosByUuid(const QByteArray &database)
|
|
|
|
|
bool QetContainerDb::currentFormat(const QByteArray &database)
|
|
|
|
|
{
|
|
|
|
|
Database db;
|
|
|
|
|
if (!openDatabase(db, database, nullptr) || db.m_prefix.isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
Statement s(db, QStringLiteral("SELECT value FROM meta WHERE key = 'folio_ids'"));
|
|
|
|
|
return s.next() && s.value(0).toString() == QLatin1String("uuid");
|
|
|
|
|
return openDatabase(db, database, nullptr) && db.m_format == QetContainer::Format;
|
|
|
|
|
}
|
|
|
|
|