1 /************************************************************************
2  **
3  **  @file   vvitconverter.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 "vvitconverter.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 VVITConverter::MeasurementMinVerStr = QStringLiteral("0.2.0");
57 const QString VVITConverter::MeasurementMaxVerStr = QStringLiteral("0.5.1");
58 const QString VVITConverter::CurrentSchema        = QStringLiteral("://schema/individual_measurements/v0.5.1.xsd");
59 
60 //VVITConverter::MeasurementMinVer; // <== DON'T FORGET TO UPDATE TOO!!!!
61 //VVITConverter::MeasurementMaxVer; // <== DON'T FORGET TO UPDATE TOO!!!!
62 
63 namespace
64 {
65 // The list of all string we use for conversion
66 // Better to use global variables because repeating QStringLiteral blows up code size
67 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strTagRead_Only, (QLatin1String("read-only")))
68 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strGivenName, (QLatin1String("given-name")))
69 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strFamilyName, (QLatin1String("family-name")))
70 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strCustomer, (QLatin1String("customer")))
71 Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPersonal, (QLatin1String("personal")))
72 }
73 
74 //---------------------------------------------------------------------------------------------------------------------
VVITConverter(const QString & fileName)75 VVITConverter::VVITConverter(const QString &fileName)
76     :VAbstractMConverter(fileName)
77 {
78     ValidateInputFile(CurrentSchema);
79 }
80 
81 //---------------------------------------------------------------------------------------------------------------------
XSDSchema(int ver) const82 QString VVITConverter::XSDSchema(int ver) const
83 {
84     QHash <int, QString> schemas =
85     {
86         std::make_pair(FORMAT_VERSION(0, 2, 0), QStringLiteral("://schema/individual_measurements/v0.2.0.xsd")),
87         std::make_pair(FORMAT_VERSION(0, 3, 0), QStringLiteral("://schema/individual_measurements/v0.3.0.xsd")),
88         std::make_pair(FORMAT_VERSION(0, 3, 1), QStringLiteral("://schema/individual_measurements/v0.3.1.xsd")),
89         std::make_pair(FORMAT_VERSION(0, 3, 2), QStringLiteral("://schema/individual_measurements/v0.3.2.xsd")),
90         std::make_pair(FORMAT_VERSION(0, 3, 3), QStringLiteral("://schema/individual_measurements/v0.3.3.xsd")),
91         std::make_pair(FORMAT_VERSION(0, 4, 0), QStringLiteral("://schema/individual_measurements/v0.4.0.xsd")),
92         std::make_pair(FORMAT_VERSION(0, 5, 0), QStringLiteral("://schema/individual_measurements/v0.5.0.xsd")),
93         std::make_pair(FORMAT_VERSION(0, 5, 1), CurrentSchema),
94     };
95 
96     if (schemas.contains(ver))
97     {
98         return schemas.value(ver);
99     }
100     else
101     {
102         InvalidVersion(ver);
103     }
104 }
105 
106 //---------------------------------------------------------------------------------------------------------------------
ApplyPatches()107 void VVITConverter::ApplyPatches()
108 {
109     switch (m_ver)
110     {
111         case (FORMAT_VERSION(0, 2, 0)):
112             ToV0_3_0();
113             ValidateXML(XSDSchema(FORMAT_VERSION(0, 3, 0)));
114             Q_FALLTHROUGH();
115         case (FORMAT_VERSION(0, 3, 0)):
116             ToV0_3_1();
117             ValidateXML(XSDSchema(FORMAT_VERSION(0, 3, 1)));
118             Q_FALLTHROUGH();
119         case (FORMAT_VERSION(0, 3, 1)):
120             ToV0_3_2();
121             ValidateXML(XSDSchema(FORMAT_VERSION(0, 3, 2)));
122             Q_FALLTHROUGH();
123         case (FORMAT_VERSION(0, 3, 2)):
124             ToV0_3_3();
125             ValidateXML(XSDSchema(FORMAT_VERSION(0, 3, 3)));
126             Q_FALLTHROUGH();
127         case (FORMAT_VERSION(0, 3, 3)):
128             ToV0_4_0();
129             ValidateXML(XSDSchema(FORMAT_VERSION(0, 4, 0)));
130             Q_FALLTHROUGH();
131         case (FORMAT_VERSION(0, 4, 0)):
132             ToV0_5_0();
133             ValidateXML(XSDSchema(FORMAT_VERSION(0, 5, 0)));
134             Q_FALLTHROUGH();
135         case (FORMAT_VERSION(0, 5, 0)):
136             ToV0_5_1();
137             ValidateXML(XSDSchema(FORMAT_VERSION(0, 5, 1)));
138             Q_FALLTHROUGH();
139         case (FORMAT_VERSION(0, 5, 1)):
140             break;
141         default:
142             InvalidVersion(m_ver);
143             break;
144     }
145 }
146 
147 //---------------------------------------------------------------------------------------------------------------------
DowngradeToCurrentMaxVersion()148 void VVITConverter::DowngradeToCurrentMaxVersion()
149 {
150     SetVersion(MeasurementMaxVerStr);
151     Save();
152 }
153 
154 //---------------------------------------------------------------------------------------------------------------------
IsReadOnly() const155 bool VVITConverter::IsReadOnly() const
156 {
157     // Check if attribute read-only was not changed in file format
158     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMaxVer == FORMAT_VERSION(0, 5, 1),
159                       "Check attribute read-only.");
160 
161     // Possibly in future attribute read-only will change position etc.
162     // For now position is the same for all supported format versions.
163     // But don't forget to keep all versions of attribute until we support that format versions
164 
165     return UniqueTagText(*strTagRead_Only, falseStr) == trueStr;
166 }
167 
168 //---------------------------------------------------------------------------------------------------------------------
AddNewTagsForV0_3_0()169 void VVITConverter::AddNewTagsForV0_3_0()
170 {
171     // TODO. Delete if minimal supported version is 0.3.0
172     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 0),
173                       "Time to refactor the code.");
174 
175     QDomElement rootElement = this->documentElement();
176     QDomNode refChild = rootElement.firstChildElement(QStringLiteral("version"));
177 
178     refChild = rootElement.insertAfter(CreateElementWithText(QStringLiteral("read-only"), falseStr), refChild);
179     refChild = rootElement.insertAfter(createElement(QStringLiteral("notes")), refChild);
180 
181     rootElement.insertAfter(CreateElementWithText(QStringLiteral("unit"), MUnitV0_2_0()), refChild);
182 }
183 
184 //---------------------------------------------------------------------------------------------------------------------
MUnitV0_2_0()185 QString VVITConverter::MUnitV0_2_0()
186 {
187     // TODO. Delete if minimal supported version is 0.3.0
188     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 0),
189                       "Time to refactor the code.");
190 
191     return UniqueTagText(QStringLiteral("unit"), QStringLiteral("cm"));
192 }
193 
194 //---------------------------------------------------------------------------------------------------------------------
ConvertMeasurementsToV0_3_0()195 void VVITConverter::ConvertMeasurementsToV0_3_0()
196 {
197     // TODO. Delete if minimal supported version is 0.3.0
198     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 0),
199                       "Time to refactor the code.");
200 
201     const QString tagBM = QStringLiteral("body-measurements");
202 
203     QDomElement bm = createElement(tagBM);
204 
205     const QMultiMap<QString, QString> names = OldNamesToNewNames_InV0_3_0();
206     const QList<QString> keys = names.uniqueKeys();
207     for (auto &key : keys)
208     {
209         qreal resValue = 0;
210 
211         // This has the same effect as a .values(), just isn't as elegant
212         const QList<QString> list = names.values( key );
213         for(const auto &val : list)
214         {
215             const QDomNodeList nodeList = this->elementsByTagName(val);
216             if (nodeList.isEmpty())
217             {
218                 continue;
219             }
220 
221             const qreal value = GetParametrDouble(nodeList.at(0).toElement(), QStringLiteral("value"), "0.0");
222 
223             if (not qFuzzyIsNull(value))
224             {
225                 resValue = value;
226             }
227         }
228 
229         bm.appendChild(AddMV0_3_0(key, resValue));
230     }
231 
232     QDomElement rootElement = this->documentElement();
233     const QDomNodeList listBM = elementsByTagName(tagBM);
234     rootElement.replaceChild(bm, listBM.at(0));
235 }
236 
237 //---------------------------------------------------------------------------------------------------------------------
AddMV0_3_0(const QString & name,qreal value)238 QDomElement VVITConverter::AddMV0_3_0(const QString &name, qreal value)
239 {
240     // TODO. Delete if minimal supported version is 0.3.0
241     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 0),
242                       "Time to refactor the code.");
243 
244     QDomElement element = createElement(QStringLiteral("m"));
245 
246     SetAttribute(element, QStringLiteral("name"), name);
247     SetAttribute(element, QStringLiteral("value"), QString().setNum(value));
248     SetAttribute(element, QStringLiteral("description"), QString());
249     SetAttribute(element, QStringLiteral("full_name"), QString());
250 
251     return element;
252 }
253 
254 //---------------------------------------------------------------------------------------------------------------------
GenderV0_3_1()255 void VVITConverter::GenderV0_3_1()
256 {
257     // TODO. Delete if minimal supported version is 0.3.1
258     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 1),
259                       "Time to refactor the code.");
260 
261     const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("sex"));
262     QDomElement sex = nodeList.at(0).toElement();
263     QDomElement parent = sex.parentNode().toElement();
264     parent.replaceChild(CreateElementWithText(QStringLiteral("gender"), sex.text()), sex);
265 }
266 
267 //---------------------------------------------------------------------------------------------------------------------
PM_SystemV0_3_2()268 void VVITConverter::PM_SystemV0_3_2()
269 {
270     // TODO. Delete if minimal supported version is 0.3.2
271     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 2),
272                       "Time to refactor the code.");
273 
274     const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("personal"));
275     QDomElement personal = nodeList.at(0).toElement();
276 
277     QDomElement parent = personal.parentNode().toElement();
278     parent.insertBefore(CreateElementWithText(QStringLiteral("pm_system"), QStringLiteral("998")), personal);
279 }
280 
281 //---------------------------------------------------------------------------------------------------------------------
ConvertMeasurementsToV0_3_3()282 void VVITConverter::ConvertMeasurementsToV0_3_3()
283 {
284     // TODO. Delete if minimal supported version is 0.3.3
285     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 3),
286                       "Time to refactor the code.");
287 
288     const QMap<QString, QString> names = OldNamesToNewNames_InV0_3_3();
289     auto i = names.constBegin();
290     while (i != names.constEnd())
291     {
292         const QDomNodeList nodeList = this->elementsByTagName(QStringLiteral("m"));
293         if (nodeList.isEmpty())
294         {
295             ++i;
296             continue;
297         }
298 
299         for (int ii = 0; ii < nodeList.size(); ++ii)
300         {
301             const QString attrName = QStringLiteral("name");
302             QDomElement element = nodeList.at(ii).toElement();
303             const QString name = GetParametrString(element, attrName);
304             if (name == i.value())
305             {
306                 SetAttribute(element, attrName, i.key());
307             }
308         }
309 
310         ++i;
311     }
312 }
313 
314 //---------------------------------------------------------------------------------------------------------------------
ConverCustomerNameToV0_4_0()315 void VVITConverter::ConverCustomerNameToV0_4_0()
316 {
317     // Find root tag
318     const QDomNodeList personalList = this->elementsByTagName(*strPersonal);
319     if (personalList.isEmpty())
320     {
321         return;
322     }
323 
324     QDomNode personal = personalList.at(0);
325 
326     // Given name
327     QString givenName;
328     const QDomNodeList givenNameList = this->elementsByTagName(*strGivenName);
329     if (not givenNameList.isEmpty())
330     {
331         QDomNode givenNameNode = givenNameList.at(0);
332         givenName = givenNameNode.toElement().text();
333         personal.removeChild(givenNameNode);
334     }
335 
336     // Family name
337     QString familyName;
338     const QDomNodeList familyNameList = this->elementsByTagName(*strFamilyName);
339     if (not familyNameList.isEmpty())
340     {
341         QDomNode familyNameNode = familyNameList.at(0);
342         familyName = familyNameNode.toElement().text();
343         personal.removeChild(familyNameNode);
344     }
345 
346     QString customerName;
347     if (not givenName.isEmpty() && not familyName.isEmpty())
348     {
349         customerName = givenName + QLatin1Char(' ') + familyName;
350     }
351     else if (not givenName.isEmpty() && familyName.isEmpty())
352     {
353         customerName = givenName;
354     }
355     else if (givenName.isEmpty() && not familyName.isEmpty())
356     {
357         customerName = familyName;
358     }
359 
360     personal.insertBefore(CreateElementWithText(*strCustomer, not customerName.isEmpty() ? customerName : QString()),
361                           personal.firstChild());
362 }
363 
364 //---------------------------------------------------------------------------------------------------------------------
ToV0_3_0()365 void VVITConverter::ToV0_3_0()
366 {
367     // TODO. Delete if minimal supported version is 0.3.0
368     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 0),
369                       "Time to refactor the code.");
370 
371     AddRootComment();
372     SetVersion(QStringLiteral("0.3.0"));
373     AddNewTagsForV0_3_0();
374     ConvertMeasurementsToV0_3_0();
375     Save();
376 }
377 
378 //---------------------------------------------------------------------------------------------------------------------
ToV0_3_1()379 void VVITConverter::ToV0_3_1()
380 {
381     // TODO. Delete if minimal supported version is 0.3.1
382     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 1),
383                       "Time to refactor the code.");
384 
385     SetVersion(QStringLiteral("0.3.1"));
386     GenderV0_3_1();
387     Save();
388 }
389 
390 //---------------------------------------------------------------------------------------------------------------------
ToV0_3_2()391 void VVITConverter::ToV0_3_2()
392 {
393     // TODO. Delete if minimal supported version is 0.3.2
394     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 2),
395                       "Time to refactor the code.");
396 
397     SetVersion(QStringLiteral("0.3.2"));
398     PM_SystemV0_3_2();
399     Save();
400 }
401 
402 //---------------------------------------------------------------------------------------------------------------------
ToV0_3_3()403 void VVITConverter::ToV0_3_3()
404 {
405     // TODO. Delete if minimal supported version is 0.3.3
406     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 3, 3),
407                       "Time to refactor the code.");
408 
409     SetVersion(QStringLiteral("0.3.3"));
410     ConvertMeasurementsToV0_3_3();
411     Save();
412 }
413 
414 //---------------------------------------------------------------------------------------------------------------------
ToV0_4_0()415 void VVITConverter::ToV0_4_0()
416 {
417     // TODO. Delete if minimal supported version is 0.4.0
418     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 4, 0),
419                       "Time to refactor the code.");
420 
421     SetVersion(QStringLiteral("0.4.0"));
422     ConverCustomerNameToV0_4_0();
423     Save();
424 }
425 
426 //---------------------------------------------------------------------------------------------------------------------
ToV0_5_0()427 void VVITConverter::ToV0_5_0()
428 {
429     // TODO. Delete if minimal supported version is 0.5.0
430     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 0),
431                       "Time to refactor the code.");
432 
433     SetVersion(QStringLiteral("0.5.0"));
434     Save();
435 }
436 
437 //---------------------------------------------------------------------------------------------------------------------
ToV0_5_1()438 void VVITConverter::ToV0_5_1()
439 {
440     // TODO. Delete if minimal supported version is 0.5.1
441     Q_STATIC_ASSERT_X(VVITConverter::MeasurementMinVer < FORMAT_VERSION(0, 5, 1),
442                       "Time to refactor the code.");
443 
444     SetVersion(QStringLiteral("0.5.1"));
445     Save();
446 }
447