FreeCAD

Форк
0
/
Metadata.cpp 
1217 строк · 35.5 Кб
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
#ifndef XERCES_CPP_NAMESPACE_BEGIN
62
#define XERCES_CPP_NAMESPACE_QUALIFIER
63
using namespace XERCES_CPP_NAMESPACE;
64
#else
65
XERCES_CPP_NAMESPACE_USE
66
#endif
67

68
namespace MetadataInternal
69
{
70
class XMLErrorHandler: public HandlerBase
71
{
72
    void warning(const SAXParseException& toCatch) override
73
    {
74
        // Don't deal with warnings at all
75
        boost::ignore_unused(toCatch);
76
    }
77

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

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

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

110
Metadata::Metadata()
111
    : _dom(nullptr)
112
{}
113

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

130
App::Metadata::Metadata(const std::string& rawData)
131
    : _dom(nullptr)
132
{
133
    MemBufInputSource buffer(
134
        reinterpret_cast<const XMLByte*>(rawData.c_str()),
135
        rawData.size(),
136
        "raw data (in memory)");
137
    loadFromInputSource(buffer);
138
}
139

140
void Metadata::loadFromInputSource(const InputSource& source)
141
{
142
    // Any exception thrown by the XML code propagates out and prevents object creation
143
    XMLPlatformUtils::Initialize();
144

145
    _parser = std::make_shared<XercesDOMParser>();
146
    _parser->setValidationScheme(XercesDOMParser::Val_Never);
147
    _parser->setDoNamespaces(true);
148

149
    auto errHandler = std::make_unique<MetadataInternal::XMLErrorHandler>();
150
    _parser->setErrorHandler(errHandler.get());
151

152
    _parser->parse(source);
153

154
    auto doc = _parser->getDocument();
155
    _dom = doc->getDocumentElement();
156

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

173
Metadata::~Metadata() = default;
174

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

180
std::string Metadata::type() const
181
{
182
    return _type;
183
}
184

185
Meta::Version Metadata::version() const
186
{
187
    return _version;
188
}
189

190
std::string App::Metadata::date() const
191
{
192
    return _date;
193
}
194

195
std::string Metadata::description() const
196
{
197
    return _description;
198
}
199

200
std::vector<Meta::Contact> Metadata::maintainer() const
201
{
202
    return _maintainer;
203
}
204

205
std::vector<Meta::License> Metadata::license() const
206
{
207
    return _license;
208
}
209

210
std::vector<Meta::Url> Metadata::url() const
211
{
212
    return _url;
213
}
214

215
std::vector<Meta::Contact> Metadata::author() const
216
{
217
    return _author;
218
}
219

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

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

230
std::vector<Meta::Dependency> Metadata::replace() const
231
{
232
    return _replace;
233
}
234

235
std::vector<std::string> Metadata::tag() const
236
{
237
    return _tag;
238
}
239

240
fs::path Metadata::icon() const
241
{
242
    return _icon;
243
}
244

245
std::string Metadata::classname() const
246
{
247
    return _classname;
248
}
249

250
boost::filesystem::path Metadata::subdirectory() const
251
{
252
    return _subdirectory;
253
}
254

255
std::vector<fs::path> Metadata::file() const
256
{
257
    return _file;
258
}
259

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

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

270
Meta::Version Metadata::pythonmin() const
271
{
272
    return _pythonmin;
273
}
274

275
std::multimap<std::string, Metadata> Metadata::content() const
276
{
277
    return _content;
278
}
279

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

290
XERCES_CPP_NAMESPACE::DOMElement* Metadata::dom() const
291
{
292
    return _dom;
293
}
294

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

304
void Metadata::setType(const std::string& type)
305
{
306
    _type = type;
307
}
308

309
void Metadata::setVersion(const Meta::Version& version)
310
{
311
    _version = version;
312
}
313

314
void App::Metadata::setDate(const std::string& date)
315
{
316
    _date = date;
317
}
318

319
void Metadata::setDescription(const std::string& description)
320
{
321
    _description = description;
322
}
323

324
void Metadata::addMaintainer(const Meta::Contact& maintainer)
325
{
326
    _maintainer.push_back(maintainer);
327
}
328

329
void Metadata::addLicense(const Meta::License& license)
330
{
331
    _license.push_back(license);
332
}
333

334
void Metadata::addUrl(const Meta::Url& url)
335
{
336
    _url.push_back(url);
337
}
338

339
void Metadata::addAuthor(const Meta::Contact& author)
340
{
341
    _author.push_back(author);
342
}
343

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

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

354
void Metadata::addReplace(const Meta::Dependency& dep)
355
{
356
    _replace.push_back(dep);
357
}
358

359
void Metadata::addTag(const std::string& tag)
360
{
361
    _tag.push_back(tag);
362
}
363

364
void Metadata::setIcon(const fs::path& path)
365
{
366
    _icon = path;
367
}
368

369
void Metadata::setClassname(const std::string& name)
370
{
371
    _classname = name;
372
}
373

374
void Metadata::setSubdirectory(const boost::filesystem::path& path)
375
{
376
    _subdirectory = path;
377
}
378

379
void Metadata::addFile(const fs::path& path)
380
{
381
    _file.push_back(path);
382
}
383

384
void Metadata::addContentItem(const std::string& tag, const Metadata& item)
385
{
386
    _content.insert(std::make_pair(tag, item));
387
}
388

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

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

399
void Metadata::setFreeCADMax(const Meta::Version& version)
400
{
401
    _freecadmax = version;
402
}
403

404
void Metadata::addGenericMetadata(const std::string& tag,
405
                                  const Meta::GenericMetadata& genericMetadata)
406
{
407
    _genericMetadata.insert(std::make_pair(tag, genericMetadata));
408
}
409

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

422
void Metadata::removeMaintainer(const Meta::Contact& maintainer)
423
{
424
    auto new_end = std::remove(_maintainer.begin(), _maintainer.end(), maintainer);
425
    _maintainer.erase(new_end, _maintainer.end());
426
}
427

428
void Metadata::removeLicense(const Meta::License& license)
429
{
430
    auto new_end = std::remove(_license.begin(), _license.end(), license);
431
    _license.erase(new_end, _license.end());
432
}
433

434
void Metadata::removeUrl(const Meta::Url& url)
435
{
436
    auto new_end = std::remove(_url.begin(), _url.end(), url);
437
    _url.erase(new_end, _url.end());
438
}
439

440
void Metadata::removeAuthor(const Meta::Contact& author)
441
{
442
    auto new_end = std::remove(_author.begin(), _author.end(), author);
443
    _author.erase(new_end, _author.end());
444
}
445

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

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

467
void Metadata::removeReplace(const Meta::Dependency& dep)
468
{
469
    auto new_end = std::remove(_replace.begin(), _replace.end(), dep);
470
    _replace.erase(new_end, _replace.end());
471
}
472

473
void Metadata::removeTag(const std::string& tag)
474
{
475
    auto new_end = std::remove(_tag.begin(), _tag.end(), tag);
476
    _tag.erase(new_end, _tag.end());
477
}
478

479
void Metadata::removeFile(const boost::filesystem::path& path)
480
{
481
    auto new_end = std::remove(_file.begin(), _file.end(), path);
482
    _file.erase(new_end, _file.end());
483
}
484

485

486
void Metadata::clearContent()
487
{
488
    _content.clear();
489
}
490

491
void Metadata::clearMaintainer()
492
{
493
    _maintainer.clear();
494
}
495

496
void Metadata::clearLicense()
497
{
498
    _license.clear();
499
}
500

501
void Metadata::clearUrl()
502
{
503
    _url.clear();
504
}
505

506
void Metadata::clearAuthor()
507
{
508
    _author.clear();
509
}
510

511
void Metadata::clearDepend()
512
{
513
    _depend.clear();
514
}
515

516
void Metadata::clearConflict()
517
{
518
    _conflict.clear();
519
}
520

521
void Metadata::clearReplace()
522
{
523
    _replace.clear();
524
}
525

526
void Metadata::clearTag()
527
{
528
    _tag.clear();
529
}
530

531
void Metadata::clearFile()
532
{
533
    _file.clear();
534
}
535

536

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

552
void addAttribute(DOMElement* node, const std::string& key, const std::string& value)
553
{
554
    if (value.empty()) {
555
        return;
556
    }
557
    node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str(value.c_str()).unicodeForm());
558
}
559

560
void addAttribute(DOMElement* node, const std::string& key, bool value)
561
{
562
    if (value) {
563
        node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str("True").unicodeForm());
564
    }
565
    else {
566
        node->setAttribute(XUTF8Str(key.c_str()).unicodeForm(), XUTF8Str("False").unicodeForm());
567
    }
568
}
569

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

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

608
void Metadata::write(const fs::path& file) const
609
{
610
    DOMImplementation* impl =
611
        DOMImplementationRegistry::getDOMImplementation(XUTF8Str("Core LS").unicodeForm());
612

613
    DOMDocument* doc = impl->createDocument(nullptr, XUTF8Str("package").unicodeForm(), nullptr);
614
    DOMElement* root = doc->getDocumentElement();
615
    root->setAttribute(XUTF8Str("format").unicodeForm(), XUTF8Str("1").unicodeForm());
616
    root->setAttribute(XUTF8Str("xmlns").unicodeForm(),
617
                       XUTF8Str("https://wiki.freecad.org/Package_Metadata").unicodeForm());
618

619
    appendToElement(root);
620

621
    DOMLSSerializer* theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer();
622
    DOMConfiguration* config = theSerializer->getDomConfig();
623
    if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true)) {
624
        config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
625
    }
626

627
    // set feature if the serializer supports the feature/mode
628
    if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, true)) {
629
        config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, true);
630
    }
631

632
    if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true)) {
633
        config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true);
634
    }
635

636
    try {
637
        XMLFormatTarget* myFormTarget = new LocalFileFormatTarget(file.string().c_str());
638
        DOMLSOutput* theOutput = ((DOMImplementationLS*)impl)->createLSOutput();
639

640
        theOutput->setByteStream(myFormTarget);
641
        theSerializer->write(doc, theOutput);
642

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

660
    doc->release();
661
}
662

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

692
    if (!dep.version_eq.empty()) {
693
        return _version == Meta::Version(dep.version_eq);
694
    }
695
    // Any of the others might be specified in pairs, so only return the "false" case
696

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

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

732
    if (_freecadmin != Meta::Version() && _freecadmin > fcVersion) {
733
        return false;
734
    }
735
    if (_freecadmax != Meta::Version() && _freecadmax < fcVersion) {
736
        return false;
737
    }
738
    return true;
739
}
740

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

751
    if (!_date.empty()) {
752
        appendSimpleXMLNode(root, "date", _date);
753
    }
754

755
    for (const auto& maintainer : _maintainer) {
756
        auto element = appendSimpleXMLNode(root, "maintainer", maintainer.name);
757
        if (element) {
758
            addAttribute(element, "email", maintainer.email);
759
        }
760
    }
761

762
    for (const auto& license : _license) {
763
        auto element = appendSimpleXMLNode(root, "license", license.name);
764
        if (element) {
765
            addAttribute(element, "file", license.file.string());
766
        }
767
    }
768

769
    if (_freecadmin != Meta::Version()) {
770
        appendSimpleXMLNode(root, "freecadmin", _freecadmin.str());
771
    }
772

773
    if (_freecadmax != Meta::Version()) {
774
        appendSimpleXMLNode(root, "freecadmax", _freecadmax.str());
775
    }
776

777
    if (_pythonmin != Meta::Version()) {
778
        appendSimpleXMLNode(root, "pythonmin", _pythonmin.str());
779
    }
780

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

812
    for (const auto& author : _author) {
813
        auto element = appendSimpleXMLNode(root, "author", author.name);
814
        if (element) {
815
            addAttribute(element, "email", author.email);
816
        }
817
    }
818

819
    for (const auto& depend : _depend) {
820
        addDependencyNode(root, "depend", depend);
821
    }
822

823
    for (const auto& conflict : _conflict) {
824
        addDependencyNode(root, "conflict", conflict);
825
    }
826

827
    for (const auto& replace : _replace) {
828
        addDependencyNode(root, "replace", replace);
829
    }
830

831
    for (const auto& tag : _tag) {
832
        appendSimpleXMLNode(root, "tag", tag);
833
    }
834

835
    appendSimpleXMLNode(root, "icon", _icon.string());
836

837
    appendSimpleXMLNode(root, "classname", _classname);
838

839
    appendSimpleXMLNode(root, "subdirectory", _subdirectory.string());
840

841
    for (const auto& file : _file) {
842
        appendSimpleXMLNode(root, "file", file.string());
843
    }
844

845
    for (const auto& md : _genericMetadata) {
846
        auto element = appendSimpleXMLNode(root, md.first, md.second.contents);
847
        for (const auto& attr : md.second.attributes) {
848
            addAttribute(element, attr.first, attr.second);
849
        }
850
    }
851

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

865

866
void Metadata::parseVersion1(const DOMNode* startNode)
867
{
868
    auto children = startNode->getChildNodes();
869

870
    for (XMLSize_t i = 0; i < children->getLength(); ++i) {
871
        auto child = children->item(i);
872
        auto element = dynamic_cast<const DOMElement*>(child);
873
        if (!element) {
874
            continue;
875
        }
876

877
        auto tag = element->getNodeName();
878
        auto tagString = StrXUTF8(tag).str;
879

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

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

972
Meta::Contact::Contact(std::string name, std::string email)
973
    : name(std::move(name)),
974
      email(std::move(email))
975
{
976
    // This has to be provided manually since we have another constructor
977
}
978

979
Meta::Contact::Contact(const XERCES_CPP_NAMESPACE::DOMElement* elem)
980
{
981
    if (!elem){
982
        return;
983
    }
984
    auto emailAttribute = elem->getAttribute(XUTF8Str("email").unicodeForm());
985
    name = StrXUTF8(elem->getTextContent()).str;
986
    email = StrXUTF8(emailAttribute).str;
987
}
988

989
bool App::Meta::Contact::operator==(const Contact& rhs) const
990
{
991
    return name == rhs.name && email == rhs.email;
992
}
993

994
Meta::License::License(std::string name, fs::path file)
995
    : name(std::move(name)),
996
      file(std::move(file))
997
{
998
    // This has to be provided manually since we have another constructor
999
}
1000

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

1013
bool App::Meta::License::operator==(const License& rhs) const
1014
{
1015
    return name == rhs.name && file == rhs.file;
1016
}
1017

1018
App::Meta::Url::Url()
1019
    : location(""),
1020
      type(App::Meta::UrlType::website)
1021
{}
1022

1023
Meta::Url::Url(std::string location, UrlType type)
1024
    : location(std::move(location)),
1025
      type(type)
1026
{
1027
    // This has to be provided manually since we have another constructor
1028
}
1029

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

1058
    if (type == UrlType::repository) {
1059
        branch = StrXUTF8(elem->getAttribute(XUTF8Str("branch").unicodeForm())).str;
1060
    }
1061
    location = StrXUTF8(elem->getTextContent()).str;
1062
}
1063

1064
bool App::Meta::Url::operator==(const Url& rhs) const
1065
{
1066
    if (type == UrlType::repository && branch != rhs.branch) {
1067
        return false;
1068
    }
1069
    return type == rhs.type && location == rhs.location;
1070
}
1071

1072
App::Meta::Dependency::Dependency()
1073
    : optional(false),
1074
      dependencyType(App::Meta::DependencyType::automatic)
1075
{}
1076

1077
App::Meta::Dependency::Dependency(std::string pkg)
1078
    : package(std::move(pkg)),
1079
      optional(false),
1080
      dependencyType(App::Meta::DependencyType::automatic)
1081
{}
1082

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

1117
    package = StrXUTF8(elem->getTextContent()).str;
1118
}
1119

1120
bool App::Meta::Dependency::operator==(const Dependency& rhs) const
1121
{
1122
    return package == rhs.package && version_lt == rhs.version_lt && version_lte == rhs.version_lte
1123
        && version_eq == rhs.version_eq && version_gte == rhs.version_gte
1124
        && version_gt == rhs.version_gt && condition == rhs.condition && optional == rhs.optional
1125
        && dependencyType == rhs.dependencyType;
1126
}
1127

1128
Meta::Version::Version() = default;
1129

1130
Meta::Version::Version(int major, int minor, int patch, std::string suffix)
1131
    : major(major),
1132
      minor(minor),
1133
      patch(patch),
1134
      suffix(std::move(suffix))
1135
{}
1136

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

1159
std::string Meta::Version::str() const
1160
{
1161
    if (*this == Meta::Version()) {
1162
        return "";
1163
    }
1164
    std::ostringstream stream;
1165
    stream << major << "." << minor << "." << patch << suffix;
1166
    return stream.str();
1167
}
1168

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

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

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

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

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

1199
bool Meta::Version::operator!=(const Version& rhs) const
1200
{
1201
    return std::tie(major, minor, patch, suffix)
1202
        != std::tie(rhs.major, rhs.minor, rhs.patch, rhs.suffix);
1203
}
1204

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

1215
App::Meta::GenericMetadata::GenericMetadata(std::string contents)
1216
    : contents(std::move(contents))
1217
{}
1218

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

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

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

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