1 /************************************************************************
2  **
3  **  @file   VVSTConverter.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   15 7, 2015
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2015 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "vvstconverter.h"
30 
31 #include <QDomNode>
32 #include <QDomNodeList>
33 #include <QDomText>
34 #include <QFile>
35 #include <QLatin1String>
36 #include <QList>
37 #include <QMap>
38 #include <QMultiMap>
39 #include <QStaticStringData>
40 #include <QStringData>
41 #include <QStringDataPtr>
42 #include <QGlobalStatic>
43 
44 #include "../exception/vexception.h"
45 #include "../vmisc/def.h"
46 #include "vabstractmconverter.h"
47 
48 /*
49  * Version rules:
50  * 1. Version have three parts "major.minor.patch";
51  * 2. major part only for stable releases;
52  * 3. minor - 10 or more patch changes, or one big change;
53  * 4. patch - little change.
54  */
55 
56 const QString VVSTConverter::MeasurementMinVerStr = QStringLiteral("0.3.0");
57 const QString VVSTConverter::MeasurementMaxVerStr = QStringLiteral("0.5.1");
58 const QString VVSTConverter::CurrentSchema        = QStringLiteral("://schema/multisize_measurements/v0.5.1.xsd");
59 
60 //VVSTConverter::MeasurementMinVer; // <== DON'T FORGET TO UPDATE TOO!!!!
61 //VVSTConverter::MeasurementMaxVer; // <== DON'T FORGET TO UPDATE TOO!!!!
62 
63 namespace
64 {
65 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strTagRead_Only, (QLatin1String("read-only")))
66 }
67 
68 //---------------------------------------------------------------------------------------------------------------------
VVSTConverter(const QString & fileName)69 VVSTConverter::VVSTConverter(const QString &fileName)
70     :VAbstractMConverter(fileName)
71 {
72     ValidateInputFile(CurrentSchema);
73 }
74 
75 //---------------------------------------------------------------------------------------------------------------------
XSDSchema(int ver) const76 QString VVSTConverter::XSDSchema(int ver) const
77 {
78     QHash <int, QString> schemas =
79     {
80         std::make_pair(FORMAT_VERSION(0, 3, 0), QStringLiteral("://schema/multisize_measurements/v0.3.0.xsd")),
81         std::make_pair(FORMAT_VERSION(0, 4, 0), QStringLiteral("://schema/multisize_measurements/v0.4.0.xsd")),
82         std::make_pair(FORMAT_VERSION(0, 4, 1), QStringLiteral("://schema/multisize_measurements/v0.4.1.xsd")),
83         std::make_pair(FORMAT_VERSION(0, 4, 2), QStringLiteral("://schema/multisize_measurements/v0.4.2.xsd")),
84         std::make_pair(FORMAT_VERSION(0, 4, 3), QStringLiteral("://schema/multisize_measurements/v0.4.3.xsd")),
85         std::make_pair(FORMAT_VERSION(0, 4, 4), QStringLiteral("://schema/multisize_measurements/v0.4.4.xsd")),
86         std::make_pair(FORMAT_VERSION(0, 5, 0), QStringLiteral("://schema/multisize_measurements/v0.5.0.xsd")),
87         std::make_pair(FORMAT_VERSION(0, 5, 1), CurrentSchema),
88     };
89 
90     if (schemas.contains(ver))
91     {
92         return schemas.value(ver);
93     }
94     else
95     {
96         InvalidVersion(ver);
97     }
98 }
99 
100 //---------------------------------------------------------------------------------------------------------------------
ApplyPatches()101 void VVSTConverter::ApplyPatches()
102 {
103     switch (m_ver)
104     {
105         case (FORMAT_VERSION(0, 3, 0)):
106             ToV0_4_0();
107             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 0)));
108             Q_FALLTHROUGH();
109         case (FORMAT_VERSION(0, 4, 0)):
110             ToV0_4_1();
111             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 1)));
112             Q_FALLTHROUGH();
113         case (FORMAT_VERSION(0, 4, 1)):
114             ToV0_4_2();
115             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 2)));
116             Q_FALLTHROUGH();
117         case (FORMAT_VERSION(0, 4, 2)):
118             ToV0_4_3();
119             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 3)));
120             Q_FALLTHROUGH();
121         case (FORMAT_VERSION(0, 4, 3)):
122             ToV0_4_4();
123             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 4)));
124             Q_FALLTHROUGH();
125         case (FORMAT_VERSION(0, 4, 4)):
126             ToV0_5_0();
127             ValidateXML(XSDSchema(FORMAT_VERSION(0, 5, 0)));
128             Q_FALLTHROUGH();
129         case (FORMAT_VERSION(0, 5, 0)):
130             ToV0_5_1();
131             ValidateXML(XSDSchema(FORMAT_VERSION(0, 5, 1)));
132             Q_FALLTHROUGH();
133         case (FORMAT_VERSION(0, 5, 1)):
134             break;
135         default:
136             InvalidVersion(m_ver);
137             break;
138     }
139 }
140 
141 //---------------------------------------------------------------------------------------------------------------------
DowngradeToCurrentMaxVersion()142 void VVSTConverter::DowngradeToCurrentMaxVersion()
143 {
144     SetVersion(MeasurementMaxVerStr);
145     Save();
146 }
147 
148 //---------------------------------------------------------------------------------------------------------------------
IsReadOnly() const149 bool VVSTConverter::IsReadOnly() const
150 {
151     // Check if attribute read-only was not changed in file format
152     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMaxVer == FORMAT_VERSION(0, 5, 1),
153                       "Check attribute read-only.");
154 
155     // Possibly in future attribute read-only will change position etc.
156     // For now position is the same for all supported format versions.
157     // But don't forget to keep all versions of attribute until we support that format versions
158 
159     return UniqueTagText(*strTagRead_Only, falseStr) == trueStr;
160 }
161 
162 //---------------------------------------------------------------------------------------------------------------------
AddNewTagsForV0_4_0()163 void VVSTConverter::AddNewTagsForV0_4_0()
164 {
165     // TODO. Delete if minimal supported version is 0.4.0
166     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
167                       "Time to refactor the code.");
168 
169     QDomElement rootElement = this->documentElement();
170     QDomNode refChild = rootElement.firstChildElement(QStringLiteral("version"));
171 
172     refChild = rootElement.insertAfter(CreateElementWithText(QStringLiteral("read-only"), falseStr), refChild);
173 
174     rootElement.insertAfter(CreateElementWithText(QStringLiteral("notes"),
175                                                   UniqueTagText(QStringLiteral("description"))), refChild);
176 }
177 
178 //---------------------------------------------------------------------------------------------------------------------
RemoveTagsForV0_4_0()179 void VVSTConverter::RemoveTagsForV0_4_0()
180 {
181     // TODO. Delete if minimal supported version is 0.4.0
182     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
183                       "Time to refactor the code.");
184 
185     QDomElement rootElement = this->documentElement();
186 
187     {
188         const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("description"));
189         if (not nodeList.isEmpty())
190         {
191             rootElement.removeChild(nodeList.at(0));
192         }
193     }
194 
195     {
196         const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("id"));
197         if (not nodeList.isEmpty())
198         {
199             rootElement.removeChild(nodeList.at(0));
200         }
201     }
202 }
203 
204 //---------------------------------------------------------------------------------------------------------------------
ConvertMeasurementsToV0_4_0()205 void VVSTConverter::ConvertMeasurementsToV0_4_0()
206 {
207     // TODO. Delete if minimal supported version is 0.4.0
208     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
209                       "Time to refactor the code.");
210 
211     const QString tagBM = QStringLiteral("body-measurements");
212 
213     QDomElement bm = createElement(tagBM);
214 
215     const QMultiMap<QString, QString> names = OldNamesToNewNames_InV0_3_0();
216     const QList<QString> keys = names.uniqueKeys();
217     for (auto &key : keys)
218     {
219         qreal resValue = 0;
220         qreal resSizeIncrease = 0;
221         qreal resHeightIncrease = 0;
222 
223         // This has the same effect as a .values(), just isn't as elegant
224         const QList<QString> list = names.values( key );
225         for(const auto &val : list)
226         {
227             const QDomNodeList nodeList = this->elementsByTagName(val);
228             if (nodeList.isEmpty())
229             {
230                 continue;
231             }
232 
233             QDomElement m = nodeList.at(0).toElement();
234             const qreal value = GetParametrDouble(m, QStringLiteral("value"), "0.0");
235             const qreal size_increase = GetParametrDouble(m, QStringLiteral("size_increase"), "0.0");
236             const qreal height_increase = GetParametrDouble(m, QStringLiteral("height_increase"), "0.0");
237 
238             if (not qFuzzyIsNull(value))
239             {
240                 resValue = value;
241                 resSizeIncrease = size_increase;
242                 resHeightIncrease = height_increase;
243             }
244         }
245 
246         bm.appendChild(AddMV0_4_0(key, resValue, resSizeIncrease, resHeightIncrease));
247     }
248 
249     QDomElement rootElement = this->documentElement();
250     const QDomNodeList listBM = elementsByTagName(tagBM);
251     rootElement.replaceChild(bm, listBM.at(0));
252 }
253 
254 //---------------------------------------------------------------------------------------------------------------------
AddMV0_4_0(const QString & name,qreal value,qreal sizeIncrease,qreal heightIncrease)255 QDomElement VVSTConverter::AddMV0_4_0(const QString &name, qreal value, qreal sizeIncrease, qreal heightIncrease)
256 {
257     // TODO. Delete if minimal supported version is 0.4.0
258     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
259                       "Time to refactor the code.");
260 
261     QDomElement element = createElement(QStringLiteral("m"));
262 
263     SetAttribute(element, QStringLiteral("name"), name);
264     SetAttribute(element, QStringLiteral("base"), QString().setNum(value));
265     SetAttribute(element, QStringLiteral("size_increase"), QString().setNum(sizeIncrease));
266     SetAttribute(element, QStringLiteral("height_increase"), QString().setNum(heightIncrease));
267     SetAttribute(element, QStringLiteral("description"),QString());
268     SetAttribute(element, QStringLiteral("full_name"), QString());
269 
270     return element;
271 }
272 
273 //---------------------------------------------------------------------------------------------------------------------
PM_SystemV0_4_1()274 void VVSTConverter::PM_SystemV0_4_1()
275 {
276     // TODO. Delete if minimal supported version is 0.4.1
277     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 1),
278                       "Time to refactor the code.");
279 
280     const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("size"));
281     QDomElement personal = nodeList.at(0).toElement();
282 
283     QDomElement parent = personal.parentNode().toElement();
284     parent.insertBefore(CreateElementWithText(QStringLiteral("pm_system"), QStringLiteral("998")), personal);
285 }
286 
287 //---------------------------------------------------------------------------------------------------------------------
ConvertMeasurementsToV0_4_2()288 void VVSTConverter::ConvertMeasurementsToV0_4_2()
289 {
290     // TODO. Delete if minimal supported version is 0.4.2
291     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 2),
292                       "Time to refactor the code.");
293 
294     const QMap<QString, QString> names = OldNamesToNewNames_InV0_3_3();
295     auto i = names.constBegin();
296     while (i != names.constEnd())
297     {
298         const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("m"));
299         if (nodeList.isEmpty())
300         {
301             ++i;
302             continue;
303         }
304 
305         for (int ii = 0; ii < nodeList.size(); ++ii)
306         {
307             const QString attrName = QStringLiteral("name");
308             QDomElement element = nodeList.at(ii).toElement();
309             const QString name = GetParametrString(element, attrName);
310             if (name == i.value())
311             {
312                 SetAttribute(element, attrName, i.key());
313             }
314         }
315 
316         ++i;
317     }
318 }
319 
320 //---------------------------------------------------------------------------------------------------------------------
AddNewTagsForV0_5_0()321 void VVSTConverter::AddNewTagsForV0_5_0()
322 {
323     // TODO. Delete if minimal supported version is 0.5.0
324     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 0),
325                       "Time to refactor the code.");
326 
327     QDomElement root = documentElement();
328     const QDomElement pmSystemTag = root.firstChildElement(QStringLiteral("pm_system"));
329     if (pmSystemTag.isNull())
330     {
331         return;
332     }
333 
334     QDomElement dimensionsTag = createElement(QStringLiteral("dimensions"));
335 
336     auto Base = [this](const QString &base)
337     {
338         const QDomElement root = documentElement();
339         const QDomElement baseTag = root.firstChildElement(base);
340         if (baseTag.isNull())
341         {
342             return 0;
343         }
344 
345         return GetParametrInt(baseTag, QStringLiteral("base"), QChar('0'));
346     };
347 
348     const Unit units = Units();
349 
350     {
351         const int step = static_cast<int>(UnitConvertor(6, Unit::Cm, units));
352         const int min = static_cast<int>(UnitConvertor(50, Unit::Cm, units));
353         const int max = static_cast<int>(UnitConvertor(200, Unit::Cm, units));
354 
355         QDomElement dimensionX = createElement(QStringLiteral("dimension"));
356         SetAttribute(dimensionX, QStringLiteral("type"), QChar('x'));
357         SetAttribute(dimensionX, QStringLiteral("step"), step);
358         SetAttribute(dimensionX, QStringLiteral("min"), min);
359         SetAttribute(dimensionX, QStringLiteral("max"), max);
360         SetAttribute(dimensionX, QStringLiteral("base"), Base(QStringLiteral("height")));
361         dimensionsTag.appendChild(dimensionX);
362     }
363 
364     {
365         const int step = static_cast<int>(UnitConvertor(2, Unit::Cm, units));
366         const int min = static_cast<int>(UnitConvertor(22, Unit::Cm, units));
367         const int max = static_cast<int>(UnitConvertor(72, Unit::Cm, units));
368 
369         QDomElement dimensionY = createElement(QStringLiteral("dimension"));
370         SetAttribute(dimensionY, QStringLiteral("type"), QChar('y'));
371         SetAttribute(dimensionY, QStringLiteral("step"), step);
372         SetAttribute(dimensionY, QStringLiteral("min"), min);
373         SetAttribute(dimensionY, QStringLiteral("max"), max);
374         SetAttribute(dimensionY, QStringLiteral("base"), Base(QStringLiteral("size")));
375         SetAttribute(dimensionY, QStringLiteral("circumference"), true);
376         dimensionsTag.appendChild(dimensionY);
377     }
378 
379     root.insertAfter(dimensionsTag, pmSystemTag);
380 
381     root.insertAfter(createElement(QStringLiteral("restrictions")), dimensionsTag);
382 }
383 
384 //---------------------------------------------------------------------------------------------------------------------
RemoveTagsForV0_5_0()385 void VVSTConverter::RemoveTagsForV0_5_0()
386 {
387     // TODO. Delete if minimal supported version is 0.5.0
388     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 0),
389                       "Time to refactor the code.");
390 
391     QDomElement root = documentElement();
392 
393     const QDomElement sizeTag = root.firstChildElement(QStringLiteral("size"));
394     if (not sizeTag.isNull())
395     {
396         root.removeChild(sizeTag);
397     }
398 
399     const QDomElement heightTag = root.firstChildElement(QStringLiteral("height"));
400     if (not heightTag.isNull())
401     {
402         root.removeChild(heightTag);
403     }
404 }
405 
406 //---------------------------------------------------------------------------------------------------------------------
ConvertMeasurementsToV0_5_0()407 void VVSTConverter::ConvertMeasurementsToV0_5_0()
408 {
409     // TODO. Delete if minimal supported version is 0.5.0
410     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 0),
411                       "Time to refactor the code.");
412 
413     const QDomNodeList measurements = elementsByTagName(QStringLiteral("m"));
414     for (int i = 0; i < measurements.size(); ++i)
415     {
416         QDomElement m = measurements.at(i).toElement();
417 
418         SetAttribute(m, QStringLiteral("shiftA"), GetParametrString(m, QStringLiteral("height_increase")));
419         m.removeAttribute(QStringLiteral("height_increase"));
420 
421         SetAttribute(m, QStringLiteral("shiftB"), GetParametrString(m, QStringLiteral("size_increase")));
422         m.removeAttribute(QStringLiteral("size_increase"));
423     }
424 }
425 
426 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_0()427 void VVSTConverter::ToV0_4_0()
428 {
429     // TODO. Delete if minimal supported version is 0.4.0
430     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
431                       "Time to refactor the code.");
432 
433     AddRootComment();
434     SetVersion(QStringLiteral("0.4.0"));
435     AddNewTagsForV0_4_0();
436     RemoveTagsForV0_4_0();
437     ConvertMeasurementsToV0_4_0();
438     Save();
439 }
440 
441 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_1()442 void VVSTConverter::ToV0_4_1()
443 {
444     // TODO. Delete if minimal supported version is 0.4.1
445     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 1),
446                       "Time to refactor the code.");
447 
448     SetVersion(QStringLiteral("0.4.1"));
449     PM_SystemV0_4_1();
450     Save();
451 }
452 
453 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_2()454 void VVSTConverter::ToV0_4_2()
455 {
456     // TODO. Delete if minimal supported version is 0.4.2
457     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 2),
458                       "Time to refactor the code.");
459 
460     SetVersion(QStringLiteral("0.4.2"));
461     ConvertMeasurementsToV0_4_2();
462     Save();
463 }
464 
465 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_3()466 void VVSTConverter::ToV0_4_3()
467 {
468     // TODO. Delete if minimal supported version is 0.4.3
469     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 3),
470                       "Time to refactor the code.");
471 
472     SetVersion(QStringLiteral("0.4.3"));
473     Save();
474 }
475 
476 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_4()477 void VVSTConverter::ToV0_4_4()
478 {
479     // TODO. Delete if minimal supported version is 0.4.4
480     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 4),
481                       "Time to refactor the code.");
482 
483     SetVersion(QStringLiteral("0.4.4"));
484     Save();
485 }
486 
487 //---------------------------------------------------------------------------------------------------------------------
ToV0_5_0()488 void VVSTConverter::ToV0_5_0()
489 {
490     // TODO. Delete if minimal supported version is 0.5.0
491     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 0),
492                       "Time to refactor the code.");
493 
494     SetVersion(QStringLiteral("0.5.0"));
495     AddNewTagsForV0_5_0();
496     RemoveTagsForV0_5_0();
497     ConvertMeasurementsToV0_5_0();
498     Save();
499 }
500 
501 //---------------------------------------------------------------------------------------------------------------------
ToV0_5_1()502 void VVSTConverter::ToV0_5_1()
503 {
504     // TODO. Delete if minimal supported version is 0.5.1
505     Q_STATIC_ASSERT_X(VVSTConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 1),
506                       "Time to refactor the code.");
507 
508     SetVersion(QStringLiteral("0.5.1"));
509     Save();
510 }
511