Seamly2D
Code documentation
vdomdocument.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Seamly, LLC *
4  * *
5  * https://github.com/fashionfreedom/seamly2d *
6  * *
7  ***************************************************************************
8  **
9  ** Seamly2D is free software: you can redistribute it and/or modify
10  ** it under the terms of the GNU General Public License as published by
11  ** the Free Software Foundation, either version 3 of the License, or
12  ** (at your option) any later version.
13  **
14  ** Seamly2D is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  ** GNU General Public License for more details.
18  **
19  ** You should have received a copy of the GNU General Public License
20  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
21  **
22  **************************************************************************
23 
24  ************************************************************************
25  **
26  ** @file vdomdocument.cpp
27  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
28  ** @date November 15, 2013
29  **
30  ** @brief
31  ** @copyright
32  ** This source code is part of the Valentine project, a pattern making
33  ** program, whose allow create and modeling patterns of clothing.
34  ** Copyright (C) 2013-2015 Seamly2D project
35  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
36  **
37  ** Seamly2D is free software: you can redistribute it and/or modify
38  ** it under the terms of the GNU General Public License as published by
39  ** the Free Software Foundation, either version 3 of the License, or
40  ** (at your option) any later version.
41  **
42  ** Seamly2D is distributed in the hope that it will be useful,
43  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
44  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  ** GNU General Public License for more details.
46  **
47  ** You should have received a copy of the GNU General Public License
48  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
49  **
50  *************************************************************************/
51 
52 #include "vdomdocument.h"
53 
54 #include <qcompilerdetection.h>
55 #include <qdom.h>
56 #include <QSaveFile>
57 
58 #include "../exception/vexceptionbadid.h"
59 #include "../exception/vexceptionconversionerror.h"
60 #include "../exception/vexceptionemptyparameter.h"
61 #include "../exception/vexceptionwrongid.h"
62 #include "../exception/vexception.h"
63 #include "../vmisc/logging.h"
64 #include "../ifcdef.h"
65 
66 #include <QAbstractMessageHandler>
67 #include <QByteArray>
68 #include <QDomNodeList>
69 #include <QDomText>
70 #include <QFile>
71 #include <QIODevice>
72 #include <QMessageLogger>
73 #include <QObject>
74 #include <QSourceLocation>
75 #include <QStringList>
76 #include <QTemporaryFile>
77 #include <QTextDocument>
78 #include <QTextStream>
79 #include <QUrl>
80 #include <QVector>
81 #include <QXmlSchema>
82 #include <QXmlSchemaValidator>
83 #include <QtDebug>
84 #include <QXmlStreamWriter>
85 
86 namespace
87 {
88 //---------------------------------------------------------------------------------------------------------------------
89 void SaveNodeCanonically(QXmlStreamWriter &stream, const QDomNode &domNode)
90 {
91  if (stream.hasError())
92  {
93  return;
94  }
95 
96  if (domNode.isElement())
97  {
98  const QDomElement domElement = domNode.toElement();
99  if (not domElement.isNull())
100  {
101  stream.writeStartElement(domElement.tagName());
102 
103  if (domElement.hasAttributes())
104  {
105  QMap<QString, QString> attributes;
106  const QDomNamedNodeMap attributeMap = domElement.attributes();
107  for (int i = 0; i < attributeMap.count(); ++i)
108  {
109  const QDomNode attribute = attributeMap.item(i);
110  attributes.insert(attribute.nodeName(), attribute.nodeValue());
111  }
112 
113  QMap<QString, QString>::const_iterator i = attributes.constBegin();
114  while (i != attributes.constEnd())
115  {
116  stream.writeAttribute(i.key(), i.value());
117  ++i;
118  }
119  }
120 
121  if (domElement.hasChildNodes())
122  {
123  QDomNode elementChild = domElement.firstChild();
124  while (not elementChild.isNull())
125  {
126  SaveNodeCanonically(stream, elementChild);
127  elementChild = elementChild.nextSibling();
128  }
129  }
130 
131  stream.writeEndElement();
132  }
133  }
134  else if (domNode.isComment())
135  {
136  stream.writeComment(domNode.nodeValue());
137  }
138  else if (domNode.isText())
139  {
140  stream.writeCharacters(domNode.nodeValue());
141  }
142 }
143 }
144 
145 //This class need for validation pattern file using XSD shema
146 class MessageHandler : public QAbstractMessageHandler
147 {
148 public:
150  : QAbstractMessageHandler(),
151  m_messageType(QtMsgType()),
152  m_description(),
153  m_sourceLocation(QSourceLocation())
154  {}
155 
156  QString statusMessage() const;
157  qint64 line() const;
158  qint64 column() const;
159 protected:
160  // cppcheck-suppress unusedFunction
161  virtual void handleMessage(QtMsgType type, const QString &description,
162  const QUrl &identifier, const QSourceLocation &sourceLocation) Q_DECL_OVERRIDE;
163 private:
164  QtMsgType m_messageType;
165  QString m_description;
166  QSourceLocation m_sourceLocation;
167 };
168 
169 //---------------------------------------------------------------------------------------------------------------------
171 {
172  QTextDocument doc;
173  doc.setHtml(m_description);
174  return doc.toPlainText();
175 }
176 
177 //---------------------------------------------------------------------------------------------------------------------
178 inline qint64 MessageHandler::line() const
179 {
180  return m_sourceLocation.line();
181 }
182 
183 //---------------------------------------------------------------------------------------------------------------------
184 inline qint64 MessageHandler::column() const
185 {
186  return m_sourceLocation.column();
187 }
188 
189 //---------------------------------------------------------------------------------------------------------------------
190 // cppcheck-suppress unusedFunction
191 void MessageHandler::handleMessage(QtMsgType type, const QString &description, const QUrl &identifier,
192  const QSourceLocation &sourceLocation)
193 {
194  Q_UNUSED(type)
195  Q_UNUSED(identifier)
196 
197  m_messageType = type;
198  m_description = description;
199  m_sourceLocation = sourceLocation;
200 }
201 
202 Q_LOGGING_CATEGORY(vXML, "v.xml")
203 
204 const QString VDomDocument::AttrId = QStringLiteral("id");
205 const QString VDomDocument::AttrText = QStringLiteral("text");
206 const QString VDomDocument::AttrBold = QStringLiteral("bold");
207 const QString VDomDocument::AttrItalic = QStringLiteral("italic");
208 const QString VDomDocument::AttrAlignment = QStringLiteral("alignment");
209 const QString VDomDocument::AttrFSIncrement = QStringLiteral("sfIncrement");
210 
211 const QString VDomDocument::TagVersion = QStringLiteral("version");
212 const QString VDomDocument::TagUnit = QStringLiteral("unit");
213 const QString VDomDocument::TagLine = QStringLiteral("line");
214 
215 //---------------------------------------------------------------------------------------------------------------------
217  : QDomDocument(),
218  map()
219 {}
220 
221 //---------------------------------------------------------------------------------------------------------------------
222 QDomElement VDomDocument::elementById(quint32 id, const QString &tagName)
223 {
224  if (id == 0)
225  {
226  return QDomElement();
227  }
228 
229  if (map.contains(id))
230  {
231  const QDomElement e = map[id];
232  if (e.parentNode().nodeType() != QDomNode::BaseNode)
233  {
234  return e;
235  }
236  map.remove(id);
237  }
238 
239  if (tagName.isEmpty())
240  {
241  if (this->find(this->documentElement(), id))
242  {
243  return map[id];
244  }
245  }
246  else
247  {
248  const QDomNodeList list = elementsByTagName(tagName);
249  for (int i=0; i < list.size(); ++i)
250  {
251  const QDomElement domElement = list.at(i).toElement();
252  if (not domElement.isNull() && domElement.hasAttribute(AttrId))
253  {
254  try
255  {
256  const quint32 elementId = GetParametrUInt(domElement, AttrId, NULL_ID_STR);
257 
258  this->map[elementId] = domElement;
259  if (elementId == id)
260  {
261  return domElement;
262  }
263  }
264  catch (const VExceptionConversionError &)
265  {
266  // do nothing
267  }
268  }
269  }
270  }
271 
272  return QDomElement();
273 }
274 
275 //---------------------------------------------------------------------------------------------------------------------
276 /**
277  * @brief Find element by id.
278  * @param node node
279  * @param id id value
280  * @return true if found
281  */
282 bool VDomDocument::find(const QDomElement &node, quint32 id)
283 {
284  if (node.hasAttribute(AttrId))
285  {
286  try
287  {
288  const quint32 elementId = GetParametrUInt(node, AttrId, NULL_ID_STR);
289 
290  this->map[elementId] = node;
291  if (elementId == id)
292  {
293  return true;
294  }
295  }
296  catch (const VExceptionConversionError &)
297  {
298  // do nothing
299  }
300  }
301 
302  for (qint32 i=0; i<node.childNodes().length(); ++i)
303  {
304  const QDomNode n = node.childNodes().at(i);
305  if (n.isElement())
306  {
307  if (this->find(n.toElement(), id))
308  {
309  return true;
310  }
311  }
312  }
313  return false;
314 }
315 
316 //---------------------------------------------------------------------------------------------------------------------
317 bool VDomDocument::SaveCanonicalXML(QIODevice *file, int indent, QString &error) const
318 {
319  SCASSERT(file != nullptr)
320 
321  QXmlStreamWriter stream(file);
322  stream.setAutoFormatting(true);
323  stream.setAutoFormattingIndent(indent);
324  stream.writeStartDocument();
325 
326  QDomNode root = documentElement();
327  while (not root.isNull())
328  {
329  SaveNodeCanonically(stream, root);
330  if (stream.hasError())
331  {
332  break;
333  }
334  root = root.nextSibling();
335  }
336 
337  stream.writeEndDocument();
338 
339  if (stream.hasError())
340  {
341  error = tr("Fail to write Canonical XML.");
342  return false;
343  }
344  return true;
345 }
346 
347 //---------------------------------------------------------------------------------------------------------------------
348 /**
349  * @brief Returns the long long value of the given attribute. RENAME: GetParameterLongLong?
350  * @param domElement tag in xml tree
351  * @param name attribute name
352  * @return long long value
353  */
354 quint32 VDomDocument::GetParametrUInt(const QDomElement &domElement, const QString &name, const QString &defValue)
355 {
356  Q_ASSERT_X(not name.isEmpty(), Q_FUNC_INFO, "name of parametr is empty");
357  Q_ASSERT_X(not domElement.isNull(), Q_FUNC_INFO, "domElement is null"); //-V591
358 
359  bool ok = false;
360  QString parametr;
361  quint32 id = 0;
362 
363  try
364  {
365  parametr = GetParametrString(domElement, name, defValue);
366  id = parametr.toUInt(&ok);
367  if (ok == false)
368  {
369  throw VExceptionConversionError(QObject::tr("Can't convert toUInt parameter"), name);
370  }
371  }
372  catch (const VExceptionEmptyParameter &e)
373  {
374  VExceptionConversionError excep(QObject::tr("Can't convert toUInt parameter"), name);
375  excep.AddMoreInformation(e.ErrorMessage());
376  throw excep;
377  }
378 
379  return id;
380 }
381 
382 //---------------------------------------------------------------------------------------------------------------------
383 bool VDomDocument::getParameterBool(const QDomElement &domElement, const QString &name, const QString &defValue)
384 {
385  Q_ASSERT_X(not name.isEmpty(), Q_FUNC_INFO, "name of parametr is empty");
386  Q_ASSERT_X(not domElement.isNull(), Q_FUNC_INFO, "domElement is null");
387 
388  QString parametr;
389  bool val = true;
390 
391  const QString message = QObject::tr("Can't convert toBool parameter");
392  try
393  {
394  parametr = GetParametrString(domElement, name, defValue);
395 
396  const QStringList bools = QStringList() << QLatin1String("true")
397  << QLatin1String("false")
398  << QLatin1String("1")
399  << QLatin1String("0");
400  switch (bools.indexOf(parametr))
401  {
402  case 0: // true
403  case 2: // 1
404  val = true;
405  break;
406  case 1: // false
407  case 3: // 0
408  val = false;
409  break;
410  default:// others
411  throw VExceptionConversionError(message, name);
412  }
413  }
414  catch (const VExceptionEmptyParameter &e)
415  {
416  VExceptionConversionError excep(message, name);
417  excep.AddMoreInformation(e.ErrorMessage());
418  throw excep;
419  }
420 
421  return val;
422 }
423 
424 //---------------------------------------------------------------------------------------------------------------------
425 NodeUsage VDomDocument::GetParametrUsage(const QDomElement &domElement, const QString &name)
426 {
427  const bool value = getParameterBool(domElement, name, trueStr);
428  if (value)
429  {
430  return NodeUsage::InUse;
431  }
432  else
433  {
434  return NodeUsage::NotInUse;
435  }
436 }
437 
438 //---------------------------------------------------------------------------------------------------------------------
439 void VDomDocument::SetParametrUsage(QDomElement &domElement, const QString &name, const NodeUsage &value)
440 {
441  if (value == NodeUsage::InUse)
442  {
443  domElement.setAttribute(name, trueStr);
444  }
445  else
446  {
447  domElement.setAttribute(name, falseStr);
448  }
449 }
450 
451 //---------------------------------------------------------------------------------------------------------------------
452 /**
453  * @brief Returns the string value of the given attribute. RENAME: see above
454  *
455  * if attribute empty return default value. If default value empty too throw exception.
456  * @return attribute value
457  * @throw VExceptionEmptyParameter when attribute is empty
458  */
459 QString VDomDocument::GetParametrString(const QDomElement &domElement, const QString &name,
460  const QString &defValue)
461 {
462  Q_ASSERT_X(not name.isEmpty(), Q_FUNC_INFO, "name of parametr is empty");
463  Q_ASSERT_X(not domElement.isNull(), Q_FUNC_INFO, "domElement is null");
464  const QString parameter = domElement.attribute(name, defValue);
465  if (parameter.isEmpty())
466  {
467  if (defValue.isEmpty())
468  {
469  throw VExceptionEmptyParameter(QObject::tr("Got empty parameter"), name, domElement);
470  }
471  else
472  {
473  return defValue;
474  }
475  }
476  return parameter;
477 }
478 
479 //---------------------------------------------------------------------------------------------------------------------
480 QString VDomDocument::GetParametrEmptyString(const QDomElement &domElement, const QString &name)
481 {
482  QString result;
483  try
484  {
485  result = GetParametrString(domElement, name, "");
486  }
487  catch(const VExceptionEmptyParameter &)
488  {
489  // do nothing
490  }
491  return result;
492 }
493 
494 //---------------------------------------------------------------------------------------------------------------------
495 /**
496  * @brief Returns the double value of the given attribute.
497  * @param domElement tag in xml tree
498  * @param name attribute name
499  * @return double value
500  */
501 qreal VDomDocument::GetParametrDouble(const QDomElement &domElement, const QString &name, const QString &defValue)
502 {
503  Q_ASSERT_X(not name.isEmpty(), Q_FUNC_INFO, "name of parametr is empty");
504  Q_ASSERT_X(not domElement.isNull(), Q_FUNC_INFO, "domElement is null");
505 
506  bool ok = false;
507  qreal param = 0;
508 
509  const QString message = QObject::tr("Can't convert toDouble parameter");
510  try
511  {
512  QString parametr = GetParametrString(domElement, name, defValue);
513  param = parametr.replace(",", ".").toDouble(&ok);
514  if (ok == false)
515  {
516  throw VExceptionConversionError(message, name);
517  }
518  }
519  catch (const VExceptionEmptyParameter &e)
520  {
521  VExceptionConversionError excep(message, name);
522  excep.AddMoreInformation(e.ErrorMessage());
523  throw excep;
524  }
525  return param;
526 }
527 
528 //---------------------------------------------------------------------------------------------------------------------
529 /**
530  * @brief getParameterId return value id attribute.
531  * @param domElement tag in xml tree.
532  * @return id value.
533  */
534 quint32 VDomDocument::getParameterId(const QDomElement &domElement)
535 {
536  Q_ASSERT_X(not domElement.isNull(), Q_FUNC_INFO, "domElement is null");
537 
538  quint32 id = NULL_ID;
539 
540  const QString message = QObject::tr("Got wrong parameter id. Need only id > 0.");
541  try
542  {
544  if (id == NULL_ID)
545  {
546  throw VExceptionWrongId(message, domElement);
547  }
548  }
549  catch (const VExceptionConversionError &e)
550  {
551  VExceptionWrongId excep(message, domElement);
552  excep.AddMoreInformation(e.ErrorMessage());
553  throw excep;
554  }
555  return id;
556 }
557 
558 //---------------------------------------------------------------------------------------------------------------------
560 {
562 
563  if (unit == Unit::Px)
564  {
565  unit = Unit::Cm;
566  }
567 
568  return unit;
569 }
570 
571 //---------------------------------------------------------------------------------------------------------------------
572 QString VDomDocument::UniqueTagText(const QString &tagName, const QString &defVal) const
573 {
574  const QDomNodeList nodeList = this->elementsByTagName(tagName);
575  if (nodeList.isEmpty())
576  {
577  return defVal;
578  }
579  else
580  {
581  const QDomNode domNode = nodeList.at(0);
582  if (domNode.isNull() == false && domNode.isElement())
583  {
584  const QDomElement domElement = domNode.toElement();
585  if (domElement.isNull() == false)
586  {
587  const QString text = domElement.text();
588  if (text.isEmpty())
589  {
590  return defVal;
591  }
592  else
593  {
594  return text;
595  }
596  }
597  }
598  }
599  return defVal;
600 }
601 
602 //---------------------------------------------------------------------------------------------------------------------
603 /**
604  * @brief TestUniqueId test exist unique id in pattern file. Each id must be unique.
605  */
607 {
608  QVector<quint32> vector;
609  CollectId(documentElement(), vector);
610 }
611 
612 //---------------------------------------------------------------------------------------------------------------------
613 void VDomDocument::CollectId(const QDomElement &node, QVector<quint32> &vector) const
614 {
615  if (node.hasAttribute(VDomDocument::AttrId))
616  {
617  const quint32 id = getParameterId(node);
618  if (vector.contains(id))
619  {
620  throw VExceptionWrongId(tr("This id is not unique."), node);
621  }
622  vector.append(id);
623  }
624 
625  for (qint32 i=0; i<node.childNodes().length(); ++i)
626  {
627  const QDomNode n = node.childNodes().at(i);
628  if (n.isElement())
629  {
630  CollectId(n.toElement(), vector);
631  }
632  }
633 }
634 
635 //---------------------------------------------------------------------------------------------------------------------
636 /**
637  * @brief ValidateXML validate xml file by xsd schema.
638  * @param schema path to schema file.
639  * @param fileName name of xml file.
640  */
641 void VDomDocument::ValidateXML(const QString &schema, const QString &fileName)
642 {
643  qCDebug(vXML, "Validation xml file %s.", qUtf8Printable(fileName));
644  QFile pattern(fileName);
645  // cppcheck-suppress ConfigurationNotChecked
646  if (pattern.open(QIODevice::ReadOnly) == false)
647  {
648  const QString errorMsg(tr("Can't open file %1:\n%2.").arg(fileName).arg(pattern.errorString()));
649  throw VException(errorMsg);
650  }
651 
652  QFile fileSchema(schema);
653  // cppcheck-suppress ConfigurationNotChecked
654  if (fileSchema.open(QIODevice::ReadOnly) == false)
655  {
656  pattern.close();
657  const QString errorMsg(tr("Can't open schema file %1:\n%2.").arg(schema).arg(fileSchema.errorString()));
658  throw VException(errorMsg);
659  }
660 
661  MessageHandler messageHandler;
662  QXmlSchema sch;
663  sch.setMessageHandler(&messageHandler);
664  if (sch.load(&fileSchema, QUrl::fromLocalFile(fileSchema.fileName()))==false)
665  {
666  pattern.close();
667  fileSchema.close();
668  VException e(messageHandler.statusMessage());
669  e.AddMoreInformation(tr("Could not load schema file '%1'.").arg(fileSchema.fileName()));
670  throw e;
671  }
672  qCDebug(vXML, "Schema loaded.");
673 
674  bool errorOccurred = false;
675  if (sch.isValid() == false)
676  {
677  errorOccurred = true;
678  }
679  else
680  {
681  QXmlSchemaValidator validator(sch);
682  if (validator.validate(&pattern, QUrl::fromLocalFile(pattern.fileName())) == false)
683  {
684  errorOccurred = true;
685  }
686  }
687 
688  if (errorOccurred)
689  {
690  pattern.close();
691  fileSchema.close();
692  VException e(messageHandler.statusMessage());
693  e.AddMoreInformation(tr("Validation error file %3 in line %1 column %2").arg(messageHandler.line())
694  .arg(messageHandler.column()).arg(fileName));
695  throw e;
696  }
697  pattern.close();
698  fileSchema.close();
699 }
700 
701 //---------------------------------------------------------------------------------------------------------------------
702 void VDomDocument::setXMLContent(const QString &fileName)
703 {
704  QFile file(fileName);
705  // cppcheck-suppress ConfigurationNotChecked
706  if (file.open(QIODevice::ReadOnly) == false)
707  {
708  const QString errorMsg(tr("Can't open file %1:\n%2.").arg(fileName).arg(file.errorString()));
709  throw VException(errorMsg);
710  }
711 
712  QString errorMsg;
713  int errorLine = -1;
714  int errorColumn = -1;
715  if (QDomDocument::setContent(&file, &errorMsg, &errorLine, &errorColumn) == false)
716  {
717  file.close();
718  VException e(errorMsg);
719  e.AddMoreInformation(tr("Parsing error file %3 in line %1 column %2").arg(errorLine).arg(errorColumn)
720  .arg(fileName));
721  throw e;
722  }
723 }
724 
725 //---------------------------------------------------------------------------------------------------------------------
727 {
728  QString r;
729  for (auto i = static_cast<int>(Unit::Mm), last = static_cast<int>(Unit::LAST_UNIT_DO_NOT_USE); i < last;++i)
730  {
731  r += UnitsToStr(static_cast<Unit>(i));
732  if (i < last - 1)
733  {
734  r += ", ";
735  }
736  }
737  return r;
738 }
739 
740 //---------------------------------------------------------------------------------------------------------------------
741 bool VDomDocument::SaveDocument(const QString &fileName, QString &error)
742 {
743  if (fileName.isEmpty())
744  {
745  qDebug()<<"Got empty file name.";
746  return false;
747  }
748  bool success = false;
749  QSaveFile file(fileName);
750  // cppcheck-suppress ConfigurationNotChecked
751  if (file.open(QIODevice::WriteOnly))
752  {
753  // See issue #666. QDomDocument produces random attribute order.
754  const int indent = 4;
755  if (not SaveCanonicalXML(&file, indent, error))
756  {
757  return false;
758  }
759  // Left these strings in case we will need them for testing purposes
760  // QTextStream out(&file);
761  // out.setCodec("UTF-8");
762  // save(out, indent);
763 
764  success = file.commit();
765  }
766 
767  if (not success)
768  {
769  error = file.errorString();
770  }
771 
772  return success;
773 }
774 
775 //---------------------------------------------------------------------------------------------------------------------
776 // cppcheck-suppress unusedFunction
777 QString VDomDocument::Major() const
778 {
779  QString version = UniqueTagText(TagVersion, "0.0.0");
780  QStringList v = version.split(".");
781  return v.at(0);
782 }
783 
784 //---------------------------------------------------------------------------------------------------------------------
785 // cppcheck-suppress unusedFunction
786 QString VDomDocument::Minor() const
787 {
788  QString version = UniqueTagText(TagVersion, "0.0.0");
789  QStringList v = version.split(".");
790  return v.at(1);
791 }
792 
793 //---------------------------------------------------------------------------------------------------------------------
794 // cppcheck-suppress unusedFunction
795 QString VDomDocument::Patch() const
796 {
797  QString version = UniqueTagText(TagVersion, "0.0.0");
798  QStringList v = version.split(".");
799  return v.at(2);
800 }
801 
802 //---------------------------------------------------------------------------------------------------------------------
803 bool VDomDocument::setTagText(const QString &tag, const QString &text)
804 {
805  const QDomNodeList nodeList = this->elementsByTagName(tag);
806  if (nodeList.isEmpty())
807  {
808  qDebug()<<"Can't save tag "<<tag<<Q_FUNC_INFO;
809  }
810  else
811  {
812  const QDomNode domNode = nodeList.at(0);
813  if (domNode.isNull() == false && domNode.isElement())
814  {
815  const QDomElement domElement = domNode.toElement();
816  return setTagText(domElement, text);
817  }
818  }
819  return false;
820 }
821 
822 //---------------------------------------------------------------------------------------------------------------------
823 bool VDomDocument::setTagText(const QDomElement &domElement, const QString &text)
824 {
825  if (domElement.isNull() == false)
826  {
827  QDomElement parent = domElement.parentNode().toElement();
828  QDomElement newTag = createElement(domElement.tagName());
829 
830  const QDomText newTagText = createTextNode(text);
831  newTag.appendChild(newTagText);
832 
833  parent.replaceChild(newTag, domElement);
834  return true;
835  }
836  return false;
837 }
838 
839 //---------------------------------------------------------------------------------------------------------------------
840 /**
841  * @brief RemoveAllChildren remove all children from file.
842  * @param domElement tag in xml tree.
843  */
844 void VDomDocument::RemoveAllChildren(QDomElement &domElement)
845 {
846  if ( domElement.hasChildNodes() )
847  {
848  while ( domElement.childNodes().length() >= 1 )
849  {
850  domElement.removeChild( domElement.firstChild() );
851  }
852  }
853 }
854 
855 //---------------------------------------------------------------------------------------------------------------------
856 QDomNode VDomDocument::ParentNodeById(const quint32 &nodeId)
857 {
858  QDomElement domElement = NodeById(nodeId);
859  return domElement.parentNode();
860 }
861 
862 //---------------------------------------------------------------------------------------------------------------------
863 QDomElement VDomDocument::CloneNodeById(const quint32 &nodeId)
864 {
865  QDomElement domElement = NodeById(nodeId);
866  return domElement.cloneNode().toElement();
867 }
868 
869 //---------------------------------------------------------------------------------------------------------------------
870 QDomElement VDomDocument::NodeById(const quint32 &nodeId)
871 {
872  QDomElement domElement = elementById(nodeId);
873  if (domElement.isNull() || domElement.isElement() == false)
874  {
875  throw VExceptionBadId(tr("Couldn't get node"), nodeId);
876  }
877  return domElement;
878 }
879 
880 //---------------------------------------------------------------------------------------------------------------------
881 bool VDomDocument::SafeCopy(const QString &source, const QString &destination, QString &error)
882 {
883  bool result = false;
884 
885 #ifdef Q_OS_WIN32
886  qt_ntfs_permission_lookup++; // turn checking on
887 #endif /*Q_OS_WIN32*/
888 
889  QTemporaryFile destFile(destination + QLatin1String(".XXXXXX"));
890  destFile.setAutoRemove(false);// Will be renamed to be destination file
891  // cppcheck-suppress ConfigurationNotChecked
892  if (not destFile.open())
893  {
894  error = destFile.errorString();
895  }
896  else
897  {
898  QFile sourceFile(source);
899  // cppcheck-suppress ConfigurationNotChecked
900  if (sourceFile.open(QIODevice::ReadOnly))
901  {
902  result = true;
903  char block[4096];
904  qint64 bytes;
905  while ((bytes = sourceFile.read(block, sizeof(block))) > 0)
906  {
907  if (bytes != destFile.write(block, bytes))
908  {
909  error = destFile.errorString();
910  result = false;
911  break;
912  }
913  }
914 
915  if (bytes == -1)
916  {
917  error = sourceFile.errorString();
918  result = false;
919  }
920 
921  if (result)
922  {
923  QFile::remove(destination);
924  if (not destFile.rename(destination))
925  {
926  error = destFile.errorString();
927  result = false;
928  }
929  else
930  {
931  result = true;
932  }
933  }
934  }
935  else
936  {
937  error = sourceFile.errorString();
938  }
939  }
940 
941 #ifdef Q_OS_WIN32
942  qt_ntfs_permission_lookup--; // turn off check permission again
943 #endif /*Q_OS_WIN32*/
944 
945  return result;
946 }
947 
948 //---------------------------------------------------------------------------------------------------------------------
950 {
951  // We use implicit conversion. That's why check if values are still the same as excpected.
952  Q_STATIC_ASSERT(Qt::AlignLeft == 1);
953  Q_STATIC_ASSERT(Qt::AlignRight == 2);
954  Q_STATIC_ASSERT(Qt::AlignHCenter == 4);
955 
957 
958  if (not element.isNull())
959  {
960  QDomElement tagLine = element.firstChildElement();
961  while (tagLine.isNull() == false)
962  {
963  if (tagLine.tagName() == TagLine)
964  {
965  VLabelTemplateLine line;
966  line.line = GetParametrString(tagLine, AttrText, tr("<empty>"));
967  line.bold = getParameterBool(tagLine, AttrBold, falseStr);
968  line.italic = getParameterBool(tagLine, AttrItalic, falseStr);
969  line.alignment = static_cast<int>(GetParametrUInt(tagLine, AttrAlignment, "0"));
970  line.fontSizeIncrement = static_cast<int>(GetParametrUInt(tagLine, AttrFSIncrement, "0"));
971  lines.append(line);
972  }
973  tagLine = tagLine.nextSiblingElement(TagLine);
974  }
975  }
976 
977  return lines;
978 }
979 
980 //---------------------------------------------------------------------------------------------------------------------
981 void VDomDocument::SetLabelTemplate(QDomElement &element, const QVector<VLabelTemplateLine> &lines)
982 {
983  if (not element.isNull())
984  {
985  for (int i=0; i < lines.size(); ++i)
986  {
987  QDomElement tagLine = createElement(TagLine);
988 
989  SetAttribute(tagLine, AttrText, lines.at(i).line);
990  SetAttribute(tagLine, AttrBold, lines.at(i).bold);
991  SetAttribute(tagLine, AttrItalic, lines.at(i).italic);
992  SetAttribute(tagLine, AttrAlignment, lines.at(i).alignment);
993  SetAttribute(tagLine, AttrFSIncrement, lines.at(i).fontSizeIncrement);
994 
995  element.appendChild(tagLine);
996  }
997  }
998 }
virtual void handleMessage(QtMsgType type, const QString &description, const QUrl &identifier, const QSourceLocation &sourceLocation) Q_DECL_OVERRIDE
QtMsgType m_messageType
QString m_description
QSourceLocation m_sourceLocation
QString statusMessage() const
qint64 line() const
qint64 column() const
The VDomDocument class represents a Seamly2D document (.val file).
Definition: vdomdocument.h:105
static const QString AttrItalic
Definition: vdomdocument.h:111
QDomElement elementById(quint32 id, const QString &tagName=QString())
static QString GetParametrString(const QDomElement &domElement, const QString &name, const QString &defValue=QString())
Returns the string value of the given attribute. RENAME: see above.
void SetLabelTemplate(QDomElement &element, const QVector< VLabelTemplateLine > &lines)
static const QString AttrBold
Definition: vdomdocument.h:110
static quint32 GetParametrUInt(const QDomElement &domElement, const QString &name, const QString &defValue)
Returns the long long value of the given attribute. RENAME: GetParameterLongLong?
static QString GetParametrEmptyString(const QDomElement &domElement, const QString &name)
QString Major() const
static const QString AttrAlignment
Definition: vdomdocument.h:112
Unit MUnit() const
static void ValidateXML(const QString &schema, const QString &fileName)
ValidateXML validate xml file by xsd schema.
QString Minor() const
bool find(const QDomElement &node, quint32 id)
Find element by id.
static QString UnitsHelpString()
QDomElement CloneNodeById(const quint32 &nodeId)
void CollectId(const QDomElement &node, QVector< quint32 > &vector) const
bool SaveCanonicalXML(QIODevice *file, int indent, QString &error) const
static bool getParameterBool(const QDomElement &domElement, const QString &name, const QString &defValue)
void TestUniqueId() const
TestUniqueId test exist unique id in pattern file. Each id must be unique.
bool setTagText(const QString &tag, const QString &text)
QHash< quint32, QDomElement > map
Map used for finding element by id.
Definition: vdomdocument.h:170
static void SetParametrUsage(QDomElement &domElement, const QString &name, const NodeUsage &value)
QString Patch() const
static qreal GetParametrDouble(const QDomElement &domElement, const QString &name, const QString &defValue)
Returns the double value of the given attribute.
QDomElement NodeById(const quint32 &nodeId)
static bool SafeCopy(const QString &source, const QString &destination, QString &error)
static const QString TagVersion
Definition: vdomdocument.h:115
static const QString TagUnit
Definition: vdomdocument.h:116
QString UniqueTagText(const QString &tagName, const QString &defVal=QString()) const
void SetAttribute(QDomElement &domElement, const QString &name, const T &value) const
SetAttribute set attribute in pattern file. Replace "," by ".".
Definition: vdomdocument.h:185
QDomNode ParentNodeById(const quint32 &nodeId)
static const QString AttrId
Definition: vdomdocument.h:108
virtual void setXMLContent(const QString &fileName)
static const QString TagLine
Definition: vdomdocument.h:117
static const QString AttrText
Definition: vdomdocument.h:109
static void RemoveAllChildren(QDomElement &domElement)
RemoveAllChildren remove all children from file.
virtual bool SaveDocument(const QString &fileName, QString &error)
static NodeUsage GetParametrUsage(const QDomElement &domElement, const QString &name)
QVector< VLabelTemplateLine > GetLabelTemplate(const QDomElement &element) const
static quint32 getParameterId(const QDomElement &domElement)
getParameterId return value id attribute.
static const QString AttrFSIncrement
Definition: vdomdocument.h:113
The VExceptionBadId class for exception bad id.
The VExceptionConversionError class for exception of conversion error.
virtual QString ErrorMessage() const Q_DECL_OVERRIDE
ErrorMessage return main error message.
The VExceptionEmptyParameter class for exception empty parameter.
virtual QString ErrorMessage() const Q_DECL_OVERRIDE
ErrorMessage return main error message.
The VExceptionWrongId class for exception wrong id.
The VException class parent for all exception. Could be use for abstract exception.
Definition: vexception.h:66
void AddMoreInformation(const QString &info)
AddMoreInformation add more information for error.
Definition: vexception.cpp:107
Unit StrToUnits(const QString &unit)
Definition: def.cpp:670
const QString unitCM
Definition: def.cpp:201
QString UnitsToStr(const Unit &unit, const bool translate)
UnitsToStr translate unit to string.
Definition: def.cpp:702
const QString trueStr
Definition: def.cpp:197
const QString falseStr
Definition: def.cpp:198
#define SCASSERT(cond)
Definition: def.h:317
Unit
Definition: def.h:105
@ LAST_UNIT_DO_NOT_USE
NodeUsage
Definition: def.h:107
#define NULL_ID
Definition: ifcdef.h:76
#define NULL_ID_STR
Definition: ifcdef.h:77
void SaveNodeCanonically(QXmlStreamWriter &stream, const QDomNode &domNode)
QString line
Definition: ifcdef.h:281
int fontSizeIncrement
Definition: ifcdef.h:285