FreeCAD

Форк
0
/
Metadata.cpp 
1212 строк · 35.4 Кб
1
/**************************************************************************
2
*                                                                         *
3
*   Copyright (c) 2021-2023 FreeCAD Project Association                   *
4
*                                                                         *
5
*   This file is part of FreeCAD.                                         *
6
*                                                                         *
7
*   FreeCAD is free software: you can redistribute it and/or modify it    *
8
*   under the terms of the GNU Lesser General Public License as           *
9
*   published by the Free Software Foundation, either version 2.1 of the  *
10
*   License, or (at your option) any later version.                       *
11
*                                                                         *
12
*   FreeCAD is distributed in the hope that it will be useful, but        *
13
*   WITHOUT ANY WARRANTY; without even the implied warranty of            *
14
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
15
*   Lesser General Public License for more details.                       *
16
*                                                                         *
17
*   You should have received a copy of the GNU Lesser General Public      *
18
*   License along with FreeCAD. If not, see                               *
19
*   <https://www.gnu.org/licenses/>.                                      *
20
*                                                                         *
21
***************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
# include <boost/core/ignore_unused.hpp>
27
# include <memory>
28
# include <sstream>
29
#endif
30

31
#include <xercesc/framework/LocalFileFormatTarget.hpp>
32
#include <xercesc/framework/LocalFileInputSource.hpp>
33
#include <xercesc/framework/MemBufInputSource.hpp>
34
#include <xercesc/sax/HandlerBase.hpp>
35

36
#include "App/Application.h"
37
#include "App/Expression.h"
38
#include "Base/XMLTools.h"
39

40
#include "Metadata.h"
41

42

43
/*
44
*** From GCC: ***
45
In the GNU C Library, "major" and "minor" are defined
46
by <sys/sysmacros.h>. For historical compatibility, it is
47
currently defined by <sys/types.h> as well, but we plan to
48
remove this soon. To use "major", include <sys/sysmacros.h>
49
directly. If you did not intend to use a system-defined macro
50
"major", you should undefine it after including <sys/types.h>.
51
*/
52
#ifdef major
53
#undef major
54
#endif
55
#ifdef minor
56
#undef minor
57
#endif
58

59
using namespace App;
60
namespace fs = boost::filesystem;
61
XERCES_CPP_NAMESPACE_USE
62

63
namespace MetadataInternal
64
{
65
class XMLErrorHandler: public HandlerBase
66
{
67
    void warning(const SAXParseException& toCatch) override
68
    {
69
        // Don't deal with warnings at all
70
        boost::ignore_unused(toCatch);
71
    }
72

73
    void error(const SAXParseException& toCatch) override
74
    {
75
        std::stringstream message;
76
        message << "Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
77
                << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
78
                << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
79
        throw Base::XMLBaseException(message.str());
80
    }
81

82
    void fatalError(const SAXParseException& toCatch) override
83
    {
84
        std::stringstream message;
85
        message << "Fatal error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
86
                << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
87
                << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
88
        throw Base::XMLBaseException(message.str());
89
    }
90
};
91
}// namespace MetadataInternal
92

93
Metadata::Metadata(const fs::path& metadataFile)
94
    : _dom(nullptr)
95
{
96
#if defined(FC_OS_WIN32)
97
    auto source =
98
        LocalFileInputSource(reinterpret_cast<const XMLCh*>(metadataFile.wstring().c_str()));
99
#else
100
    auto source = LocalFileInputSource(XUTF8Str(metadataFile.string().c_str()).unicodeForm());
101
#endif
102
    loadFromInputSource(source);
103
}
104

105
Metadata::Metadata()
106
    : _dom(nullptr)
107
{}
108

109
Metadata::Metadata(const DOMNode* domNode, int format)
110
    : _dom(nullptr)
111
{
112
    auto element = dynamic_cast<const DOMElement*>(domNode);
113
    if (element) {
114
        switch (format) {
115
            case 1:
116
                parseVersion1(element);
117
                break;
118
            default:
119
                throw Base::XMLBaseException(
120
                    "package.xml format version is not supported by this version of FreeCAD");
121
        }
122
    }
123
}
124

125
App::Metadata::Metadata(const std::string& rawData)
126
    : _dom(nullptr)
127
{
128
    MemBufInputSource buffer(
129
        reinterpret_cast<const XMLByte*>(rawData.c_str()),
130
        rawData.size(),
131
        "raw data (in memory)");
132
    loadFromInputSource(buffer);
133
}
134

135
void Metadata::loadFromInputSource(const InputSource& source)
136
{
137
    // Any exception thrown by the XML code propagates out and prevents object creation
138
    XMLPlatformUtils::Initialize();
139

140
    _parser = std::make_shared<XercesDOMParser>();
141
    _parser->setValidationScheme(XercesDOMParser::Val_Never);
142
    _parser->setDoNamespaces(true);
143

144
    auto errHandler = std::make_unique<MetadataInternal::XMLErrorHandler>();
145
    _parser->setErrorHandler(errHandler.get());
146

147
    _parser->parse(source);
148

149
    auto doc = _parser->getDocument();
150
    _dom = doc->getDocumentElement();
151

152
    auto rootTagName = StrXUTF8(_dom->getTagName()).str;
153
    if (rootTagName != "package") {
154
        throw Base::XMLBaseException(
155
            "Malformed package.xml document: Root <package> group not found");
156
    }
157
    auto formatVersion = XMLString::parseInt(_dom->getAttribute(XUTF8Str("format").unicodeForm()));
158
    switch (formatVersion) {
159
        case 1:
160
            parseVersion1(_dom);
161
            break;
162
        default:
163
            throw Base::XMLBaseException(
164
                "package.xml format version is not supported by this version of FreeCAD");
165
    }
166
}
167

168
Metadata::~Metadata() = default;
169

170
std::string Metadata::name() const
171
{
172
    return _name;
173
}
174

175
std::string Metadata::type() const
176
{
177
    return _type;
178
}
179

180
Meta::Version Metadata::version() const
181
{
182
    return _version;
183
}
184

185
std::string App::Metadata::date() const
186
{
187
    return _date;
188
}
189

190
std::string Metadata::description() const
191
{
192
    return _description;
193
}
194

195
std::vector<Meta::Contact> Metadata::maintainer() const
196
{
197
    return _maintainer;
198
}
199

200
std::vector<Meta::License> Metadata::license() const
201
{
202
    return _license;
203
}
204

205
std::vector<Meta::Url> Metadata::url() const
206
{
207
    return _url;
208
}
209

210
std::vector<Meta::Contact> Metadata::author() const
211
{
212
    return _author;
213
}
214

215
std::vector<Meta::Dependency> Metadata::depend() const
216
{
217
    return _depend;
218
}
219

220
std::vector<Meta::Dependency> Metadata::conflict() const
221
{
222
    return _conflict;
223
}
224

225
std::vector<Meta::Dependency> Metadata::replace() const
226
{
227
    return _replace;
228
}
229

230
std::vector<std::string> Metadata::tag() const
231
{
232
    return _tag;
233
}
234

235
fs::path Metadata::icon() const
236
{
237
    return _icon;
238
}
239

240
std::string Metadata::classname() const
241
{
242
    return _classname;
243
}
244

245
boost::filesystem::path Metadata::subdirectory() const
246
{
247
    return _subdirectory;
248
}
249

250
std::vector<fs::path> Metadata::file() const
251
{
252
    return _file;
253
}
254

255
Meta::Version Metadata::freecadmin() const
256
{
257
    return _freecadmin;
258
}
259

260
Meta::Version Metadata::freecadmax() const
261
{
262
    return _freecadmax;
263
}
264

265
Meta::Version Metadata::pythonmin() const
266
{
267
    return _pythonmin;
268
}
269

270
std::multimap<std::string, Metadata> Metadata::content() const
271
{
272
    return _content;
273
}
274

275
std::vector<Meta::GenericMetadata> Metadata::operator[](const std::string& tag) const
276
{
277
    std::vector<Meta::GenericMetadata> returnValue;
278
    auto range = _genericMetadata.equal_range(tag);
279
    for (auto item = range.first; item != range.second; ++item) {
280
        returnValue.push_back(item->second);
281
    }
282
    return returnValue;
283
}
284

285
XERCES_CPP_NAMESPACE::DOMElement* Metadata::dom() const
286
{
287
    return _dom;
288
}
289

290
void Metadata::setName(const std::string& name)
291
{
292
    std::string invalidCharacters = "/\\?%*:|\"<>";// Should cover all OSes
293
    if (_name.find_first_of(invalidCharacters) != std::string::npos) {
294
        throw Base::RuntimeError("Name cannot contain any of: " + invalidCharacters);
295
    }
296
    _name = name;
297
}
298

299
void Metadata::setType(const std::string& type)
300
{
301
    _type = type;
302
}
303

304
void Metadata::setVersion(const Meta::Version& version)
305
{
306
    _version = version;
307
}
308

309
void App::Metadata::setDate(const std::string& date)
310
{
311
    _date = date;
312
}
313

314
void Metadata::setDescription(const std::string& description)
315
{
316
    _description = description;
317
}
318

319
void Metadata::addMaintainer(const Meta::Contact& maintainer)
320
{
321
    _maintainer.push_back(maintainer);
322
}
323

324
void Metadata::addLicense(const Meta::License& license)
325
{
326
    _license.push_back(license);
327
}
328

329
void Metadata::addUrl(const Meta::Url& url)
330
{
331
    _url.push_back(url);
332
}
333

334
void Metadata::addAuthor(const Meta::Contact& author)
335
{
336
    _author.push_back(author);
337
}
338

339
void Metadata::addDepend(const Meta::Dependency& dep)
340
{
341
    _depend.push_back(dep);
342
}
343

344
void Metadata::addConflict(const Meta::Dependency& dep)
345
{
346
    _conflict.push_back(dep);
347
}
348

349
void Metadata::addReplace(const Meta::Dependency& dep)
350
{
351
    _replace.push_back(dep);
352
}
353

354
void Metadata::addTag(const std::string& tag)
355
{
356
    _tag.push_back(tag);
357
}
358

359
void Metadata::setIcon(const fs::path& path)
360
{
361
    _icon = path;
362
}
363

364
void Metadata::setClassname(const std::string& name)
365
{
366
    _classname = name;
367
}
368

369
void Metadata::setSubdirectory(const boost::filesystem::path& path)
370
{
371
    _subdirectory = path;
372
}
373

374
void Metadata::addFile(const fs::path& path)
375
{
376
    _file.push_back(path);
377
}
378

379
void Metadata::addContentItem(const std::string& tag, const Metadata& item)
380
{
381
    _content.insert(std::make_pair(tag, item));
382
}
383

384
void Metadata::setFreeCADMin(const Meta::Version& version)
385
{
386
    _freecadmin = version;
387
}
388

389
void Metadata::setPythonMin(const Meta::Version& version)
390
{
391
    _pythonmin = version;
392
}
393

394
void Metadata::setFreeCADMax(const Meta::Version& version)
395
{
396
    _freecadmax = version;
397
}
398

399
void Metadata::addGenericMetadata(const std::string& tag,
400
                                  const Meta::GenericMetadata& genericMetadata)
401
{
402
    _genericMetadata.insert(std::make_pair(tag, genericMetadata));
403
}
404

405
void Metadata::removeContentItem(const std::string& tag, const std::string& itemName)
406
{
407
    auto tagRange = _content.equal_range(tag);
408
    auto foundItem =
409
        std::find_if(tagRange.first, tagRange.second, [&itemName](const auto& check) -> bool {
410
            return itemName == check.second.name();
411
        });
412
    if (foundItem != tagRange.second) {
413
        _content.erase(foundItem);
414
    }
415
}
416

417
void Metadata::removeMaintainer(const Meta::Contact& maintainer)
418
{
419
    auto new_end = std::remove(_maintainer.begin(), _maintainer.end(), maintainer);
420
    _maintainer.erase(new_end, _maintainer.end());
421
}
422

423
void Metadata::removeLicense(const Meta::License& license)
424
{
425
    auto new_end = std::remove(_license.begin(), _license.end(), license);
426
    _license.erase(new_end, _license.end());
427
}
428

429
void Metadata::removeUrl(const Meta::Url& url)
430
{
431
    auto new_end = std::remove(_url.begin(), _url.end(), url);
432
    _url.erase(new_end, _url.end());
433
}
434

435
void Metadata::removeAuthor(const Meta::Contact& author)
436
{
437
    auto new_end = std::remove(_author.begin(), _author.end(), author);
438
    _author.erase(new_end, _author.end());
439
}
440

441
void Metadata::removeDepend(const Meta::Dependency& dep)
442
{
443
    bool found = false;
444
    for (const auto& check : _depend) {
445
        if (dep == check) {
446
            found = true;
447
        }
448
    }
449
    if (!found) {
450
        throw Base::RuntimeError("No match found for dependency to remove");
451
    }
452
    auto new_end = std::remove(_depend.begin(), _depend.end(), dep);
453
    _depend.erase(new_end, _depend.end());
454
}
455

456
void Metadata::removeConflict(const Meta::Dependency& dep)
457
{
458
    auto new_end = std::remove(_conflict.begin(), _conflict.end(), dep);
459
    _conflict.erase(new_end, _conflict.end());
460
}
461

462
void Metadata::removeReplace(const Meta::Dependency& dep)
463
{
464
    auto new_end = std::remove(_replace.begin(), _replace.end(), dep);
465
    _replace.erase(new_end, _replace.end());
466
}
467

468
void Metadata::removeTag(const std::string& tag)
469
{
470
    auto new_end = std::remove(_tag.begin(), _tag.end(), tag);
471
    _tag.erase(new_end, _tag.end());
472
}
473

474
void Metadata::removeFile(const boost::filesystem::path& path)
475
{
476
    auto new_end = std::remove(_file.begin(), _file.end(), path);
477
    _file.erase(new_end, _file.end());
478
}
479

480

481
void Metadata::clearContent()
482
{
483
    _content.clear();
484
}
485

486
void Metadata::clearMaintainer()
487
{
488
    _maintainer.clear();
489
}
490

491
void Metadata::clearLicense()
492
{
493
    _license.clear();
494
}
495

496
void Metadata::clearUrl()
497
{
498
    _url.clear();
499
}
500

501
void Metadata::clearAuthor()
502
{
503
    _author.clear();
504
}
505

506
void Metadata::clearDepend()
507
{
508
    _depend.clear();
509
}
510

511
void Metadata::clearConflict()
512
{
513
    _conflict.clear();
514
}
515

516
void Metadata::clearReplace()
517
{
518
    _replace.clear();
519
}
520

521
void Metadata::clearTag()
522
{
523
    _tag.clear();
524
}
525

526
void Metadata::clearFile()
527
{
528
    _file.clear();
529
}
530

531

532
DOMElement* appendSimpleXMLNode(DOMElement* baseNode, const std::string& nodeName,
533
                                const std::string& nodeContents)
534
{
535
    // For convenience (and brevity of final output) don't create nodes that don't have contents
536
    if (nodeContents.empty()) {
537
        return nullptr;
538
    }
539
    auto doc = baseNode->getOwnerDocument();
540
    DOMElement* namedElement = doc->createElement(XUTF8Str(nodeName.c_str()).unicodeForm());
541
    baseNode->appendChild(namedElement);
542
    DOMText* namedNode = doc->createTextNode(XUTF8Str(nodeContents.c_str()).unicodeForm());
543
    namedElement->appendChild(namedNode);
544
    return namedElement;
545
}
546

547
void addAttribute(DOMElement* node, const std::string& key, const std::string& value)
548
{
549
    if (value.empty()) {
550
        return;
551
    }
552
    node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str(value.c_str()).unicodeForm());
553
}
554

555
void addAttribute(DOMElement* node, const std::string& key, bool value)
556
{
557
    if (value) {
558
        node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str("True").unicodeForm());
559
    }
560
    else {
561
        node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str("False").unicodeForm());
562
    }
563
}
564

565
void addAttribute(DOMElement* node, const std::string& key, Meta::DependencyType value)
566
{
567
    // Someday we should be able to change this to use reflection, but it's not yet
568
    // available (using C++17)
569
    std::string stringified("automatic");
570
    switch (value) {
571
        case Meta::DependencyType::automatic:
572
            stringified = "automatic";
573
            break;
574
        case Meta::DependencyType::internal:
575
            stringified = "internal";
576
            break;
577
        case Meta::DependencyType::addon:
578
            stringified = "addon";
579
            break;
580
        case Meta::DependencyType::python:
581
            stringified = "python";
582
            break;
583
    }
584
    node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(),
585
                       XUTF8Str(stringified.c_str()).unicodeForm());
586
}
587

588
void addDependencyNode(DOMElement* root, const std::string& name, const Meta::Dependency& depend)
589
{
590
    auto element = appendSimpleXMLNode(root, name, depend.package);
591
    if (element) {
592
        addAttribute(element, "version_lt", depend.version_lt);
593
        addAttribute(element, "version_lte", depend.version_lte);
594
        addAttribute(element, "version_eq", depend.version_eq);
595
        addAttribute(element, "version_gte", depend.version_gte);
596
        addAttribute(element, "version_gt", depend.version_gt);
597
        addAttribute(element, "condition", depend.condition);
598
        addAttribute(element, "optional", depend.optional);
599
        addAttribute(element, "type", depend.dependencyType);
600
    }
601
}
602

603
void Metadata::write(const fs::path& file) const
604
{
605
    DOMImplementation* impl =
606
        DOMImplementationRegistry::getDOMImplementation(XUTF8Str("Core LS").unicodeForm());
607

608
    DOMDocument* doc = impl->createDocument(nullptr, XUTF8Str("package").unicodeForm(), nullptr);
609
    DOMElement* root = doc->getDocumentElement();
610
    root->setAttribute(XUTF8Str("format").unicodeForm(), XUTF8Str("1").unicodeForm());
611
    root->setAttribute(XUTF8Str("xmlns").unicodeForm(),
612
                       XUTF8Str("https://wiki.freecad.org/Package_Metadata").unicodeForm());
613

614
    appendToElement(root);
615

616
    DOMLSSerializer* theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer();
617
    DOMConfiguration* config = theSerializer->getDomConfig();
618
    if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true)) {
619
        config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
620
    }
621

622
    // set feature if the serializer supports the feature/mode
623
    if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, true)) {
624
        config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, true);
625
    }
626

627
    if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true)) {
628
        config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true);
629
    }
630

631
    try {
632
        XMLFormatTarget* myFormTarget = new LocalFileFormatTarget(file.string().c_str());
633
        DOMLSOutput* theOutput = ((DOMImplementationLS*)impl)->createLSOutput();
634

635
        theOutput->setByteStream(myFormTarget);
636
        theSerializer->write(doc, theOutput);
637

638
        theOutput->release();
639
        theSerializer->release();
640
        delete myFormTarget;
641
    }
642
    catch (const XMLException& toCatch) {
643
        char* message = XMLString::transcode(toCatch.getMessage());
644
        std::string what = message;
645
        XMLString::release(&message);
646
        throw Base::XMLBaseException(what);
647
    }
648
    catch (const DOMException& toCatch) {
649
        char* message = XMLString::transcode(toCatch.getMessage());
650
        std::string what = message;
651
        XMLString::release(&message);
652
        throw Base::XMLBaseException(what);
653
    }
654

655
    doc->release();
656
}
657

658
bool Metadata::satisfies(const Meta::Dependency& dep)
659
{
660
    if (dep.package != _name) {
661
        return false;
662
    }
663
    // The "condition" attribute allows an expression to enable or disable this dependency check: it must contain a valid
664
    // FreeCAD Expression. If it evaluates to false, this dependency is bypassed (e.g. this function returns false).
665
    if (!dep.condition.empty()) {
666
        auto injectedString = dep.condition;
667
        std::map<std::string, std::string> replacements;
668
        std::map<std::string, std::string>& config = App::Application::Config();
669
        replacements.insert(std::make_pair("$BuildVersionMajor", config["BuildVersionMajor"]));
670
        replacements.insert(std::make_pair("$BuildVersionMinor", config["BuildVersionMinor"]));
671
        replacements.insert(std::make_pair("$BuildVersionMinor", config["BuildVersionPoint"]));
672
        replacements.insert(std::make_pair("$BuildRevision", config["BuildRevision"]));
673
        for (const auto& replacement : replacements) {
674
            auto pos = injectedString.find(replacement.first);
675
            while (pos != std::string::npos) {
676
                injectedString.replace(pos, replacement.first.length(), replacement.second);
677
                pos = injectedString.find(replacement.first);
678
            }
679
        }
680
        auto parsedExpression = App::Expression::parse(nullptr, dep.condition);
681
        auto result = parsedExpression->eval();
682
        if (!boost::any_cast<bool>(result->getValueAsAny())) {
683
            return false;
684
        }
685
    }
686

687
    if (!dep.version_eq.empty()) {
688
        return _version == Meta::Version(dep.version_eq);
689
    }
690
    // Any of the others might be specified in pairs, so only return the "false" case
691

692
    if (!dep.version_lt.empty()) {
693
        if (!(_version < Meta::Version(dep.version_lt))) {
694
            return false;
695
        }
696
    }
697
    if (!dep.version_lte.empty()) {
698
        if (!(_version <= Meta::Version(dep.version_lt))) {
699
            return false;
700
        }
701
    }
702
    if (!dep.version_gt.empty()) {
703
        if (!(_version > Meta::Version(dep.version_lt))) {
704
            return false;
705
        }
706
    }
707
    if (!dep.version_gte.empty()) {
708
        if (!(_version >= Meta::Version(dep.version_lt))) {
709
            return false;
710
        }
711
    }
712
    return true;
713
}
714

715
bool Metadata::supportsCurrentFreeCAD() const
716
{
717
    static auto fcVersion = Meta::Version();
718
    if (fcVersion == Meta::Version()) {
719
        std::map<std::string, std::string>& config = App::Application::Config();
720
        std::stringstream ss;
721
        ss << config["BuildVersionMajor"] << "." << config["BuildVersionMinor"] << "."
722
           << config["BuildVersionPoint"] << "."
723
           << (config["BuildRevision"].empty() ? "0" : config["BuildRevision"]);
724
        fcVersion = Meta::Version(ss.str());
725
    }
726

727
    if (_freecadmin != Meta::Version() && _freecadmin > fcVersion) {
728
        return false;
729
    }
730
    if (_freecadmax != Meta::Version() && _freecadmax < fcVersion) {
731
        return false;
732
    }
733
    return true;
734
}
735

736
void Metadata::appendToElement(DOMElement* root) const
737
{
738
    appendSimpleXMLNode(root, "name", _name);
739
    appendSimpleXMLNode(root, "type", _type);
740
    appendSimpleXMLNode(root, "description", _description);
741
    if (_version != Meta::Version()) {
742
        // Only append version if it's not 0.0.0
743
        appendSimpleXMLNode(root, "version", _version.str());
744
    }
745

746
    if (!_date.empty()) {
747
        appendSimpleXMLNode(root, "date", _date);
748
    }
749

750
    for (const auto& maintainer : _maintainer) {
751
        auto element = appendSimpleXMLNode(root, "maintainer", maintainer.name);
752
        if (element) {
753
            addAttribute(element, "email", maintainer.email);
754
        }
755
    }
756

757
    for (const auto& license : _license) {
758
        auto element = appendSimpleXMLNode(root, "license", license.name);
759
        if (element) {
760
            addAttribute(element, "file", license.file.string());
761
        }
762
    }
763

764
    if (_freecadmin != Meta::Version()) {
765
        appendSimpleXMLNode(root, "freecadmin", _freecadmin.str());
766
    }
767

768
    if (_freecadmax != Meta::Version()) {
769
        appendSimpleXMLNode(root, "freecadmax", _freecadmax.str());
770
    }
771

772
    if (_pythonmin != Meta::Version()) {
773
        appendSimpleXMLNode(root, "pythonmin", _pythonmin.str());
774
    }
775

776
    for (const auto& url : _url) {
777
        auto element = appendSimpleXMLNode(root, "url", url.location);
778
        if (element) {
779
            std::string typeAsString("website");
780
            switch (url.type) {
781
                case Meta::UrlType::website:
782
                    typeAsString = "website";
783
                    break;
784
                case Meta::UrlType::repository:
785
                    typeAsString = "repository";
786
                    break;
787
                case Meta::UrlType::bugtracker:
788
                    typeAsString = "bugtracker";
789
                    break;
790
                case Meta::UrlType::readme:
791
                    typeAsString = "readme";
792
                    break;
793
                case Meta::UrlType::documentation:
794
                    typeAsString = "documentation";
795
                    break;
796
                case Meta::UrlType::discussion:
797
                    typeAsString = "discussion";
798
                    break;
799
            }
800
            addAttribute(element, "type", typeAsString);
801
            if (url.type == Meta::UrlType::repository) {
802
                addAttribute(element, "branch", url.branch);
803
            }
804
        }
805
    }
806

807
    for (const auto& author : _author) {
808
        auto element = appendSimpleXMLNode(root, "author", author.name);
809
        if (element) {
810
            addAttribute(element, "email", author.email);
811
        }
812
    }
813

814
    for (const auto& depend : _depend) {
815
        addDependencyNode(root, "depend", depend);
816
    }
817

818
    for (const auto& conflict : _conflict) {
819
        addDependencyNode(root, "conflict", conflict);
820
    }
821

822
    for (const auto& replace : _replace) {
823
        addDependencyNode(root, "replace", replace);
824
    }
825

826
    for (const auto& tag : _tag) {
827
        appendSimpleXMLNode(root, "tag", tag);
828
    }
829

830
    appendSimpleXMLNode(root, "icon", _icon.string());
831

832
    appendSimpleXMLNode(root, "classname", _classname);
833

834
    appendSimpleXMLNode(root, "subdirectory", _subdirectory.string());
835

836
    for (const auto& file : _file) {
837
        appendSimpleXMLNode(root, "file", file.string());
838
    }
839

840
    for (const auto& md : _genericMetadata) {
841
        auto element = appendSimpleXMLNode(root, md.first, md.second.contents);
842
        for (const auto& attr : md.second.attributes) {
843
            addAttribute(element, attr.first, attr.second);
844
        }
845
    }
846

847
    if (!_content.empty()) {
848
        auto doc = root->getOwnerDocument();
849
        DOMElement* contentRootElement = doc->createElement(XUTF8Str("content").unicodeForm());
850
        root->appendChild(contentRootElement);
851
        for (const auto& content : _content) {
852
            DOMElement* contentElement =
853
                doc->createElement(XUTF8Str(content.first.c_str()).unicodeForm());
854
            contentRootElement->appendChild(contentElement);
855
            content.second.appendToElement(contentElement);
856
        }
857
    }
858
}
859

860

861
void Metadata::parseVersion1(const DOMNode* startNode)
862
{
863
    auto children = startNode->getChildNodes();
864

865
    for (XMLSize_t i = 0; i < children->getLength(); ++i) {
866
        auto child = children->item(i);
867
        auto element = dynamic_cast<const DOMElement*>(child);
868
        if (!element) {
869
            continue;
870
        }
871

872
        auto tag = element->getNodeName();
873
        auto tagString = StrXUTF8(tag).str;
874

875
        if (tagString == "name") {
876
            _name = StrXUTF8(element->getTextContent()).str;
877
        }
878
        else if (tagString == "type") {
879
            _type = StrXUTF8(element->getTextContent()).str;
880
        }
881
        else if (tagString == "version") {
882
            _version = Meta::Version(StrXUTF8(element->getTextContent()).str);
883
        }
884
        else if (tagString == "date") {
885
            _date = StrXUTF8(element->getTextContent()).str;
886
        }
887
        else if (tagString == "description") {
888
            _description = StrXUTF8(element->getTextContent()).str;
889
        }
890
        else if (tagString == "maintainer") {
891
            _maintainer.emplace_back(element);
892
        }
893
        else if (tagString == "license") {
894
            _license.emplace_back(element);
895
        }
896
        else if (tagString == "freecadmin") {
897
            _freecadmin = Meta::Version(StrXUTF8(element->getTextContent()).str);
898
        }
899
        else if (tagString == "freecadmax") {
900
            _freecadmax = Meta::Version(StrXUTF8(element->getTextContent()).str);
901
        }
902
        else if (tagString == "pythonmin") {
903
            _pythonmin = Meta::Version(StrXUTF8(element->getTextContent()).str);
904
        }
905
        else if (tagString == "url") {
906
            _url.emplace_back(element);
907
        }
908
        else if (tagString == "author") {
909
            _author.emplace_back(element);
910
        }
911
        else if (tagString == "depend") {
912
            _depend.emplace_back(element);
913
        }
914
        else if (tagString == "conflict") {
915
            _conflict.emplace_back(element);
916
        }
917
        else if (tagString == "replace") {
918
            _replace.emplace_back(element);
919
        }
920
        else if (tagString == "tag") {
921
            _tag.emplace_back(StrXUTF8(element->getTextContent()).str);
922
        }
923
        else if (tagString == "file") {
924
            _file.emplace_back(StrXUTF8(element->getTextContent()).str);
925
        }
926
        else if (tagString == "classname") {
927
            _classname = StrXUTF8(element->getTextContent()).str;
928
        }
929
        else if (tagString == "subdirectory") {
930
            _subdirectory = StrXUTF8(element->getTextContent()).str;
931
        }
932
        else if (tagString == "icon") {
933
            _icon = fs::path(StrXUTF8(element->getTextContent()).str);
934
        }
935
        else if (tagString == "content") {
936
            parseContentNodeVersion1(element);// Recursive call
937
        }
938
        else {
939
            // If none of this node's nodeChildren have nodeChildren of their own, it is a simple element and we
940
            // can handle it as a GenericMetadata object
941
            auto nodeChildren = element->getChildNodes();
942
            bool hasGrandchildren = false;
943
            for (XMLSize_t j = 0; j < nodeChildren->getLength() && !hasGrandchildren; ++j) {
944
                if (nodeChildren->item(j)->getChildNodes()->getLength() > 0) {
945
                    hasGrandchildren = true;
946
                }
947
            }
948
            if (!hasGrandchildren) {
949
                _genericMetadata.insert(std::make_pair(tagString, Meta::GenericMetadata(element)));
950
            }
951
        }
952
    }
953
}
954

955
void Metadata::parseContentNodeVersion1(const DOMElement* contentNode)
956
{
957
    auto children = contentNode->getChildNodes();
958
    for (XMLSize_t i = 0; i < children->getLength(); ++i) {
959
        auto child = dynamic_cast<const DOMElement*>(children->item(i));
960
        if (child) {
961
            auto tag = StrXUTF8(child->getTagName()).str;
962
            _content.insert(std::make_pair(tag, Metadata(child, 1)));
963
        }
964
    }
965
}
966

967
Meta::Contact::Contact(std::string name, std::string email)
968
    : name(std::move(name)),
969
      email(std::move(email))
970
{
971
    // This has to be provided manually since we have another constructor
972
}
973

974
Meta::Contact::Contact(const XERCES_CPP_NAMESPACE::DOMElement* elem)
975
{
976
    if (!elem){
977
        return;
978
    }
979
    auto emailAttribute = elem->getAttribute(XUTF8Str("email").unicodeForm());
980
    name = StrXUTF8(elem->getTextContent()).str;
981
    email = StrXUTF8(emailAttribute).str;
982
}
983

984
bool App::Meta::Contact::operator==(const Contact& rhs) const
985
{
986
    return name == rhs.name && email == rhs.email;
987
}
988

989
Meta::License::License(std::string name, fs::path file)
990
    : name(std::move(name)),
991
      file(std::move(file))
992
{
993
    // This has to be provided manually since we have another constructor
994
}
995

996
Meta::License::License(const XERCES_CPP_NAMESPACE::DOMElement* elem)
997
{
998
    if (!elem){
999
        return;
1000
    }
1001
    auto fileAttribute = elem->getAttribute(XUTF8Str("file").unicodeForm());
1002
    if (XMLString::stringLen(fileAttribute) > 0) {
1003
        file = fs::path(StrXUTF8(fileAttribute).str);
1004
    }
1005
    name = StrXUTF8(elem->getTextContent()).str;
1006
}
1007

1008
bool App::Meta::License::operator==(const License& rhs) const
1009
{
1010
    return name == rhs.name && file == rhs.file;
1011
}
1012

1013
App::Meta::Url::Url()
1014
    : location(""),
1015
      type(App::Meta::UrlType::website)
1016
{}
1017

1018
Meta::Url::Url(std::string location, UrlType type)
1019
    : location(std::move(location)),
1020
      type(type)
1021
{
1022
    // This has to be provided manually since we have another constructor
1023
}
1024

1025
Meta::Url::Url(const XERCES_CPP_NAMESPACE::DOMElement* elem)
1026
{
1027
    if (!elem) {
1028
        return;
1029
    }
1030
    auto typeAttribute = StrXUTF8(elem->getAttribute(XUTF8Str("type").unicodeForm())).str;
1031
    if (typeAttribute.empty() || typeAttribute == "website") {
1032
        type = UrlType::website;
1033
    }
1034
    else if (typeAttribute == "bugtracker") {
1035
        type = UrlType::bugtracker;
1036
    }
1037
    else if (typeAttribute == "repository") {
1038
        type = UrlType::repository;
1039
    }
1040
    else if (typeAttribute == "readme") {
1041
        type = UrlType::readme;
1042
    }
1043
    else if (typeAttribute == "documentation") {
1044
        type = UrlType::documentation;
1045
    }
1046
    else if (typeAttribute == "discussion") {
1047
        type = UrlType::discussion;
1048
    }
1049
    else {
1050
        type = UrlType::website;
1051
    }
1052

1053
    if (type == UrlType::repository) {
1054
        branch = StrXUTF8(elem->getAttribute(XUTF8Str("branch").unicodeForm())).str;
1055
    }
1056
    location = StrXUTF8(elem->getTextContent()).str;
1057
}
1058

1059
bool App::Meta::Url::operator==(const Url& rhs) const
1060
{
1061
    if (type == UrlType::repository && branch != rhs.branch) {
1062
        return false;
1063
    }
1064
    return type == rhs.type && location == rhs.location;
1065
}
1066

1067
App::Meta::Dependency::Dependency()
1068
    : optional(false),
1069
      dependencyType(App::Meta::DependencyType::automatic)
1070
{}
1071

1072
App::Meta::Dependency::Dependency(std::string pkg)
1073
    : package(std::move(pkg)),
1074
      optional(false),
1075
      dependencyType(App::Meta::DependencyType::automatic)
1076
{}
1077

1078
Meta::Dependency::Dependency(const XERCES_CPP_NAMESPACE::DOMElement* elem)
1079
{
1080
    version_lt = StrXUTF8(elem->getAttribute(XUTF8Str("version_lt").unicodeForm())).str;
1081
    version_lte = StrXUTF8(elem->getAttribute(XUTF8Str("version_lte").unicodeForm())).str;
1082
    version_eq = StrXUTF8(elem->getAttribute(XUTF8Str("version_eq").unicodeForm())).str;
1083
    version_gte = StrXUTF8(elem->getAttribute(XUTF8Str("version_gte").unicodeForm())).str;
1084
    version_gt = StrXUTF8(elem->getAttribute(XUTF8Str("version_gt").unicodeForm())).str;
1085
    condition = StrXUTF8(elem->getAttribute(XUTF8Str("condition").unicodeForm())).str;
1086
    std::string opt_string = StrXUTF8(elem->getAttribute(XUTF8Str("optional").unicodeForm())).str;
1087
    if (opt_string == "true"
1088
        || opt_string == "True") {// Support Python capitalization in this one case...
1089
        optional = true;
1090
    }
1091
    else {
1092
        optional = false;
1093
    }
1094
    std::string type_string = StrXUTF8(elem->getAttribute(XUTF8Str("type").unicodeForm())).str;
1095
    if (type_string == "automatic" || type_string.empty()) {
1096
        dependencyType = Meta::DependencyType::automatic;
1097
    }
1098
    else if (type_string == "addon") {
1099
        dependencyType = Meta::DependencyType::addon;
1100
    }
1101
    else if (type_string == "internal") {
1102
        dependencyType = Meta::DependencyType::internal;
1103
    }
1104
    else if (type_string == "python") {
1105
        dependencyType = Meta::DependencyType::python;
1106
    }
1107
    else {
1108
        auto message = std::string("Invalid dependency type \"") + type_string + "\"";
1109
        throw Base::XMLBaseException(message);
1110
    }
1111

1112
    package = StrXUTF8(elem->getTextContent()).str;
1113
}
1114

1115
bool App::Meta::Dependency::operator==(const Dependency& rhs) const
1116
{
1117
    return package == rhs.package && version_lt == rhs.version_lt && version_lte == rhs.version_lte
1118
        && version_eq == rhs.version_eq && version_gte == rhs.version_gte
1119
        && version_gt == rhs.version_gt && condition == rhs.condition && optional == rhs.optional
1120
        && dependencyType == rhs.dependencyType;
1121
}
1122

1123
Meta::Version::Version() = default;
1124

1125
Meta::Version::Version(int major, int minor, int patch, std::string suffix)
1126
    : major(major),
1127
      minor(minor),
1128
      patch(patch),
1129
      suffix(std::move(suffix))
1130
{}
1131

1132
Meta::Version::Version(const std::string& versionString)
1133
{
1134
    std::istringstream stream(versionString);
1135
    char separator {'.'};
1136
    stream >> major;
1137
    if (stream) {
1138
        stream >> separator;
1139
    }
1140
    if (stream) {
1141
        stream >> minor;
1142
    }
1143
    if (stream) {
1144
        stream >> separator;
1145
    }
1146
    if (stream) {
1147
        stream >> patch;
1148
    }
1149
    if (stream) {
1150
        stream >> suffix;
1151
    }
1152
}
1153

1154
std::string Meta::Version::str() const
1155
{
1156
    if (*this == Meta::Version()) {
1157
        return "";
1158
    }
1159
    std::ostringstream stream;
1160
    stream << major << "." << minor << "." << patch << suffix;
1161
    return stream.str();
1162
}
1163

1164
bool Meta::Version::operator<(const Version& rhs) const
1165
{
1166
    return std::tie(major, minor, patch, suffix)
1167
        < std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1168
}
1169

1170
bool Meta::Version::operator>(const Version& rhs) const
1171
{
1172
    return std::tie(major, minor, patch, suffix)
1173
        > std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1174
}
1175

1176
bool Meta::Version::operator<=(const Version& rhs) const
1177
{
1178
    return std::tie(major, minor, patch, suffix)
1179
        <= std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1180
}
1181

1182
bool Meta::Version::operator>=(const Version& rhs) const
1183
{
1184
    return std::tie(major, minor, patch, suffix)
1185
        >= std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1186
}
1187

1188
bool Meta::Version::operator==(const Version& rhs) const
1189
{
1190
    return std::tie(major, minor, patch, suffix)
1191
        == std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1192
}
1193

1194
bool Meta::Version::operator!=(const Version& rhs) const
1195
{
1196
    return std::tie(major, minor, patch, suffix)
1197
        != std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1198
}
1199

1200
Meta::GenericMetadata::GenericMetadata(const XERCES_CPP_NAMESPACE::DOMElement* elem)
1201
{
1202
    contents = StrXUTF8(elem->getTextContent()).str;
1203
    for (XMLSize_t i = 0; i < elem->getAttributes()->getLength(); ++i) {
1204
        auto attr = elem->getAttributes()->item(i);
1205
        attributes.insert(
1206
            std::make_pair(StrXUTF8(attr->getNodeName()).str, StrXUTF8(attr->getTextContent()).str));
1207
    }
1208
}
1209

1210
App::Meta::GenericMetadata::GenericMetadata(std::string contents)
1211
    : contents(std::move(contents))
1212
{}
1213

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.