diff --git a/src/app/tape/main.cpp b/src/app/tape/main.cpp index 72ce7de78..2989a96ad 100644 --- a/src/app/tape/main.cpp +++ b/src/app/tape/main.cpp @@ -33,10 +33,6 @@ #include // For QT_REQUIRE_VERSION #include -// Lock producing random attribute order in XML -// https://stackoverflow.com/questions/27378143/qt-5-produce-random-attribute-order-in-xml -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; - int main(int argc, char *argv[]) { Q_INIT_RESOURCE(tapeicon); @@ -47,8 +43,6 @@ int main(int argc, char *argv[]) QT_REQUIRE_VERSION(argc, argv, "5.2.0") - qt_qhash_seed.store(0); // Lock producing random attribute order in XML - #ifndef Q_OS_MAC // supports natively InitHighDpiScaling(argc, argv); #endif //Q_OS_MAC diff --git a/src/app/valentina/main.cpp b/src/app/valentina/main.cpp index 56d3a8378..bea11be2d 100644 --- a/src/app/valentina/main.cpp +++ b/src/app/valentina/main.cpp @@ -34,10 +34,6 @@ #include // For QT_REQUIRE_VERSION #include -// Lock producing random attribute order in XML -// https://stackoverflow.com/questions/27378143/qt-5-produce-random-attribute-order-in-xml -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; - //--------------------------------------------------------------------------------------------------------------------- int main(int argc, char *argv[]) @@ -52,8 +48,6 @@ int main(int argc, char *argv[]) QT_REQUIRE_VERSION(argc, argv, "5.2.0") - qt_qhash_seed.store(0); // Lock producing random attribute order in XML - // Need to internally move a node inside a piece main path qRegisterMetaTypeStreamOperators("VPieceNode"); diff --git a/src/libs/ifc/xml/vdomdocument.cpp b/src/libs/ifc/xml/vdomdocument.cpp index 218e4741b..9f25ac7d2 100644 --- a/src/libs/ifc/xml/vdomdocument.cpp +++ b/src/libs/ifc/xml/vdomdocument.cpp @@ -63,6 +63,66 @@ #include #include #include +#include + +namespace +{ +//--------------------------------------------------------------------------------------------------------------------- +void SaveNodeCanonically(QXmlStreamWriter &stream, const QDomNode &domNode) +{ + if (stream.hasError()) + { + return; + } + + if (domNode.isElement()) + { + const QDomElement domElement = domNode.toElement(); + if (not domElement.isNull()) + { + stream.writeStartElement(domElement.tagName()); + + if (domElement.hasAttributes()) + { + QMap attributes; + const QDomNamedNodeMap attributeMap = domElement.attributes(); + for (int i = 0; i < attributeMap.count(); ++i) + { + const QDomNode attribute = attributeMap.item(i); + attributes.insert(attribute.nodeName(), attribute.nodeValue()); + } + + QMap::const_iterator i = attributes.constBegin(); + while (i != attributes.constEnd()) + { + stream.writeAttribute(i.key(), i.value()); + ++i; + } + } + + if (domElement.hasChildNodes()) + { + QDomNode elementChild = domElement.firstChild(); + while (not elementChild.isNull()) + { + SaveNodeCanonically(stream, elementChild); + elementChild = elementChild.nextSibling(); + } + } + + stream.writeEndElement(); + } + } + else if (domNode.isComment()) + { + stream.writeComment(domNode.nodeValue()); + } + else if (domNode.isText()) + { + stream.writeCharacters(domNode.nodeValue()); + } +} +} //This class need for validation pattern file using XSD shema class MessageHandler : public QAbstractMessageHandler @@ -202,6 +262,37 @@ bool VDomDocument::find(const QDomElement &node, const QString& id) return false; } +//--------------------------------------------------------------------------------------------------------------------- +bool VDomDocument::SaveCanonicalXML(QIODevice *file, int indent, QString &error) const +{ + SCASSERT(file != nullptr) + + QXmlStreamWriter stream(file); + stream.setAutoFormatting(true); + stream.setAutoFormattingIndent(indent); + stream.writeStartDocument(); + + QDomNode root = documentElement(); + while (not root.isNull()) + { + SaveNodeCanonically(stream, root); + if (stream.hasError()) + { + break; + } + root = root.nextSibling(); + } + + stream.writeEndDocument(); + + if (stream.hasError()) + { + error = tr("Fail to write Canonical XML."); + return false; + } + return true; +} + //--------------------------------------------------------------------------------------------------------------------- /** * @brief Returns the long long value of the given attribute. RENAME: GetParameterLongLong? @@ -649,10 +740,17 @@ bool VDomDocument::SaveDocument(const QString &fileName, QString &error) const // cppcheck-suppress ConfigurationNotChecked if (file.open(QIODevice::WriteOnly)) { + // See issue #666. QDomDocument produces random attribute order. const int indent = 4; - QTextStream out(&file); - out.setCodec("UTF-8"); - save(out, indent); + if (not SaveCanonicalXML(&file, indent, error)) + { + return false; + } + // Left these strings in case we will need them for testing purposes + // QTextStream out(&file); + // out.setCodec("UTF-8"); + // save(out, indent); + success = file.commit(); } diff --git a/src/libs/ifc/xml/vdomdocument.h b/src/libs/ifc/xml/vdomdocument.h index c303fa4e4..0aaac1f92 100644 --- a/src/libs/ifc/xml/vdomdocument.h +++ b/src/libs/ifc/xml/vdomdocument.h @@ -142,6 +142,8 @@ private: QHash map; bool find(const QDomElement &node, const QString& id); + + bool SaveCanonicalXML(QIODevice *file, int indent, QString &error) const; }; //---------------------------------------------------------------------------------------------------------------------