From c8aa3df6d68146f0befc9cfcf44b89157ec78b8d Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Wed, 3 Mar 2021 13:20:06 +0100
Subject: [PATCH] provide means to store binary data in XML file (stored as
 Base64 data)

---
 GUI/coregui/Models/SessionItem.cpp            |  7 ++--
 GUI/coregui/Models/SessionItem.h              |  4 +--
 GUI/coregui/Models/SessionXML.cpp             | 22 +++++++++---
 GUI/coregui/Models/SessionXML.h               |  3 +-
 .../utils/DeserializationException.cpp        | 33 ++++++++++++++++++
 GUI/coregui/utils/DeserializationException.h  | 34 +++++++++++++++++++
 6 files changed, 93 insertions(+), 10 deletions(-)
 create mode 100644 GUI/coregui/utils/DeserializationException.cpp
 create mode 100644 GUI/coregui/utils/DeserializationException.h

diff --git a/GUI/coregui/Models/SessionItem.cpp b/GUI/coregui/Models/SessionItem.cpp
index 8442e619086..d8e5f303748 100644
--- a/GUI/coregui/Models/SessionItem.cpp
+++ b/GUI/coregui/Models/SessionItem.cpp
@@ -568,9 +568,12 @@ void SessionItem::addTranslator(const IPathTranslator& translator)
     m_translators.push_back(translator.clone());
 }
 
-void SessionItem::writeNonSessionItemData(QXmlStreamWriter*) const {}
+QByteArray SessionItem::serializeBinaryData() const
+{
+    return QByteArray();
+}
 
-void SessionItem::readNonSessionItemData(QXmlStreamReader*) {}
+void SessionItem::deserializeBinaryData(const QByteArray& data) {}
 
 void SessionItem::childDeleted(SessionItem* child)
 {
diff --git a/GUI/coregui/Models/SessionItem.h b/GUI/coregui/Models/SessionItem.h
index 34e8f4a52f7..809903a01f3 100644
--- a/GUI/coregui/Models/SessionItem.h
+++ b/GUI/coregui/Models/SessionItem.h
@@ -128,8 +128,8 @@ public:
     virtual QStringList translateList(const QStringList& list) const;
     void addTranslator(const IPathTranslator& translator);
 
-    virtual void writeNonSessionItemData(QXmlStreamWriter* writer) const;
-    virtual void readNonSessionItemData(QXmlStreamReader* reader);
+    virtual QByteArray serializeBinaryData() const;
+    virtual void deserializeBinaryData(const QByteArray& data);
 
 private:
     void childDeleted(SessionItem* child);
diff --git a/GUI/coregui/Models/SessionXML.cpp b/GUI/coregui/Models/SessionXML.cpp
index e2c7645c290..4bb6fa114a4 100644
--- a/GUI/coregui/Models/SessionXML.cpp
+++ b/GUI/coregui/Models/SessionXML.cpp
@@ -19,6 +19,7 @@
 #include "GUI/coregui/Models/SessionItemTags.h"
 #include "GUI/coregui/Models/SessionModel.h"
 #include "GUI/coregui/Views/MaterialEditor/ExternalProperty.h"
+#include "GUI/coregui/utils/DeserializationException.h"
 #include "GUI/coregui/utils/GUIHelpers.h"
 #include "GUI/coregui/utils/MessageService.h"
 #include <QtCore/QXmlStreamWriter>
@@ -63,9 +64,13 @@ void SessionXML::writeItemAndChildItems(QXmlStreamWriter* writer, const SessionI
     for (auto child : item->children())
         writeItemAndChildItems(writer, child);
 
-    writer->writeStartElement(SessionXML::ArbitraryData);
-    item->writeNonSessionItemData(writer);
-    writer->writeEndElement();
+    QByteArray a = item->serializeBinaryData();
+    if (!a.isEmpty()) {
+        writer->writeStartElement(SessionXML::BinaryData);
+        writer->writeAttribute(SessionXML::Version, "1");
+        writer->writeCharacters(a.toBase64());
+        writer->writeEndElement();
+    }
 
     if (item->parent())
         writer->writeEndElement(); // ItemTag
@@ -148,8 +153,15 @@ void SessionXML::readItems(QXmlStreamReader* reader, SessionItem* parent, QStrin
                 }
             } else if (reader->name() == SessionXML::ParameterTag) {
                 SessionXML::readProperty(reader, parent, messageService);
-            } else if (reader->name() == SessionXML::ArbitraryData) {
-                parent->readNonSessionItemData(reader);
+            } else if (reader->name() == SessionXML::BinaryData) {
+                if (reader->attributes().value(SessionXML::Version).toInt() == 1) {
+                    QString valueAsBase64 =
+                        reader->readElementText(QXmlStreamReader::SkipChildElements);
+                    const auto data = QByteArray::fromBase64(
+                        valueAsBase64.toLatin1()); // #baimport add a unit test for this!
+                    parent->deserializeBinaryData(data);
+                } else
+                    throw DeserializationException::tooNew();
             }
         } else if (reader->isEndElement()) {
             if (reader->name() == SessionXML::ItemTag && parent)
diff --git a/GUI/coregui/Models/SessionXML.h b/GUI/coregui/Models/SessionXML.h
index 8ba1352a76e..366933bbca4 100644
--- a/GUI/coregui/Models/SessionXML.h
+++ b/GUI/coregui/Models/SessionXML.h
@@ -40,7 +40,8 @@ const QString ItemTag("Item");
 const QString ModelTypeAttribute("ModelType");
 const QString DisplayNameAttribute("DisplayName");
 const QString ParameterTag("Parameter");
-const QString ArbitraryData("ArbitraryData");
+const QString BinaryData("BinaryData");
+const QString Version("Version");
 const QString ParameterNameAttribute("ParName");
 const QString ParameterTypeAttribute("ParType");
 const QString ParameterValueAttribute("ParValue");
diff --git a/GUI/coregui/utils/DeserializationException.cpp b/GUI/coregui/utils/DeserializationException.cpp
new file mode 100644
index 00000000000..a3ef79413c2
--- /dev/null
+++ b/GUI/coregui/utils/DeserializationException.cpp
@@ -0,0 +1,33 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/coregui/utils/DeserializationException.cpp
+//! @brief     Implements class DeserializationException
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/coregui/utils/DeserializationException.h"
+#include <QString>
+
+DeserializationException::DeserializationException(const QString& t) : m_text(t) {}
+
+DeserializationException DeserializationException::tooNew()
+{
+    return DeserializationException("The found file is too new");
+}
+
+DeserializationException DeserializationException::streamError()
+{
+    return DeserializationException("The data seems to be corrupted");
+}
+
+QString DeserializationException::text() const
+{
+    return m_text;
+}
diff --git a/GUI/coregui/utils/DeserializationException.h b/GUI/coregui/utils/DeserializationException.h
new file mode 100644
index 00000000000..9245e59fc81
--- /dev/null
+++ b/GUI/coregui/utils/DeserializationException.h
@@ -0,0 +1,34 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/coregui/utils/DeserializationException.h
+//! @brief     Defines class DeserializationException
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_GUI_COREGUI_UTILS_DESERIALIZATIONEXCEPTION_H
+#define BORNAGAIN_GUI_COREGUI_UTILS_DESERIALIZATIONEXCEPTION_H
+
+#include <QString>
+
+class DeserializationException {
+private:
+    DeserializationException(const QString& t);
+
+public:
+    static DeserializationException tooNew();
+    static DeserializationException streamError();
+
+    QString text() const;
+
+private:
+    QString m_text;
+};
+
+#endif // BORNAGAIN_GUI_COREGUI_UTILS_DESERIALIZATIONEXCEPTION_H
-- 
GitLab