Resolved issue #666. QDomDocument produces random attribute order.

--HG--
branch : release
This commit is contained in:
Roman Telezhynskyi 2017-04-20 19:01:08 +03:00
parent b4106f8e5a
commit 6c047b140d
4 changed files with 103 additions and 15 deletions

View File

@ -33,10 +33,6 @@
#include <QMessageBox> // For QT_REQUIRE_VERSION #include <QMessageBox> // For QT_REQUIRE_VERSION
#include <QTimer> #include <QTimer>
// 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[]) int main(int argc, char *argv[])
{ {
Q_INIT_RESOURCE(tapeicon); Q_INIT_RESOURCE(tapeicon);
@ -47,8 +43,6 @@ int main(int argc, char *argv[])
QT_REQUIRE_VERSION(argc, argv, "5.2.0") 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 #ifndef Q_OS_MAC // supports natively
InitHighDpiScaling(argc, argv); InitHighDpiScaling(argc, argv);
#endif //Q_OS_MAC #endif //Q_OS_MAC

View File

@ -34,10 +34,6 @@
#include <QMessageBox> // For QT_REQUIRE_VERSION #include <QMessageBox> // For QT_REQUIRE_VERSION
#include <QTimer> #include <QTimer>
// 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[]) 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_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 // Need to internally move a node inside a piece main path
qRegisterMetaTypeStreamOperators<VPieceNode>("VPieceNode"); qRegisterMetaTypeStreamOperators<VPieceNode>("VPieceNode");

View File

@ -63,6 +63,66 @@
#include <QXmlSchema> #include <QXmlSchema>
#include <QXmlSchemaValidator> #include <QXmlSchemaValidator>
#include <QtDebug> #include <QtDebug>
#include <QXmlStreamWriter>
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<QString, QString> 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<QString, QString>::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 //This class need for validation pattern file using XSD shema
class MessageHandler : public QAbstractMessageHandler class MessageHandler : public QAbstractMessageHandler
@ -202,6 +262,37 @@ bool VDomDocument::find(const QDomElement &node, const QString& id)
return false; 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? * @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 // cppcheck-suppress ConfigurationNotChecked
if (file.open(QIODevice::WriteOnly)) if (file.open(QIODevice::WriteOnly))
{ {
// See issue #666. QDomDocument produces random attribute order.
const int indent = 4; const int indent = 4;
QTextStream out(&file); if (not SaveCanonicalXML(&file, indent, error))
out.setCodec("UTF-8"); {
save(out, indent); 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(); success = file.commit();
} }

View File

@ -142,6 +142,8 @@ private:
QHash<QString, QDomElement> map; QHash<QString, QDomElement> map;
bool find(const QDomElement &node, const QString& id); bool find(const QDomElement &node, const QString& id);
bool SaveCanonicalXML(QIODevice *file, int indent, QString &error) const;
}; };
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------