diff --git a/sources/autoNum/assignvariables.cpp b/sources/autoNum/assignvariables.cpp index c8059bbf2..1cd48953e 100644 --- a/sources/autoNum/assignvariables.cpp +++ b/sources/autoNum/assignvariables.cpp @@ -220,12 +220,56 @@ namespace autonum Diagram *diagram, const Element *elmt, const Conductor *cndr) + { + if (!diagram) { + return formula; + } + + FormulaContext context; + const BorderTitleBlock &border = diagram->border_and_titleblock; + context.folio = border.folio(); + context.folio_index = diagram->folioIndex(); + context.folio_total = border.folioTotal(); + context.plant = border.plant(); + context.locmach = border.locmach(); + context.title_block_fields = border.additionalFields(); + context.project_properties = diagram->project()->projectProperties(); + if (elmt) + { + context.has_element = true; + context.element_position = diagram->convertPosition(elmt->scenePos()); + context.element_prefix = elmt->getPrefix(); + } + if (cndr) + { + context.has_conductor = true; + context.wire_function = cndr->properties().m_function; + context.wire_tension_protocol = cndr->properties().m_tension_protocol; + context.wire_color = cndr->properties().m_wire_color; + context.wire_section = cndr->properties().m_wire_section; + } + return formulaToLabel(std::move(formula), seqStruct, context); + } + + /** + @brief AssignVariables::formulaToLabel + Return the formula with its variables assigned from @p context, + which describes a folio and the element or conductor the formula + belongs to without needing either to be built. + @param formula - the formula to work + @param seqStruct - struct where is stocked int values + (struct is passed as a reference + and modified by this static method) + @param context - what the variables are read from + @return the string with variable assigned. + */ + QString AssignVariables::formulaToLabel(QString formula, + sequentialNumbers &seqStruct, + const FormulaContext &context) { AssignVariables av(std::move(formula), seqStruct, - diagram, - elmt, - cndr); + context); seqStruct = av.m_seq_struct; return av.m_assigned_label; } @@ -347,76 +391,54 @@ namespace autonum AssignVariables::AssignVariables(const QString& formula, const sequentialNumbers& seqStruct, - Diagram *diagram, - const Element *elmt, - const Conductor *cndr): - m_diagram(diagram), + const FormulaContext &context): + m_context(context), m_arg_formula(formula), m_assigned_label(formula), - m_seq_struct(seqStruct), - m_element(elmt), - m_conductor(cndr) + m_seq_struct(seqStruct) { - if (m_diagram) + m_assigned_label.replace("%F", m_context.folio); + m_assigned_label.replace("%f", + QString::number(m_context.folio_index+1)); + m_assigned_label.replace("%id", + QString::number(m_context.folio_index+1)); + m_assigned_label.replace("%total", + QString::number(m_context.folio_total)); + m_assigned_label.replace("%M", m_context.plant); + m_assigned_label.replace("%LM", m_context.locmach); + + QSettings settings; + if (m_context.has_element) { - m_assigned_label.replace("%F", - m_diagram - -> border_and_titleblock - .folio()); - m_assigned_label.replace("%f", - QString::number( - m_diagram - ->folioIndex()+1)); - m_assigned_label.replace("%id", - QString::number( - m_diagram - ->folioIndex()+1)); - m_assigned_label.replace("%total", - QString::number( - m_diagram - ->border_and_titleblock - .folioTotal())); - m_assigned_label.replace("%M", - m_diagram - -> border_and_titleblock - .plant()); - m_assigned_label.replace("%LM", - m_diagram - -> border_and_titleblock - .locmach()); - - QSettings settings; - if (m_element) - { - if (settings.value("border-columns_0", true).toBool()){ - m_assigned_label.replace("%c", QString::number(m_diagram->convertPosition(m_element->scenePos()).number() - 1)); - }else{ - m_assigned_label.replace("%c", QString::number(m_diagram->convertPosition(m_element->scenePos()).number())); - } - m_assigned_label.replace("%l", m_diagram->convertPosition(m_element->scenePos()).letter()); - m_assigned_label.replace("%prefix", m_element->getPrefix()); + if (settings.value("border-columns_0", true).toBool()){ + m_assigned_label.replace("%c", QString::number(m_context.element_position.number() - 1)); + }else{ + m_assigned_label.replace("%c", QString::number(m_context.element_position.number())); } - - if (m_conductor) - { - m_assigned_label.replace("%wf", cndr->properties().m_function); - m_assigned_label.replace("%wv", cndr->properties().m_tension_protocol); - m_assigned_label.replace("%wc", cndr->properties().m_wire_color); - m_assigned_label.replace("%ws", cndr->properties().m_wire_section); - } - - assignTitleBlockVar(); - assignProjectVar(); - assignSequence(); + m_assigned_label.replace("%l", m_context.element_position.letter()); + m_assigned_label.replace("%prefix", m_context.element_prefix); } + + if (m_context.has_conductor) + { + m_assigned_label.replace("%wf", m_context.wire_function); + m_assigned_label.replace("%wv", m_context.wire_tension_protocol); + m_assigned_label.replace("%wc", m_context.wire_color); + m_assigned_label.replace("%ws", m_context.wire_section); + } + + assignTitleBlockVar(); + assignProjectVar(); + assignSequence(); } void AssignVariables::assignTitleBlockVar() { - for (int i = 0; i < m_diagram->border_and_titleblock.additionalFields().count(); i++) + DiagramContext fields = m_context.title_block_fields; + for (int i = 0; i < fields.count(); i++) { - QString folio_variable = m_diagram->border_and_titleblock.additionalFields().keys().at(i); - QVariant folio_value = m_diagram->border_and_titleblock.additionalFields().operator [](folio_variable); + QString folio_variable = fields.keys().at(i); + QVariant folio_value = fields[folio_variable]; if (m_assigned_label.contains(folio_variable)) { m_assigned_label.replace("%{" + folio_variable + "}", folio_value.toString()); @@ -427,10 +449,11 @@ namespace autonum void AssignVariables::assignProjectVar() { - for (int i = 0; i < m_diagram->project()->projectProperties().count(); i++) + DiagramContext properties = m_context.project_properties; + for (int i = 0; i < properties.count(); i++) { - QString folio_variable = m_diagram->project()->projectProperties().keys().at(i); - QVariant folio_value = m_diagram->project()->projectProperties().operator [](folio_variable); + QString folio_variable = properties.keys().at(i); + QVariant folio_value = properties[folio_variable]; if (m_assigned_label.contains(folio_variable)) { m_assigned_label.replace("%{" + folio_variable + "}", folio_value.toString()); diff --git a/sources/autoNum/assignvariables.h b/sources/autoNum/assignvariables.h index 5fffdf43f..255ad6003 100644 --- a/sources/autoNum/assignvariables.h +++ b/sources/autoNum/assignvariables.h @@ -18,6 +18,7 @@ #ifndef ASSIGNVARIABLES_H #define ASSIGNVARIABLES_H #include "../diagramcontext.h" +#include "../diagramposition.h" #include "numerotationcontext.h" #include @@ -57,6 +58,36 @@ namespace autonum QStringList alpha; }; + /** + @brief The FormulaContext struct + Everything a formula's variables are read from: the folio it is on + and, for an element's or a conductor's formula, that item. + AssignVariables::formulaToLabel() fills one from a built folio; the + project database fills one from the project's file. + */ + struct FormulaContext + { + /// border_and_titleblock.folio(), folioIndex(), folioTotal()... + QString folio; + int folio_index = 0; + int folio_total = 0; + QString plant; + QString locmach; + /// the title block's additional fields and the project's properties + DiagramContext title_block_fields; + DiagramContext project_properties; + /// an element's grid cell (%c, %l) and prefix + bool has_element = false; + DiagramPosition element_position; + QString element_prefix; + /// a conductor's %wf, %wv, %wc and %ws + bool has_conductor = false; + QString wire_function; + QString wire_tension_protocol; + QString wire_color; + QString wire_section; + }; + /** @brief The AssignVariables class This class assign variable of a formula string. @@ -66,21 +97,20 @@ namespace autonum { public: static QString formulaToLabel (QString formula, sequentialNumbers &seqStruct, Diagram *diagram, const Element *elmt = nullptr, const Conductor *cndr = nullptr); + static QString formulaToLabel (QString formula, sequentialNumbers &seqStruct, const FormulaContext &context); static QString replaceVariable (const QString &formula, const DiagramContext &dc); static QString genericXref (const Element *element); private: - AssignVariables(const QString& formula, const sequentialNumbers& seqStruct , Diagram *diagram, const Element *elmt = nullptr, const Conductor *cndr = nullptr); + AssignVariables(const QString& formula, const sequentialNumbers& seqStruct, const FormulaContext &context); void assignTitleBlockVar(); void assignProjectVar(); void assignSequence(); - Diagram *m_diagram = nullptr; + const FormulaContext &m_context; QString m_arg_formula; QString m_assigned_label; sequentialNumbers m_seq_struct; - const Element *m_element = nullptr; - const Conductor *m_conductor = nullptr; }; void setSequentialToList(QStringList &list, NumerotationContext &nc, const QString& type); diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 6e09e8739..bea454bd4 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -19,6 +19,8 @@ #include "sqlreadonly.h" +#include "../autoNum/assignvariables.h" +#include "../borderproperties.h" #include "../bordertitleblock.h" #include "../diagram.h" #include "../diagramposition.h" @@ -34,6 +36,7 @@ #include "../qetinformation.h" #include "../qetproject.h" #include "../qet.h" +#include "../titleblockproperties.h" #include "../ElementsCollection/xmlelementcollection.h" #include "../properties/elementdata.h" @@ -195,9 +198,24 @@ struct DocumentElement QString sub_type; QVariant group; DiagramContext informations; + QString label; QHash terminals; }; + //The sequential values an element or a conductor was saved with, as + //Element::fromXml() and Conductor::fromXml() read them -- false for + //the attributes files written before carry. +bool readSequence(const QDomElement &item, autonum::sequentialNumbers *sequence) +{ + for (const char *name : {"sequ_1", "sequf_1", "seqt_1", "seqtf_1", "seqh_1"}) { + if (item.hasAttribute(QLatin1String(name))) { + return false; + } + } + sequence->fromXml(item.firstChildElement(QStringLiteral("sequentialNumbers"))); + return true; +} + struct DocumentConductor { QString uuid; @@ -221,12 +239,17 @@ struct DocumentConductor instead -- when a folio, element or conductor carries no saved uuid (the folios derive one on load), a conductor names its ends the older way, a folio number uses %autonum, a conductor ends on a terminal that shows - its master's contact label, or an element's definition is missing or - not one the folios could build. A file saved by a current QElectroTech - carries everything else. + its master's contact label, an element's definition is missing or + not one the folios could build, two elements on a folio number their + terminals alike, or sequential numbers are saved as the attributes + older files carry. A file saved by a current QElectroTech carries + everything else. - A symbol label computed from a formula is the label saved in the file, - which QElectroTech writes as it computes it on every save. + A label or a conductor text made from a formula is worked out again, + as the folios do, with the same AssignVariables code: the one saved + in the file is what the formula gave when it was saved, and a folio + added or moved since changes it. A frozen conductor text is left to the + folios, which work it out part-way through loading. @return true if the tables were filled */ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString *why) @@ -248,6 +271,7 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString } const DiagramContext project_wide = m_project->projectWideProperties(); + const DiagramContext project_properties = m_project->projectProperties(); //One border and title block read from each folio's XML in turn: //only what it gives is kept. BorderTitleBlock reader; @@ -288,6 +312,9 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString } BorderTitleBlock *border = &reader; + //As a new Diagram's border starts, before initFromXml() reads it + border->importBorder(BorderProperties()); + border->importTitleBlock(TitleBlockProperties()); border->titleBlockFromXml(diagram_xml); border->borderFromXml(diagram_xml); if (border->folio().contains(QStringLiteral("%autonum"))) { @@ -295,16 +322,49 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString } border->setFolioData(i + 1, int(diagram_nodes.size()), QString(), project_wide); + //What a formula on this folio is worked out from, as + //AssignVariables::formulaToLabel() reads it off a built folio. + autonum::FormulaContext folio_context; + folio_context.folio = border->folio(); + folio_context.folio_index = i; + folio_context.folio_total = border->folioTotal(); + folio_context.plant = border->plant(); + folio_context.locmach = border->locmach(); + folio_context.title_block_fields = border->additionalFields(); + folio_context.project_properties = project_properties; + QHash on_this_folio; //element uuid -> index in elements - for (const QDomElement &element_xml : QET::findInDomElement( + QSet terminal_ids; //the older terminal ids used so far + for (QDomElement element_xml : QET::findInDomElement( diagram_xml, QStringLiteral("elements"), QStringLiteral("element"))) { + //Skipped by Diagram::fromXml() as well + if (!Element::valideXml(element_xml)) { + continue; + } const QUuid uuid(element_xml.attribute(QStringLiteral("uuid"))); if (uuid.isNull() || element_uuids.contains(uuid)) { return refuse(QStringLiteral("an element has no saved uuid, or shares one")); } element_uuids.insert(uuid); + //Element::fromXml() refuses an element whose terminals are + //numbered like those of one read before it on the folio; + //which of them it matches needs their geometry, so leave any + //such folio to the folios. A current file numbers them + //across the folio. + QSet ids; + for (QDomElement t : QET::findInDomElement( + element_xml, QStringLiteral("terminals"), QStringLiteral("terminal"))) { + if (Terminal::valideXml(t)) { + ids.insert(t.attribute(QStringLiteral("id")).toInt()); + } + } + if (terminal_ids.intersects(ids)) { + return refuse(QStringLiteral("two elements on a folio number their terminals alike")); + } + terminal_ids.unite(ids); + //The definition the folio builds the element from, with the //checks Element::buildFromXml() refuses an element on -- //read once per type, as many elements share one. @@ -351,10 +411,10 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString DocumentElement element; element.uuid = uuid.toString(); element.diagram_uuid = diagram_uuid.toString(); - element.pos = border->convertPosition( - QPointF(element_xml.attribute(QStringLiteral("x")).toDouble(), - element_xml.attribute(QStringLiteral("y")).toDouble())) - .toString(); + DiagramPosition position = border->convertPosition( + QPointF(element_xml.attribute(QStringLiteral("x")).toDouble(), + element_xml.attribute(QStringLiteral("y")).toDouble())); + element.pos = position.toString(); element.type = known->type; element.sub_type = known->sub_type; element.terminals = known->terminals; @@ -363,6 +423,21 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString element.informations.fromXml( element_xml.firstChildElement(QStringLiteral("elementInformations")), QStringLiteral("elementInformation")); + //Element::actualLabel() + const QString formula = element.informations.value(QStringLiteral("formula")).toString(); + if (formula.isEmpty()) { + element.label = element.informations.value(QStringLiteral("label")).toString(); + } else { + autonum::sequentialNumbers sequence; + if (!readSequence(element_xml, &sequence)) { + return refuse(QStringLiteral("an element's sequential numbers are saved the older way")); + } + autonum::FormulaContext context = folio_context; + context.has_element = true; + context.element_position = position; + context.element_prefix = element_xml.attribute(QStringLiteral("prefix")); + element.label = autonum::AssignVariables::formulaToLabel(formula, sequence, context); + } on_this_folio.insert(uuid, int(elements.size())); elements << element; @@ -429,8 +504,27 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString conductor.element2 = ends[1][0]; conductor.terminal2 = ends[1][1]; //ConductorProperties::fromXml()'s text, without reading the - //rest of the properties - conductor.text = conductor_xml.attribute(QStringLiteral("num")); + //rest of the properties -- or, as Conductor::refreshText() + //makes it, what its formula gives. + const QString formula = conductor_xml.attribute(QStringLiteral("formula")); + if (formula.isEmpty()) { + conductor.text = conductor_xml.attribute(QStringLiteral("num")); + } else { + autonum::sequentialNumbers sequence; + if (conductor_xml.attribute(QStringLiteral("freezeLabel")) == QLatin1String("true")) { + return refuse(QStringLiteral("a conductor's text made from a formula is frozen")); + } + if (!readSequence(conductor_xml, &sequence)) { + return refuse(QStringLiteral("a conductor's sequential numbers are saved the older way")); + } + autonum::FormulaContext context = folio_context; + context.has_conductor = true; + context.wire_function = conductor_xml.attribute(QStringLiteral("function")); + context.wire_tension_protocol = conductor_xml.attribute(QStringLiteral("tension_protocol")); + context.wire_color = conductor_xml.attribute(QStringLiteral("conductor_color")); + context.wire_section = conductor_xml.attribute(QStringLiteral("conductor_section")); + conductor.text = autonum::AssignVariables::formulaToLabel(formula, sequence, context); + } conductors << conductor; } @@ -473,7 +567,7 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString qDebug() << "projectDataBase::populateFromDocument element insert error : " << m_insert_elements_query.lastError(); } bindElementInfoValues(m_insert_element_info_query, element.uuid, element.informations, - element.informations[QStringLiteral("label")].toString()); + element.label); if (!m_insert_element_info_query.exec()) { qDebug() << "projectDataBase::populateFromDocument element_info insert error : " << m_insert_element_info_query.lastError(); } diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index de6de9667..d324fac74 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -352,7 +352,7 @@ if(QET_HAS_SCRIPTING) tst_databasefromdocument.cpp) add_test(NAME tst_databasefromdocument COMMAND tst_databasefromdocument) add_dependencies(tst_databasefromdocument qelectrotech) - target_link_libraries(tst_databasefromdocument PRIVATE Qt::Test) + target_link_libraries(tst_databasefromdocument PRIVATE Qt::Test Qt::Xml) target_compile_definitions(tst_databasefromdocument PRIVATE "QET_TEST_BINARY_PATH=\"$\"" "QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"") diff --git a/tests/qttest/tst_databasefromdocument.cpp b/tests/qttest/tst_databasefromdocument.cpp index f375dd2da..d2febebf4 100644 --- a/tests/qttest/tst_databasefromdocument.cpp +++ b/tests/qttest/tst_databasefromdocument.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -74,6 +75,44 @@ class tst_databasefromdocument : public QObject return tables; } + // tremie_vibrante.qet saved once, as a document to change + QDomDocument resaved(const QString &saved) + { + run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved}); + QFile file(saved); + QDomDocument document; + if (file.open(QIODevice::ReadOnly)) + document.setContent(&file); + return document; + } + + static bool write(const QString &path, const QDomDocument &document) + { + QFile file(path); + return file.open(QIODevice::WriteOnly | QIODevice::Truncate) + && file.write(document.toByteArray()) > 0; + } + + // Both fills of @p saved, which must come from the document, agree. + void compareBothWays(const QString &saved) + { + QString how_document, how_folios; + const QJsonObject document = dump(saved, false, &how_document); + const QJsonObject folios = dump(saved, true, &how_folios); + QVERIFY2(how_document == QLatin1String("Project database filled from the document"), + qPrintable(how_document)); + QVERIFY2(how_folios.contains(QStringLiteral("QET_DATABASE_FROM_FOLIOS")), qPrintable(how_folios)); + QCOMPARE(document.keys().size(), 6); + for (const QString &table : folios.keys()) { + const QJsonArray a = document.value(table).toArray(), b = folios.value(table).toArray(); + QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows from the document, %3 from the folios") + .arg(table).arg(a.size()).arg(b.size()))); + } + m_last = document; + } + + QJsonObject m_last; // the document's tables, from compareBothWays() + private slots: void initTestCase() { @@ -101,19 +140,144 @@ private slots: const QString saved = m_dir.filePath(QStringLiteral("saved%1.qet").arg(m_run)); run({QStringLiteral("--resave"), project, saved}); QVERIFY2(QFile::exists(saved), "--resave failed"); + compareBothWays(saved); + } - QString how_document, how_folios; - const QJsonObject document = dump(saved, false, &how_document); - const QJsonObject folios = dump(saved, true, &how_folios); - QVERIFY2(how_document == QLatin1String("Project database filled from the document"), - qPrintable(how_document)); - QVERIFY2(how_folios.contains(QStringLiteral("QET_DATABASE_FROM_FOLIOS")), qPrintable(how_folios)); - QCOMPARE(document.keys().size(), 6); - for (const QString &table : folios.keys()) { - const QJsonArray a = document.value(table).toArray(), b = folios.value(table).toArray(); - QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows from the document, %3 from the folios") - .arg(table).arg(a.size()).arg(b.size()))); + // A label or a conductor text made from a formula is what the formula + // gives now, not what it gave when the file was saved: every folio's + // first element and first conductor is given a formula using each kind + // of variable, and a saved label no formula gives. + void formulasAreWorkedOut() + { + const QString saved = m_dir.filePath(QStringLiteral("formulas-saved.qet")); + run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved}); + QFile file(saved); + QVERIFY(file.open(QIODevice::ReadOnly)); + QDomDocument document; + QVERIFY(document.setContent(&file)); + file.close(); + + auto property = [&document](QDomElement parent, const QString &name, const QString &value) { + QDomElement properties = parent.firstChildElement(QStringLiteral("properties")); + if (properties.isNull()) + properties = parent.appendChild(document.createElement(QStringLiteral("properties"))).toElement(); + QDomElement p = document.createElement(QStringLiteral("property")); + p.setAttribute(QStringLiteral("name"), name); + p.appendChild(document.createTextNode(value)); + properties.appendChild(p); + }; + auto sequence = [&document](QDomElement item, const QString &unit) { + item.removeChild(item.firstChildElement(QStringLiteral("sequentialNumbers"))); + QDomElement s = document.createElement(QStringLiteral("sequentialNumbers")); + QDomElement u = document.createElement(QStringLiteral("unit")); + u.appendChild(document.createTextNode(unit)); + s.appendChild(u); + item.appendChild(s); + }; + + property(document.documentElement(), QStringLiteral("site"), QStringLiteral("S")); + const QDomNodeList diagrams = document.elementsByTagName(QStringLiteral("diagram")); + QCOMPARE(diagrams.size(), 3); + for (int i = 0 ; i < diagrams.size() ; ++i) + { + QDomElement diagram = diagrams.at(i).toElement(); + diagram.setAttribute(QStringLiteral("folio"), QStringLiteral("F%id")); + diagram.setAttribute(QStringLiteral("plant"), QStringLiteral("P")); + diagram.setAttribute(QStringLiteral("locmach"), QStringLiteral("L")); + property(diagram, QStringLiteral("zone"), QStringLiteral("Z%1").arg(i)); + + QDomElement element = diagram.firstChildElement(QStringLiteral("elements")) + .firstChildElement(QStringLiteral("element")); + QVERIFY(!element.isNull()); + element.setAttribute(QStringLiteral("prefix"), QStringLiteral("X")); + sequence(element, QStringLiteral("7")); + QDomElement informations = element.firstChildElement(QStringLiteral("elementInformations")); + if (informations.isNull()) + informations = element.appendChild(document.createElement(QStringLiteral("elementInformations"))).toElement(); + while (!informations.firstChild().isNull()) + informations.removeChild(informations.firstChild()); + for (const auto &info : {std::make_pair(QStringLiteral("formula"), + QStringLiteral("K%total-%f-%F-%M-%LM-%c%l-%prefix-%{zone}-%{site}-%sequ_1")), + std::make_pair(QStringLiteral("label"), QStringLiteral("OLD"))}) { + QDomElement e = document.createElement(QStringLiteral("elementInformation")); + e.setAttribute(QStringLiteral("name"), info.first); + e.setAttribute(QStringLiteral("show"), QStringLiteral("1")); + e.appendChild(document.createTextNode(info.second)); + informations.appendChild(e); + } + + QDomElement conductor = diagram.firstChildElement(QStringLiteral("conductors")) + .firstChildElement(QStringLiteral("conductor")); + QVERIFY(!conductor.isNull()); + conductor.setAttribute(QStringLiteral("formula"), QStringLiteral("W%total-%id-%wf-%{zone}-%sequ_1")); + conductor.setAttribute(QStringLiteral("function"), QStringLiteral("N")); + conductor.setAttribute(QStringLiteral("num"), QStringLiteral("OLD")); + sequence(conductor, QStringLiteral("3")); } + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Truncate)); + file.write(document.toByteArray()); + file.close(); + + compareBothWays(saved); + // ...and they were worked out, not left as saved + const QString info = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("element_info")).toArray()) + .toJson(QJsonDocument::Compact)); + const QString wires = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("conductor")).toArray()) + .toJson(QJsonDocument::Compact)); + QVERIFY2(info.contains(QStringLiteral("K3-1-F1-P-L-")), qPrintable(info.left(400))); + QVERIFY2(info.contains(QStringLiteral("-X-Z0-S-7")), qPrintable(info.left(400))); + QVERIFY2(wires.contains(QStringLiteral("W3-3-N-Z2-3")), qPrintable(wires.left(400))); + QVERIFY(!info.contains(QStringLiteral("OLD"))); + QVERIFY(!wires.contains(QStringLiteral("OLD"))); + } + + // An element the folio does not build, and so the conductors ending on + // it, are left out the same way. + void unbuiltElementIsLeftOut() + { + const QString saved = m_dir.filePath(QStringLiteral("unbuilt.qet")); + QDomDocument document = resaved(saved); + QDomElement conductor = document.elementsByTagName(QStringLiteral("conductor")).at(0).toElement(); + QVERIFY(!conductor.isNull()); + const QString uuid = conductor.attribute(QStringLiteral("element1")); + const QDomNodeList elements = document.elementsByTagName(QStringLiteral("element")); + bool found = false; + for (int i = 0 ; i < elements.size() ; ++i) { + QDomElement e = elements.at(i).toElement(); + if (e.attribute(QStringLiteral("uuid")) == uuid) { + e.setAttribute(QStringLiteral("x"), QStringLiteral("nan")); + found = true; + } + } + QVERIFY(found); + QVERIFY(write(saved, document)); + compareBothWays(saved); + const QString wires = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("conductor")).toArray()) + .toJson(QJsonDocument::Compact)); + QVERIFY(!wires.contains(conductor.attribute(QStringLiteral("uuid")))); + } + + // Two elements on a folio numbering their terminals alike: which one the + // folio then refuses depends on the terminals' geometry, so the folios + // fill the database. + void clashingTerminalIdsFallBack() + { + const QString saved = m_dir.filePath(QStringLiteral("clash.qet")); + QDomDocument document = resaved(saved); + const QDomElement diagram = document.elementsByTagName(QStringLiteral("diagram")).at(0).toElement(); + QDomElement first = diagram.firstChildElement(QStringLiteral("elements")).firstChildElement(QStringLiteral("element")); + QDomElement second = first.nextSiblingElement(QStringLiteral("element")); + const QString id = first.firstChildElement(QStringLiteral("terminals")) + .firstChildElement(QStringLiteral("terminal")).attribute(QStringLiteral("id")); + QDomElement terminal = second.firstChildElement(QStringLiteral("terminals")) + .firstChildElement(QStringLiteral("terminal")); + QVERIFY(!id.isEmpty() && !terminal.isNull()); + terminal.setAttribute(QStringLiteral("id"), id); + QVERIFY(write(saved, document)); + QString how; + dump(saved, false, &how); + QCOMPARE(how, QStringLiteral("Project database filled from the folios: " + "two elements on a folio number their terminals alike")); } // A file whose items carry no saved uuid is filled from the folios,