From 8ea051acfc6e314db5c31b39fe6fb5c3df665409 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 11 Oct 2026 19:57:43 +1300 Subject: [PATCH] A .qetz keeps nothing QElectroTech computes in its folio files (stage D3c) Format 3 folio files lose what building a folio computes (QetContainer::dropComputed()): a wire's description of its ends (element1_label, element1_name, element1_linked, terminalname1 and the same for end 2), the content of a symbol text showing the symbol's information or a composition of it, and -- when the project database's index is written, every symbol with a uuid of its own -- a symbol's list of its terminals. What may go was found by test, not by reading: on 342 projects saved by this version, each kind taken out of the file, the project opened and saved: the wire ends came back in all 244 that had them, the symbol texts in all 201, the terminal lists in 305 of 306 -- not in the file whose symbols share a uuid (#1408), where wires find their ends by them; such a project gets no index, so it keeps them. Stays, for now: labels made from a formula and wire numbers made from one, in the database (opening from the index and numbering read them), and a table's size (the user's as much as computed). tst_qetcontainer compares a joined document with the original less what is computed, and computedNotKept checks a file holding both kinds (one filled in by the test) loses them -- it fails when nothing is dropped. Through QElectroTech, 342 projects: a .qetz opened from its index and saved as .qet = eager = master; every folio of industrial and f2099 drawn and linked as eager (f2099 but its 3 render-noise folios). ctest 96/96 with and without QET_LAZY_FOLIOS=1. Co-Authored-By: Claude Opus 5.5 --- sources/container/qetcontainer.cpp | 60 ++++++++++++++++++++++++++++++ sources/container/qetcontainer.h | 2 + tests/qttest/tst_qetcontainer.cpp | 53 ++++++++++++++++++++++++-- 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/sources/container/qetcontainer.cpp b/sources/container/qetcontainer.cpp index 84d0a293c..5749113ee 100644 --- a/sources/container/qetcontainer.cpp +++ b/sources/container/qetcontainer.cpp @@ -93,6 +93,19 @@ QList childElements(const QDomElement &parent, const QString &tag) return list; } + //An element and the text nodes on either side of it out of its parent, + //one text node left where they were, so nothing is read twice +void removeElement(QDomElement e) +{ + QDomNode parent = e.parentNode(); + QDomNode before = e.previousSibling(), after = e.nextSibling(); + parent.removeChild(e); + if (before.isText() && after.isText()) { + before.setNodeValue(before.nodeValue() + after.nodeValue()); + parent.removeChild(after); + } +} + //The collection's symbol definitions, each to elements// void splitCollection(QDomDocument &document, const QDomElement &category, const QString &path, QList *parts, QSet *taken) @@ -164,6 +177,49 @@ bool QetContainer::readDocument(const QByteArray &content, QDomDocument *documen && join(entries, document, error); } +/** + @brief QetContainer::dropComputed + Take out of the folio @p diagram what QElectroTech computes when it + builds the folio (stage D3c): a wire's description of + its ends (element1_label, element1_name, element1_linked, terminalname1, + and the same for end 2), a symbol text's content where it shows the + symbol's information or a composition of it, and -- with + @p terminal_lists, when every symbol has a uuid of its own -- a symbol's + list of its terminals. Checked on 342 projects: opened and saved, each + comes back as it was, but the terminal lists of a file whose symbols + share a uuid (F100), where wires find their ends by them. +*/ +void QetContainer::dropComputed(QDomElement diagram, bool terminal_lists) +{ + for (QDomElement list : childElements(diagram, QStringLiteral("conductors"))) + for (QDomElement wire : childElements(list, QStringLiteral("conductor"))) + for (const char *end : {"1", "2"}) + for (const char *name : {"element%1_label", "element%1_name", "element%1_linked", + "terminalname%1"}) + wire.removeAttribute(QString::fromLatin1(name).arg(QLatin1String(end))); + for (QDomElement list : childElements(diagram, QStringLiteral("elements"))) + { + for (QDomElement element : childElements(list, QStringLiteral("element"))) + { + if (terminal_lists) + for (QDomElement terminals : childElements(element, QStringLiteral("terminals"))) + removeElement(terminals); + for (QDomElement texts : childElements(element, QStringLiteral("dynamic_texts"))) + { + for (QDomElement text : childElements(texts, QStringLiteral("dynamic_elmt_text"))) + { + const QString from = text.attribute(QStringLiteral("text_from")); + if (from != QLatin1String("ElementInfo") && from != QLatin1String("CompositeText")) + continue; + for (QDomElement content : childElements(text, QStringLiteral("text"))) + while (content.hasChildNodes()) + content.removeChild(content.firstChild()); + } + } + } + } +} + /** @brief QetContainer::split @param project : the project document, as QETProject::toXml() makes it @@ -192,6 +248,10 @@ QList QetContainer::split(const QDomDocument &project, const QStr //Folios, each with its pictures taken out int folio = 0; QMap pictures; //name -> png, once each + //Nothing QElectroTech computes is kept (format 3) + for (QDomElement diagram : childElements(root, QStringLiteral("diagram"))) + dropComputed(diagram, !index.isEmpty()); + for (QDomElement diagram : childElements(root, QStringLiteral("diagram"))) { ++folio; diff --git a/sources/container/qetcontainer.h b/sources/container/qetcontainer.h index 9a7d1692b..1d654f49d 100644 --- a/sources/container/qetcontainer.h +++ b/sources/container/qetcontainer.h @@ -60,6 +60,8 @@ namespace QetContainer bool readFolio(const QHash &files, const QString &file, QDomDocument *folio, QString *error = nullptr); + /// Take what QElectroTech computes out of a folio (format 3) + void dropComputed(QDomElement diagram, bool terminal_lists); /// Parse @p xml as a .qet is read (QETProject::openFile) bool parse(const QByteArray &xml, QDomDocument *document, QString *error = nullptr); /// true if @p content is a zip, so a .qetz rather than a .qet diff --git a/tests/qttest/tst_qetcontainer.cpp b/tests/qttest/tst_qetcontainer.cpp index be09a3593..1bccb0ce0 100644 --- a/tests/qttest/tst_qetcontainer.cpp +++ b/tests/qttest/tst_qetcontainer.cpp @@ -69,6 +69,18 @@ class tst_qetcontainer : public QObject return QetContainer::split(*original, QStringLiteral("tst_qetcontainer"), &error); } + //@p document without what QElectroTech computes, which a .qetz + //does not keep (format 3); tst_resaveunchanged checks that + //QElectroTech gives it back + static QDomDocument withoutComputed(const QDomDocument &document) + { + QDomDocument copy = document.cloneNode(true).toDocument(); + for (QDomElement diagram = copy.documentElement().firstChildElement(QStringLiteral("diagram")); + !diagram.isNull() ; diagram = diagram.nextSiblingElement(QStringLiteral("diagram"))) + QetContainer::dropComputed(diagram, false); + return copy; + } + private slots: void sameDocumentBack_data() { @@ -100,7 +112,8 @@ private slots: QVERIFY2(QetZip::fromBytes(QetZip::toBytes(entries, &error), &unzipped, &error), qPrintable(error)); QDomDocument back; QVERIFY2(QetContainer::join(unzipped, &back, &error), qPrintable(error)); - const QString d = difference(original.documentElement(), back.documentElement(), QString()); + const QString d = difference(withoutComputed(original).documentElement(), + back.documentElement(), QString()); QVERIFY2(d.isEmpty(), qPrintable(d)); } @@ -136,7 +149,9 @@ private slots: // The engineering data is in project.sqlite and nowhere else: the folio // files hold no symbol information, links, wire numbers or title-block - // fields, and the database holds them all (industrial.qet: 50 folios). + // fields, nor what QElectroTech computes from them (a wire's + // description of its ends, a symbol text showing its information), and + // the database holds them all (industrial.qet: 50 folios). void databaseHoldsTheData() { QDomDocument original; @@ -181,6 +196,37 @@ private slots: sqlite3_close(db); } + // Nor does a folio file hold what QElectroTech computes (format 3): a + // wire's description of its ends, a symbol text showing its information. + // A file holding both, so that the check can fail. + void computedNotKept() + { + QDomDocument original; + QString error; + QVERIFY(QetContainer::parse(read(QStringLiteral(QET_EXAMPLES_DIR "/schema_unifilaire_voltaique2.qet")), + &original, &error)); + const QDomNodeList texts = original.elementsByTagName(QStringLiteral("dynamic_elmt_text")); + bool filled = false; + for (int i = 0 ; i < texts.size() && !filled ; ++i) { + QDomElement text = texts.at(i).toElement(); + if (text.attribute(QStringLiteral("text_from")) != QLatin1String("ElementInfo")) continue; + QDomElement content = text.firstChildElement(QStringLiteral("text")); + while (content.hasChildNodes()) content.removeChild(content.firstChild()); + content.appendChild(original.createTextNode(QStringLiteral("COMPUTED-LABEL"))); + filled = true; + } + QVERIFY(filled); + QVERIFY(original.toString().contains(QStringLiteral("element1_label="))); + const QList entries = QetContainer::split(original, QString(), &error); + QVERIFY2(!entries.isEmpty(), qPrintable(error)); + for (const QetZip::Entry &e : entries) { + if (!e.name.startsWith(QStringLiteral("folios/"))) continue; + QVERIFY2(!e.data.contains("element1_label=") && !e.data.contains("terminalname2="), + qPrintable(e.name)); + QVERIFY2(!e.data.contains("COMPUTED-LABEL"), qPrintable(e.name)); + } + } + // Terminal strips move into the database, and come back exactly: // industrial.qet with two strips on its own terminal symbols, parsed as // a save leaves it (no whitespace text). @@ -217,7 +263,8 @@ private slots: QVERIFY2(!e.data.contains(""), "the strips stayed in project.xml"); QDomDocument back; QVERIFY2(QetContainer::join(entries, &back, &error), qPrintable(error)); - const QString d = difference(original.documentElement(), back.documentElement(), QString()); + const QString d = difference(withoutComputed(original).documentElement(), + back.documentElement(), QString()); QVERIFY2(d.isEmpty(), qPrintable(d)); }