Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
berryXMLMemento.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "berryXMLMemento.h"
14 
15 #include "berryWorkbenchPlugin.h"
16 
17 #include <Poco/NumberParser.h>
18 #include <Poco/NumberFormatter.h>
19 #include <Poco/DOM/NodeList.h>
20 #include <Poco/XML/NamePool.h>
21 #include <Poco/DOM/NamedNodeMap.h>
22 #include <Poco/DOM/Text.h>
23 #include <Poco/DOM/Attr.h>
24 #include <Poco/DOM/DOMWriter.h>
25 #include <Poco/DOM/DOMParser.h>
26 #include <Poco/DOM/DOMBuilder.h>
27 #include <Poco/SAX/InputSource.h>
28 #include <Poco/SAX/SAXException.h>
29 
30 #include <limits>
31 
32 const QString EMPTY_STRING;
33 
34 berry::XMLMemento::XMLMemento(Poco::XML::Document* document,
35  Poco::XML::Element* elem) :
36  factory(document), element(elem)
37 {
38  factory->duplicate();
39  element->duplicate();
40 }
41 
43 {
44  element->release();
45  factory->release();
46 }
47 
50 {
51  return CreateReadRoot(reader, "");
52 }
53 
55  berry::XMLMemento::XMLByteInputStream& reader, const QString& baseDir)
56 {
57  QString errorMessage;
58  Poco::Exception exception("");
59 
60  try
61  {
62  Poco::XML::DOMParser parser;
63  Poco::XML::InputSource source(reader);
64 
65  source.setSystemId(baseDir.toStdString());
66  Poco::XML::Document* doc = parser.parse(&source);
67 
68  Poco::XML::Element* elem = doc->documentElement();
69 
70  XMLMemento::Pointer memento(new XMLMemento(doc, elem));
71 
72  doc->release();
73 
74  return memento;
75  }
76  catch (Poco::XML::SAXParseException& e)
77  {
78  errorMessage = QString("Could not parse content of XML file: ") + QString::fromStdString(e.displayText());
79  }
80 
81  QString problemText = QString::fromStdString(exception.message());
82  if (problemText.isEmpty())
83  {
84  problemText = errorMessage.isEmpty() ? "Could not find root element node of XML file." : errorMessage;
85  }
86  throw WorkbenchException(problemText);
87 
88 }
89 
91  const QString& type)
92 {
93  // TODO
94  // try{
95  auto doc = new Poco::XML::Document();
96  Poco::XML::Element* elem = doc->createElement(type.toStdString());
97  doc->appendChild(elem)->release();
98 
99  XMLMemento::Pointer memento(new XMLMemento(doc, elem));
100  doc->release();
101  return memento;
102  //}catch() //TODO: look for poco exceptions
103  //{
104  //}
105 }
106 
108  const QString& type)
109 {
110  Poco::XML::Element* child = factory->createElement(type.toStdString());
111  element->appendChild(child)->release();
112  IMemento::Pointer xmlChild(new XMLMemento(factory,child));
113  return xmlChild;
114 }
115 
117  const QString& type, const QString& id)
118 {
119  Poco::XML::Element* child = factory->createElement(type.toStdString());
120  child->setAttribute(TAG_ID.toStdString(), id.toStdString());
121  element->appendChild(child)->release();
122  IMemento::Pointer xmlChild(new XMLMemento(factory,child));
123  return xmlChild;
124 }
125 
127 {
128  //TODO check any casting errors
129  Poco::XML::Element* elem = child.Cast<berry::XMLMemento> ()->GetElement();
130  Poco::XML::Element* newElement =
131  dynamic_cast<Poco::XML::Element*> (factory->importNode(elem, true));
132  element->appendChild(newElement)->release();
133  IMemento::Pointer xmlCopy(new XMLMemento(factory, newElement));
134  return xmlCopy;
135 
136 }
137 
139 {
140  // Get the nodes.
142  Poco::XML::Element* child = element->getChildElement(type.toStdString()); // Find the first node which is a child of this node
143  if (child)
144  {
145  memento = new berry::XMLMemento(factory, child);
146  return memento;
147  }
148  // A child was not found.
149  return memento;
150 }
151 
152 QList<berry::IMemento::Pointer> berry::XMLMemento::GetChildren(
153  const QString& type) const
154 {
155  QList<IMemento::Pointer> mementos;
156  Poco::XML::NodeList* elementList = element->getElementsByTagName(type.toStdString());
157  for (unsigned long i = 0; i < elementList->length(); i++)
158  {
159  Poco::XML::Element* elem =
160  dynamic_cast<Poco::XML::Element*> (elementList->item(i));
161  IMemento::Pointer child(new berry::XMLMemento(factory, elem));
162  mementos << child;
163  }
164  elementList->release();
165 
166  return mementos;
167 }
168 
169 bool berry::XMLMemento::GetFloat(const QString& key, double& value) const
170 {
171  if (!element->hasAttribute(key.toStdString())) return false;
172 
173  const std::string& attr = element->getAttribute(key.toStdString());
174 
175  try
176  {
177  value = Poco::NumberParser::parseFloat(attr);
178  } catch (const Poco::SyntaxException& /*e*/)
179  {
180  std::string _qnan = Poco::NumberFormatter::format(std::numeric_limits<double>::quiet_NaN());
181  if (_qnan == attr)
182  {
183  value = std::numeric_limits<double>::quiet_NaN();
184  return true;
185  }
186 
187  std::string _inf = Poco::NumberFormatter::format(std::numeric_limits<double>::infinity());
188  if (_inf == attr)
189  {
190  value = std::numeric_limits<double>::infinity();
191  return true;
192  }
193 
194  WorkbenchPlugin::Log("Memento problem - invalid float for key: " + key
195  + " value: " + QString::fromStdString(attr));
196  return false;
197  }
198 
199  return true;
200 }
201 
203 {
204  return QString::fromStdString(element->nodeName());
205 }
206 
208 {
209  //TODO: make error handling!
210  return QString::fromStdString(element->getAttribute(TAG_ID.toStdString()));
211 }
212 
213 bool berry::XMLMemento::GetInteger(const QString& key, int& value) const
214 {
215  if (!element->hasAttribute(key.toStdString())) return false;
216 
217  const std::string& attr = element->getAttribute(key.toStdString());
218 
219  try
220  {
221  value = Poco::NumberParser::parse(attr);
222  }
223  catch (const Poco::SyntaxException& /*e*/)
224  {
225  WorkbenchPlugin::Log("Memento problem - invalid integer for key: " + key
226  + " value: " + QString::fromStdString(attr));
227  return false;
228  }
229 
230  return true;
231 }
232 
233 bool berry::XMLMemento::GetBoolean(const QString& key, bool& value) const
234 {
235  const std::string& attr = element->getAttribute(key.toStdString());
236  if (attr.empty())
237  return false;
238  else if (attr == "true")
239  {
240  value = true;
241  return true;
242  }
243  else
244  {
245  value = false;
246  return true;
247  }
248 }
249 
250 bool berry::XMLMemento::GetString(const QString& key, QString& value) const
251 {
252  QString v = QString::fromStdString(element->getAttribute(key.toStdString()));
253  if (v.isEmpty())
254  return false;
255 
256  value = v;
257  return true;
258 }
259 
261 {
262  Poco::XML::Text* textNode = GetTextNode();
263 
264  if (textNode != nullptr)
265  {
266  return QString::fromStdString(textNode->getData());
267  }
268 
269  return EMPTY_STRING;
270 }
271 
273 {
274  QList<QString> values;
275  Poco::XML::NamedNodeMap* nnMap = element->attributes();
276 
277  values.reserve(nnMap->length());
278 
279  for (unsigned long i = 0; i < nnMap->length(); i++)
280  {
281  values[i] = QString::fromStdString(nnMap->item(i)->nodeName()); //TODO check if right
282  }
283 
284  return values;
285 }
286 
287 Poco::XML::Text* berry::XMLMemento::GetTextNode() const
288 {
289 
290  //Get the nodes
291  Poco::XML::NodeList* nodes = element->childNodes();
292 
293  unsigned long size = nodes->length();
294 
295  if (size == 0)
296  return nullptr;
297 
298  //Search for the text node
299  for (unsigned long index = 0; index < size; index++)
300  {
301  if (nodes->item(index)->nodeType() == Poco::XML::Node::TEXT_NODE)
302  {
303  return dynamic_cast<Poco::XML::Text*> (nodes->item(index));
304  }
305  }
306 
307  // a Text node was not found
308  return nullptr;
309 
310 }
311 
312 void berry::XMLMemento::PutElement(Poco::XML::Element* element, bool copyText)
313 {
314  Poco::XML::NamedNodeMap* nodeMap = element->attributes();
315  unsigned long size = nodeMap->length();
316 
317  for (unsigned long index = 0; index < size; index++)
318  {
319  Poco::XML::Node* node = nodeMap->item(index);
320  Poco::XML::Attr* attr = dynamic_cast<Poco::XML::Attr*> (node);
321  PutString(QString::fromStdString(attr->nodeName()),
322  QString::fromStdString(attr->nodeValue()));
323  }
324  nodeMap->release();
325 
326  bool needToCopyText = copyText;
327  Poco::XML::Node* child = element->firstChild();
328  while (child)
329  {
330  unsigned short nodeType = child->nodeType();
331 
332  switch (nodeType)
333  {
334  case Poco::XML::Node::ELEMENT_NODE:
335  {
336  Poco::XML::Element* elem = dynamic_cast<Poco::XML::Element*> (child);
337  XMLMemento::Pointer child = CreateChild(QString::fromStdString(elem->nodeName())).Cast<
338  berry::XMLMemento> ();
339  child->PutElement(elem, true);
340  }
341  break;
342  case Poco::XML::Node::TEXT_NODE:
343  if (needToCopyText)
344  {
345  Poco::XML::Text* text = dynamic_cast<Poco::XML::Text*> (child);
346  PutTextData(QString::fromStdString(text->getData()));
347  needToCopyText = false;
348  }
349  break;
350 
351  default:
352  break;
353  }
354 
355  child = child->nextSibling();
356  }
357 }
358 
359 void berry::XMLMemento::PutFloat(const QString& key, double value)
360 {
361  std::string xmlValue = Poco::NumberFormatter::format(value);
362  element->setAttribute(key.toStdString(), xmlValue);
363 }
364 
365 void berry::XMLMemento::PutInteger(const QString& key, int value)
366 {
367  std::string xmlValue = Poco::NumberFormatter::format(value);
368  element->setAttribute(key.toStdString(), xmlValue);
369 }
370 
372 {
373  // Do not copy the element's top level text node (this would overwrite the existing text).
374  // Text nodes of children are copied.
375  PutElement(memento.Cast<berry::XMLMemento> ()->GetElement(), false);
376 }
377 
378 void berry::XMLMemento::PutString(const QString& key,
379  const QString& value)
380 {
381  element->setAttribute(key.toStdString(), value.toStdString());
382  //if (value == null) {
383  // return;}
384  //element.setAttribute(key, value);
385 }
386 
387 void berry::XMLMemento::PutBoolean(const QString& key, bool value)
388 {
389  if (value)
390  {
391  element->setAttribute(key.toStdString(), "true");
392  }
393  else
394  {
395  element->setAttribute(key.toStdString(), "false");
396  }
397 }
398 
399 void berry::XMLMemento::PutTextData(const QString& data)
400 {
401  Poco::XML::Text* textNode = GetTextNode();
402  if (textNode == nullptr)
403  {
404  textNode = factory->createTextNode(data.toStdString());
405  element->insertBefore(textNode, element->firstChild())->release();
406  }
407  else
408  {
409  textNode->setData(data.toStdString());
410  }
411 }
412 
414 {
415  if (writer.good())
416  {
417  Poco::XML::DOMWriter out;
418  out.setOptions(3); //write declaration and pretty print
419  out.writeNode(writer, factory);
420  }
421  else
422  {
423  //TODO
424  }
425 }
426 Poco::XML::Element* berry::XMLMemento::GetElement() const
427 {
428  return element;
429 }
430 
void PutString(const QString &key, const QString &value) override
QList< QString > GetAttributeKeys() const override
bool GetInteger(const QString &key, int &value) const override
void PutBoolean(const QString &key, bool value) override
std::istream XMLByteInputStream
static void Log(const QString &message)
IMemento::Pointer CopyChild(IMemento::Pointer child)
QList< IMemento::Pointer > GetChildren(const QString &type) const override
SmartPointer< Other > Cast() const
QString GetTextData() const override
void PutInteger(const QString &key, int value) override
bool GetFloat(const QString &key, double &value) const override
QString GetType() const override
static const QString TAG_ID
Definition: berryIMemento.h:70
std::ostream XMLByteOutputStream
const QString EMPTY_STRING
bool GetString(const QString &key, QString &value) const override
static XMLMemento::Pointer CreateReadRoot(berry::XMLMemento::XMLByteInputStream &reader)
void PutTextData(const QString &data) override
IMemento::Pointer CreateChild(const QString &type) override
IMemento::Pointer GetChild(const QString &type) const override
void PutFloat(const QString &key, double value) override
static XMLMemento::Pointer CreateWriteRoot(const QString &type)
QString GetID() const override
virtual Poco::XML::Element * GetElement() const
void Save(XMLByteOutputStream &writer)
bool GetBoolean(const QString &key, bool &value) const override
XMLMemento(Poco::XML::Document *document, Poco::XML::Element *elem)
void PutMemento(IMemento::Pointer memento) override