BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
SessionXML.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/Model/Model/SessionXML.cpp
6 //! @brief Implements reader and writer classes for SessionModel
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
22 #include "GUI/Util/ComboProperty.h"
24 #include "GUI/Util/Error.h"
26 #include <QColor>
27 #include <QUuid>
28 #include <QXmlStreamWriter>
29 
30 namespace {
31 
32 void report_error(MessageService* messageService, SessionModel* model, const QString& message)
33 {
34  if (!messageService)
35  throw Error(QString("Warning: ") + message);
36  messageService->addWarning(model, message);
37 }
38 
39 SessionItem* createItem(SessionItem* parent, const QString& modelType, const QString& tag)
40 {
41  ASSERT(parent);
42  if (parent->hasModelType<GroupItem>()) {
43  if (const auto* groupItem = dynamic_cast<const GroupItem*>(parent))
44  return groupItem->getItemOfType(modelType);
45  return nullptr;
46  }
47  if (parent->sessionItemTags()->isSingleItemTag(tag))
48  return parent->getItem(tag);
49  try {
50  return parent->model()->insertNewItem(modelType, parent, -1, tag);
51  } catch (const std::exception&) {
52  return nullptr; // error will be reported later
53  }
54 }
55 
56 } // namespace
57 
58 
59 void GUI::Session::XML::writeModel(QXmlStreamWriter* writer, SessionItem* modelRootItem)
60 {
61  ASSERT(writer);
62  ASSERT(modelRootItem);
63  writer->writeStartElement(modelRootItem->model()->getModelTag());
64 
65  writeItemAndChildItems(writer, modelRootItem);
66 
67  writer->writeEndElement(); // m_model_tag
68 }
69 
70 void GUI::Session::XML::writeItemAndChildItems(QXmlStreamWriter* writer, const SessionItem* item)
71 {
72  ASSERT(item);
73  if (item->parentItem()) {
74  writer->writeStartElement(ItemTag);
75  writer->writeAttribute(ModelTypeAttribute, item->modelType());
76  QString tag = item->parentItem()->tagFromItem(item);
77  writer->writeAttribute(Tags::TagAttribute, tag);
78  writer->writeAttribute(Tags::DisplayNameAttribute,
80  for (int role : item->getRoles()) {
81  if (role == Qt::DisplayRole || role == Qt::EditRole)
82  writeVariant(writer, item->value(), role);
83  }
84  }
85 
86  for (auto* child : item->children())
87  if (item->allowWritingChildToXml(child))
88  writeItemAndChildItems(writer, child);
89 
90  QByteArray a = item->serializeBinaryData();
91  if (!a.isEmpty()) {
92  writer->writeStartElement(Tags::BinaryData);
93  writer->writeAttribute(XML::Version, "1");
94  writer->writeCharacters(a.toBase64());
95  writer->writeEndElement();
96  }
97 
98  QString nonSessionItemContent;
99  QXmlStreamWriter nonSessionItemStream(&nonSessionItemContent);
100  nonSessionItemStream.writeStartElement(Tags::NonSessionItemData); // for following writeAttrib
101  const auto sizeBefore = nonSessionItemContent.size();
102  item->writeNonSessionItems(&nonSessionItemStream);
103  if (sizeBefore != nonSessionItemContent.size()) {
104  writer->writeStartElement(Tags::NonSessionItemData);
105  item->writeNonSessionItems(writer);
106  writer->writeEndElement();
107  }
108 
109  if (item->parentItem())
110  writer->writeEndElement(); // ItemTag
111 }
112 
113 void GUI::Session::XML::readItems(QXmlStreamReader* reader, SessionItem* parent, QString topTag,
114  MessageService* messageService)
115 {
116  bool legacyDistributionFound = false;
117  ASSERT(reader);
118  ASSERT(parent);
119  const QString start_type = parent->model()->getModelTag();
120  while (!reader->atEnd()) {
121  reader->readNext();
122  if (reader->isStartElement()) {
123  if (reader->name() == XML::ItemTag) {
124  const QString model_type =
125  reader->attributes().value(Tags::ModelTypeAttribute).toString();
126 
127  if (model_type == "ParticleDistribution") {
128  if (!legacyDistributionFound) {
129  QString message(
130  "This file contains particle distributions in the sample. Elements of "
131  "this type are not supported any more. Reading the file will be "
132  "continued as much as possible, but you should review the sample.");
133 
134  report_error(messageService, parent->model(), message);
135  }
136  legacyDistributionFound = true;
137  reader->skipCurrentElement();
138  continue;
139  }
140 
141  QString tag = reader->attributes().value(Tags::TagAttribute).toString();
142  QString displayName =
143  reader->attributes().value(Tags::DisplayNameAttribute).toString();
144 
145  if (!topTag.isEmpty()) {
146  // to handle copying of item to another parent
147  tag = topTag;
148  topTag.clear();
149  }
150  auto* newItem = createItem(parent, model_type, tag);
151  if (!newItem) {
152  QString message = QString("Error while parsing XML. Can't create item of "
153  "modelType '%1' for tag '%2', parent '%3'")
154  .arg(model_type, tag, parent->itemName());
155  report_error(messageService, parent->model(), message);
156  // risky attempt to recover the rest of the project
157  reader->skipCurrentElement();
158  } else {
159  parent = newItem;
160  parent->setDisplayName(displayName);
161  }
162  ASSERT(reader->name() == ItemTag);
163  } else if (reader->name() == ParameterTag) {
164  readProperty(reader, parent);
166  } else if (reader->name() == Tags::BinaryData) {
167  if (reader->attributes().value(XML::Version).toInt() == 1) {
168  QString valueAsBase64 =
169  reader->readElementText(QXmlStreamReader::SkipChildElements);
170  const auto data = QByteArray::fromBase64(
171  valueAsBase64.toLatin1()); // #baimport add a unit test for this!
172  parent->deserializeBinaryData(data);
173  } else
176  } else if (reader->name() == Tags::NonSessionItemData) {
177  parent->readNonSessionItems(reader);
179  }
180  } else if (reader->isEndElement()) {
181  if (reader->name() == XML::ItemTag && parent)
182  parent = parent->parentItem();
183  if (reader->name() == start_type)
184  break;
185  }
186  }
187 }
188 
189 QString GUI::Session::XML::readProperty(QXmlStreamReader* reader, SessionItem* item)
190 {
191  ASSERT(reader);
192  const QString parameter_name =
193  reader->attributes().value(Tags::ParameterNameAttribute).toString();
194  const QString parameter_type =
195  reader->attributes().value(Tags::ParameterTypeAttribute).toString();
196  const int role = reader->attributes().value(Tags::ParameterRoleAttribute).toInt();
197 
198  if (!item)
199  throw Error(
200  QString("Attempt to set property '%1' for non existing item").arg(parameter_name));
201 
202  QVariant variant;
203  if (parameter_type == "double") {
204  double parameter_value =
205  reader->attributes().value(Tags::ParameterValueAttribute).toDouble();
206  variant = parameter_value;
207  } else if (parameter_type == "int") {
208  int parameter_value = reader->attributes().value(Tags::ParameterValueAttribute).toInt();
209  variant = parameter_value;
210  } else if (parameter_type == "uint") {
211  unsigned parameter_value =
212  reader->attributes().value(Tags::ParameterValueAttribute).toUInt();
213  variant = parameter_value;
214  } else if (parameter_type == "bool") {
215  bool parameter_value = reader->attributes().value(Tags::ParameterValueAttribute).toInt();
216  variant = parameter_value;
217  } else if (parameter_type == "QString") {
218  QString parameter_value =
219  reader->attributes().value(Tags::ParameterValueAttribute).toString();
220  variant = parameter_value;
221  } else if (parameter_type == "QColor") {
222  QString parameter_value =
223  reader->attributes().value(Tags::ParameterValueAttribute).toString();
224  variant = QColor(parameter_value);
225  } else if (parameter_type == "ExternalProperty") {
226  const bool isMaterialItemColor =
227  item->parentItem() != nullptr && dynamic_cast<MaterialItem*>(item->parentItem());
228 
229  if (isMaterialItemColor) {
230  // This handles legacy XML files for MaterialItem:
231  // In former times, the color of a MaterialItem was held as an "ExternalProperty". This
232  // has been changed to a simple QColor property => use only the color value of the
233  // "ExternalProperty" and store it as QColor in the variant
234  const QString colorName =
235  reader->attributes().value(Tags::ExternalPropertyColorAtt).toString();
236  variant = QColor(colorName);
237  } else {
238  // This handles legacy XML files for items referring to a material:
239  // In former times, the material in e.g. a ParticleItem was held as an
240  // "ExternalProperty". This has been changed to the material's identifier only (as
241  // QString)
242  // => use only the identifier value of the "ExternalProperty" and store it as QString in
243  // the variant
244  const QString identifier =
245  reader->attributes().value(Tags::ExternalPropertyIdentifierAtt).toString();
246  variant = identifier;
247  }
248  } else if (parameter_type == "ComboProperty") {
249  QString selections = reader->attributes().value(Tags::ParameterValueAttribute).toString();
250  QString values = reader->attributes().value(Tags::ParameterExtAttribute).toString();
251 
252  ComboProperty combo_property;
253  combo_property.setStringOfValues(values);
254  combo_property.setStringOfSelections(selections);
255 
256  variant = combo_property.variant();
257  } else {
258  QString message = QString("SessionModel::readProperty: parameter type not supported '"
259  + parameter_type + "'");
260  throw Error(message);
261  }
262 
263  if (variant.isValid())
264  item->setRoleProperty(role, variant);
265 
266  return parameter_name;
267 }
Defines class ComboProperty.
Defines class DeserializationException.
Defines error class.
Defines class GroupItem.
Defines class MaterialItem.
Defines MessageService class.
Defines class SessionFlags.
Defines class SessionItemTags.
Defines class SessionModel.
Defines reader and writer classes for SessionModel.
Defines.
Custom property to define list of string values with multiple selections. Intended for QVariant.
Definition: ComboProperty.h:25
void setStringOfValues(const QString &values)
Sets values from the string containing delimeter ';'.
void setStringOfSelections(const QString &values)
Sets selected indices from string.
QVariant variant() const
Constructs variant enclosing given ComboProperty.
static DeserializationException tooNew()
Definition: Error.h:21
The service to collect messages from different senders.
void addWarning(QObject *sender, const QString &description)
bool isSingleItemTag(const QString &tagName) const
Base class for a GUI data item.
Definition: SessionItem.h:204
QString itemName() const
Get item name, return display name if no name is set.
QVariant value() const
Get value.
QVector< int > getRoles() const
Returns vector of all present roles.
bool hasModelType() const
Definition: SessionItem.h:421
const SessionItemTags * sessionItemTags() const
void setDisplayName(const QString &display_name)
Set display name.
virtual void deserializeBinaryData(const QByteArray &data)
SessionModel * model() const
Returns model of this item.
Definition: SessionItem.cpp:60
virtual QByteArray serializeBinaryData() const
SessionItem * parentItem() const
Returns parent of this item.
Definition: SessionItem.cpp:67
virtual bool allowWritingChildToXml(SessionItem *child) const
QVariant roleProperty(int role) const
Returns corresponding variant under given role, invalid variant when role is not present.
virtual void writeNonSessionItems(QXmlStreamWriter *writer) const
QVector< SessionItem * > children() const
Returns vector of all children.
Definition: SessionItem.cpp:95
QString modelType() const
Get model type.
QString tagFromItem(const SessionItem *item) const
Returns the tag name of given item when existing.
bool setRoleProperty(int role, const QVariant &value)
Set the contained role property to the given value. See also setTranslatorForRolePropertySetter.
SessionItem * getItem(const QString &tag="", int row=0) const
Returns item in given row of given tag.
virtual void readNonSessionItems(QXmlStreamReader *reader)
Base class for a GUI data collection. A collection is e.g. all real data (RealDataModel)....
Definition: SessionModel.h:42
QString getModelTag() const
Definition: SessionModel.h:202
SessionItem * insertNewItem(QString model_type, SessionItem *parent_item=nullptr, int row=-1, QString tag="")
constexpr auto ParameterRoleAttribute("ParRole")
constexpr auto ParameterTypeAttribute("ParType")
constexpr auto BinaryData("BinaryData")
constexpr auto ExternalPropertyColorAtt("Color")
constexpr auto NonSessionItemData("NonSessionItemData")
constexpr auto ParameterValueAttribute("ParValue")
constexpr auto ParameterExtAttribute("ParExt")
constexpr auto DisplayNameAttribute("DisplayName")
constexpr auto ParameterNameAttribute("ParName")
constexpr auto TagAttribute("Tag")
constexpr auto ExternalPropertyIdentifierAtt("Identifier")
void writeItemAndChildItems(QXmlStreamWriter *writer, const SessionItem *item)
Definition: SessionXML.cpp:70
void writeVariant(QXmlStreamWriter *writer, QVariant variant, int role)
Write the variant as a complete tag, including the given role.
Definition: UtilXML.cpp:137
void readItems(QXmlStreamReader *reader, SessionItem *parent, QString topTag="", MessageService *messageService=nullptr)
Definition: SessionXML.cpp:113
constexpr auto ParameterTag("Parameter")
void writeModel(QXmlStreamWriter *writer, SessionItem *modelRootItem)
Definition: SessionXML.cpp:59
constexpr auto ModelTypeAttribute("ModelType")
QString readProperty(QXmlStreamReader *reader, SessionItem *item)
Definition: SessionXML.cpp:189
constexpr auto ItemTag("Item")
void gotoEndElementOfTag(QXmlStreamReader *reader, const QString &tag)
Definition: UtilXML.cpp:49
constexpr auto Version("version")