FreeCAD

Форк
0
/
Parameter.cpp 
2144 строки · 65.9 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This program is free software; you can redistribute it and/or modify  *
7
 *   it under the terms of the GNU Library General Public License (LGPL)   *
8
 *   as published by the Free Software Foundation; either version 2 of     *
9
 *   the License, or (at your option) any later version.                   *
10
 *   for detail see the LICENCE text file.                                 *
11
 *                                                                         *
12
 *   FreeCAD is distributed in the hope that it will be useful,            *
13
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
 *   GNU Library General Public License for more details.                  *
16
 *                                                                         *
17
 *   You should have received a copy of the GNU Library General Public     *
18
 *   License along with FreeCAD; if not, write to the Free Software        *
19
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
20
 *   USA                                                                   *
21
 *                                                                         *
22
 ***************************************************************************/
23

24

25
#include "PreCompiled.h"
26

27
#ifndef _PreComp_
28
#include <algorithm>
29
#include <cassert>
30
#include <memory>
31
#include <xercesc/dom/DOM.hpp>
32
#include <xercesc/framework/LocalFileFormatTarget.hpp>
33
#include <xercesc/framework/LocalFileInputSource.hpp>
34
#include <xercesc/framework/MemBufFormatTarget.hpp>
35
#include <xercesc/framework/MemBufInputSource.hpp>
36
#include <xercesc/parsers/XercesDOMParser.hpp>
37
#include <xercesc/sax/ErrorHandler.hpp>
38
#include <xercesc/sax/SAXParseException.hpp>
39
#include <sstream>
40
#include <string>
41
#include <utility>
42
#endif
43

44
#include <QFileInfo>
45
#include <QLockFile>
46
#include <QDir>
47

48
#ifdef FC_OS_LINUX
49
#include <unistd.h>
50
#endif
51

52
#include <boost/algorithm/string.hpp>
53
#include <fmt/printf.h>
54

55
#include "Parameter.h"
56
#include "Parameter.inl"
57
#include "Console.h"
58
#include "Exception.h"
59
#include "Tools.h"
60

61
FC_LOG_LEVEL_INIT("Parameter", true, true)
62

63
#ifndef XERCES_CPP_NAMESPACE_BEGIN
64
#define XERCES_CPP_NAMESPACE_QUALIFIER
65
using namespace XERCES_CPP_NAMESPACE;
66
#else
67
XERCES_CPP_NAMESPACE_USE
68
#endif
69
using namespace Base;
70

71

72
#include "XMLTools.h"
73

74
//**************************************************************************
75
//**************************************************************************
76
// private classes declaration:
77
// - DOMTreeErrorReporter
78
// - StrX
79
// - DOMPrintFilter
80
// - DOMPrintErrorHandler
81
// - XStr
82
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
83

84
class DOMTreeErrorReporter: public ErrorHandler
85
{
86
public:
87
    // -----------------------------------------------------------------------
88
    //  Implementation of the error handler interface
89
    // -----------------------------------------------------------------------
90
    void warning(const SAXParseException& toCatch) override;
91
    void error(const SAXParseException& toCatch) override;
92
    void fatalError(const SAXParseException& toCatch) override;
93
    void resetErrors() override;
94

95
    // -----------------------------------------------------------------------
96
    //  Getter methods
97
    // -----------------------------------------------------------------------
98
    bool getSawErrors() const;
99

100
    // -----------------------------------------------------------------------
101
    //  Private data members
102
    //
103
    //  fSawErrors
104
    //      This is set if we get any errors, and is queryable via a getter
105
    //      method. Its used by the main code to suppress output if there are
106
    //      errors.
107
    // -----------------------------------------------------------------------
108
    bool fSawErrors {false};
109
};
110

111

112
class DOMPrintFilter: public DOMLSSerializerFilter
113
{
114
public:
115
    /** @name Constructors */
116
    explicit DOMPrintFilter(ShowType whatToShow = DOMNodeFilter::SHOW_ALL);
117
    //@{
118

119
    /** @name Destructors */
120
    ~DOMPrintFilter() override = default;
121
    //@{
122

123
    /** @ interface from DOMWriterFilter */
124
    FilterAction acceptNode(const XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node) const override;
125
    //@{
126

127
    ShowType getWhatToShow() const override
128
    {
129
        return fWhatToShow;
130
    }
131

132
    // unimplemented copy ctor and assignment operator
133
    DOMPrintFilter(const DOMPrintFilter&) = delete;
134
    DOMPrintFilter(DOMPrintFilter&&) = delete;
135
    DOMPrintFilter& operator=(const DOMPrintFilter&) = delete;
136
    DOMPrintFilter& operator=(DOMPrintFilter&&) = delete;
137

138
    ShowType fWhatToShow;
139
};
140

141
class DOMPrintErrorHandler: public DOMErrorHandler
142
{
143
public:
144
    DOMPrintErrorHandler() = default;
145
    ~DOMPrintErrorHandler() override = default;
146

147
    /** @name The error handler interface */
148
    bool handleError(const DOMError& domError) override;
149
    void resetErrors()
150
    {}
151

152
    /* Unimplemented constructors and operators */
153
    DOMPrintErrorHandler(const DOMPrintErrorHandler&) = delete;
154
    DOMPrintErrorHandler(DOMPrintErrorHandler&&) = delete;
155
    void operator=(const DOMPrintErrorHandler&) = delete;
156
    void operator=(DOMPrintErrorHandler&&) = delete;
157
};
158

159

160
inline bool DOMTreeErrorReporter::getSawErrors() const
161
{
162
    return fSawErrors;
163
}
164

165

166
//**************************************************************************
167
//**************************************************************************
168
// ParameterManager
169
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
170

171

172
//**************************************************************************
173
// Construction/Destruction
174

175

176
/** Default construction
177
 */
178
ParameterGrp::ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* GroupNode,
179
                           const char* sName,
180
                           ParameterGrp* Parent)
181
    : _pGroupNode(GroupNode)
182
    , _Parent(Parent)
183
{
184
    if (sName) {
185
        _cName = sName;
186
    }
187
    if (_Parent) {
188
        _Manager = _Parent->_Manager;
189
    }
190
}
191

192

193
/** Destruction
194
 * complete destruction of the object
195
 */
196
ParameterGrp::~ParameterGrp()
197
{
198
    for (auto& v : _GroupMap) {
199
        v.second->_Parent = nullptr;
200
        v.second->_Manager = nullptr;
201
    }
202
    if (_Detached && _pGroupNode) {
203
        _pGroupNode->release();
204
    }
205
}
206

207
//**************************************************************************
208
// Access methods
209

210
void ParameterGrp::copyTo(const Base::Reference<ParameterGrp>& Grp)
211
{
212
    if (Grp == this) {
213
        return;
214
    }
215

216
    // delete previous content
217
    Grp->Clear(true);
218

219
    // copy all
220
    insertTo(Grp);
221
}
222

223
void ParameterGrp::insertTo(const Base::Reference<ParameterGrp>& Grp)
224
{
225
    if (Grp == this) {
226
        return;
227
    }
228

229
    // copy group
230
    std::vector<Base::Reference<ParameterGrp>> Grps = GetGroups();
231
    std::vector<Base::Reference<ParameterGrp>>::iterator It1;
232
    for (It1 = Grps.begin(); It1 != Grps.end(); ++It1) {
233
        (*It1)->insertTo(Grp->GetGroup((*It1)->GetGroupName()));
234
    }
235

236
    // copy strings
237
    std::vector<std::pair<std::string, std::string>> StringMap = GetASCIIMap();
238
    std::vector<std::pair<std::string, std::string>>::iterator It2;
239
    for (It2 = StringMap.begin(); It2 != StringMap.end(); ++It2) {
240
        Grp->SetASCII(It2->first.c_str(), It2->second.c_str());
241
    }
242

243
    // copy bool
244
    std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
245
    std::vector<std::pair<std::string, bool>>::iterator It3;
246
    for (It3 = BoolMap.begin(); It3 != BoolMap.end(); ++It3) {
247
        Grp->SetBool(It3->first.c_str(), It3->second);
248
    }
249

250
    // copy int
251
    std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
252
    std::vector<std::pair<std::string, long>>::iterator It4;
253
    for (It4 = IntMap.begin(); It4 != IntMap.end(); ++It4) {
254
        Grp->SetInt(It4->first.c_str(), It4->second);
255
    }
256

257
    // copy float
258
    std::vector<std::pair<std::string, double>> FloatMap = GetFloatMap();
259
    std::vector<std::pair<std::string, double>>::iterator It5;
260
    for (It5 = FloatMap.begin(); It5 != FloatMap.end(); ++It5) {
261
        Grp->SetFloat(It5->first.c_str(), It5->second);
262
    }
263

264
    // copy uint
265
    std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
266
    std::vector<std::pair<std::string, unsigned long>>::iterator It6;
267
    for (It6 = UIntMap.begin(); It6 != UIntMap.end(); ++It6) {
268
        Grp->SetUnsigned(It6->first.c_str(), It6->second);
269
    }
270
}
271

272
void ParameterGrp::exportTo(const char* FileName)
273
{
274
    auto Mngr = ParameterManager::Create();
275

276
    Mngr->CreateDocument();
277

278
    // copy all into the new document
279
    insertTo(Base::Reference<ParameterGrp>(Mngr));
280

281
    Mngr->SaveDocument(FileName);
282
}
283

284
void ParameterGrp::importFrom(const char* FileName)
285
{
286
    auto Mngr = ParameterManager::Create();
287

288
    if (Mngr->LoadDocument(FileName) != 1) {
289
        throw FileException("ParameterGrp::import() cannot load document", FileName);
290
    }
291

292
    Mngr->copyTo(Base::Reference<ParameterGrp>(this));
293
}
294

295
void ParameterGrp::insert(const char* FileName)
296
{
297
    auto Mngr = ParameterManager::Create();
298

299
    if (Mngr->LoadDocument(FileName) != 1) {
300
        throw FileException("ParameterGrp::import() cannot load document", FileName);
301
    }
302

303
    Mngr->insertTo(Base::Reference<ParameterGrp>(this));
304
}
305

306
void ParameterGrp::revert(const char* FileName)
307
{
308
    auto Mngr = ParameterManager::Create();
309

310
    if (Mngr->LoadDocument(FileName) != 1) {
311
        throw FileException("ParameterGrp::revert() cannot load document", FileName);
312
    }
313

314
    revert(Base::Reference<ParameterGrp>(Mngr));
315
}
316

317
void ParameterGrp::revert(const Base::Reference<ParameterGrp>& Grp)
318
{
319
    if (Grp == this) {
320
        return;
321
    }
322

323
    for (auto& grp : Grp->GetGroups()) {
324
        if (HasGroup(grp->GetGroupName())) {
325
            GetGroup(grp->GetGroupName())->revert(grp);
326
        }
327
    }
328

329
    for (const auto& v : Grp->GetASCIIMap()) {
330
        if (GetASCII(v.first.c_str(), v.second.c_str()) == v.second) {
331
            RemoveASCII(v.first.c_str());
332
        }
333
    }
334

335
    for (const auto& v : Grp->GetBoolMap()) {
336
        if (GetBool(v.first.c_str(), v.second) == v.second) {
337
            RemoveBool(v.first.c_str());
338
        }
339
    }
340

341
    for (const auto& v : Grp->GetIntMap()) {
342
        if (GetInt(v.first.c_str(), v.second) == v.second) {
343
            RemoveInt(v.first.c_str());
344
        }
345
    }
346

347
    for (const auto& v : Grp->GetUnsignedMap()) {
348
        if (GetUnsigned(v.first.c_str(), v.second) == v.second) {
349
            RemoveUnsigned(v.first.c_str());
350
        }
351
    }
352

353
    for (const auto& v : Grp->GetFloatMap()) {
354
        if (GetFloat(v.first.c_str(), v.second) == v.second) {
355
            RemoveFloat(v.first.c_str());
356
        }
357
    }
358
}
359

360
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
361
ParameterGrp::CreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
362
                            const char* Type,
363
                            const char* Name)
364
{
365
    if (XMLString::compareString(Start->getNodeName(), XStr("FCParamGroup").unicodeForm()) != 0
366
        && XMLString::compareString(Start->getNodeName(), XStr("FCParameters").unicodeForm())
367
            != 0) {
368
        Base::Console().Warning("CreateElement: %s cannot have the element %s of type %s\n",
369
                                StrX(Start->getNodeName()).c_str(),
370
                                Name,
371
                                Type);
372
        return nullptr;
373
    }
374

375
    if (_Detached && _Parent) {
376
        // re-attach the group
377
        _Parent->_GetGroup(_cName.c_str());
378
    }
379

380
    XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* pDocument = Start->getOwnerDocument();
381

382
    auto pcElem = pDocument->createElement(XStr(Type).unicodeForm());
383
    pcElem->setAttribute(XStr("Name").unicodeForm(), XStr(Name).unicodeForm());
384
    Start->appendChild(pcElem);
385

386
    return pcElem;
387
}
388

389
Base::Reference<ParameterGrp> ParameterGrp::GetGroup(const char* Name)
390
{
391
    if (!Name) {
392
        throw Base::ValueError("Empty group name");
393
    }
394

395
    Base::Reference<ParameterGrp> hGrp = this;
396
    std::vector<std::string> tokens;
397
    boost::split(tokens, Name, boost::is_any_of("/"));
398
    for (auto& token : tokens) {
399
        boost::trim(token);
400
        if (token.empty()) {
401
            continue;
402
        }
403
        hGrp = hGrp->_GetGroup(token.c_str());
404
        if (!hGrp) {
405
            // The group is clearing. Return some dummy group to avoid caller
406
            // crashing for backward compatibility.
407
            hGrp = new ParameterGrp();
408
            hGrp->_cName = Name;
409
            break;
410
        }
411
    }
412
    if (hGrp == this) {
413
        throw Base::ValueError("Empty group name");
414
    }
415
    return hGrp;
416
}
417

418
Base::Reference<ParameterGrp> ParameterGrp::_GetGroup(const char* Name)
419
{
420
    Base::Reference<ParameterGrp> rParamGrp;
421
    if (!_pGroupNode) {
422
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
423
            FC_WARN("Adding group " << Name << " in an orphan group " << _cName);
424
        }
425
        return rParamGrp;
426
    }
427
    if (_Clearing) {
428
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
429
            FC_WARN("Adding group " << Name << " while clearing " << GetPath());
430
        }
431
        return rParamGrp;
432
    }
433

434
    DOMElement* pcTemp {};
435

436
    // search if Group node already there
437
    pcTemp = FindElement(_pGroupNode, "FCParamGroup", Name);
438

439
    // already created?
440
    if (!(rParamGrp = _GroupMap[Name]).isValid()) {
441
        if (!pcTemp) {
442
            pcTemp = CreateElement(_pGroupNode, "FCParamGroup", Name);
443
        }
444
        // create and register handle
445
        rParamGrp = Base::Reference<ParameterGrp>(new ParameterGrp(pcTemp, Name, this));
446
        _GroupMap[Name] = rParamGrp;
447
    }
448
    else if (!pcTemp) {
449
        _pGroupNode->appendChild(rParamGrp->_pGroupNode);
450
        rParamGrp->_Detached = false;
451
        if (this->_Detached && this->_Parent) {
452
            // Re-attach the group. Note that this may fail if the parent is
453
            // clearing. That's why we check this->_Detached below.
454
            this->_Parent->_GetGroup(_cName.c_str());
455
        }
456
    }
457

458
    if (!pcTemp && !this->_Detached) {
459
        _Notify(ParamType::FCGroup, Name, Name);
460
    }
461

462
    return rParamGrp;
463
}
464

465
std::string ParameterGrp::GetPath() const
466
{
467
    std::string path;
468
    if (_Parent && _Parent != _Manager) {
469
        path = _Parent->GetPath();
470
    }
471
    if (!path.empty() && !_cName.empty()) {
472
        path += "/";
473
    }
474
    path += _cName;
475
    return path;
476
}
477

478
std::vector<Base::Reference<ParameterGrp>> ParameterGrp::GetGroups()
479
{
480
    Base::Reference<ParameterGrp> rParamGrp;
481
    std::vector<Base::Reference<ParameterGrp>> vrParamGrp;
482

483
    if (!_pGroupNode) {
484
        return vrParamGrp;
485
    }
486

487
    std::string Name;
488

489
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCParamGroup");
490
    while (pcTemp) {
491
        Name =
492
            StrX(pcTemp->getAttributes()->getNamedItem(XStr("Name").unicodeForm())->getNodeValue())
493
                .c_str();
494
        // already created?
495
        if (!(rParamGrp = _GroupMap[Name]).isValid()) {
496
            rParamGrp = Base::Reference<ParameterGrp>(new ParameterGrp(pcTemp, Name.c_str(), this));
497
            _GroupMap[Name] = rParamGrp;
498
        }
499
        vrParamGrp.push_back(rParamGrp);
500
        // go to next
501
        pcTemp = FindNextElement(pcTemp, "FCParamGroup");
502
    }
503

504
    return vrParamGrp;
505
}
506

507
/// test if this group is empty
508
bool ParameterGrp::IsEmpty() const
509
{
510
    return !(_pGroupNode && _pGroupNode->getFirstChild());
511
}
512

513
/// test if a special sub group is in this group
514
bool ParameterGrp::HasGroup(const char* Name) const
515
{
516
    if (_GroupMap.find(Name) != _GroupMap.end()) {
517
        return true;
518
    }
519

520
    if (_pGroupNode && FindElement(_pGroupNode, "FCParamGroup", Name) != nullptr) {
521
        return true;
522
    }
523

524
    return false;
525
}
526

527
const char* ParameterGrp::TypeName(ParamType Type)
528
{
529
    switch (Type) {
530
        case ParamType::FCBool:
531
            return "FCBool";
532
        case ParamType::FCInt:
533
            return "FCInt";
534
        case ParamType::FCUInt:
535
            return "FCUInt";
536
        case ParamType::FCText:
537
            return "FCText";
538
        case ParamType::FCFloat:
539
            return "FCFloat";
540
        case ParamType::FCGroup:
541
            return "FCParamGroup";
542
        default:
543
            return nullptr;
544
    }
545
}
546

547
ParameterGrp::ParamType ParameterGrp::TypeValue(const char* Name)
548
{
549
    if (Name) {
550
        if (boost::equals(Name, "FCBool")) {
551
            return ParamType::FCBool;
552
        }
553
        if (boost::equals(Name, "FCInt")) {
554
            return ParamType::FCInt;
555
        }
556
        if (boost::equals(Name, "FCUInt")) {
557
            return ParamType::FCUInt;
558
        }
559
        if (boost::equals(Name, "FCText")) {
560
            return ParamType::FCText;
561
        }
562
        if (boost::equals(Name, "FCFloat")) {
563
            return ParamType::FCFloat;
564
        }
565
        if (boost::equals(Name, "FCParamGroup")) {
566
            return ParamType::FCGroup;
567
        }
568
    }
569
    return ParamType::FCInvalid;
570
}
571

572
void ParameterGrp::SetAttribute(ParamType Type, const char* Name, const char* Value)
573
{
574
    switch (Type) {
575
        case ParamType::FCBool:
576
        case ParamType::FCInt:
577
        case ParamType::FCUInt:
578
        case ParamType::FCFloat:
579
            return _SetAttribute(Type, Name, Value);
580
        case ParamType::FCText:
581
            return SetASCII(Name, Value);
582
        case ParamType::FCGroup:
583
            RenameGrp(Name, Value);
584
            break;
585
        default:
586
            break;
587
    }
588
}
589

590
void ParameterGrp::RemoveAttribute(ParamType Type, const char* Name)
591
{
592
    switch (Type) {
593
        case ParamType::FCBool:
594
            return RemoveBool(Name);
595
        case ParamType::FCInt:
596
            return RemoveInt(Name);
597
        case ParamType::FCUInt:
598
            return RemoveUnsigned(Name);
599
        case ParamType::FCText:
600
            return RemoveASCII(Name);
601
        case ParamType::FCFloat:
602
            return RemoveFloat(Name);
603
        case ParamType::FCGroup:
604
            return RemoveGrp(Name);
605
        default:
606
            break;
607
    }
608
}
609

610
const char* ParameterGrp::GetAttribute(ParamType Type,
611
                                       const char* Name,
612
                                       std::string& Value,
613
                                       const char* Default) const
614
{
615
    if (!_pGroupNode) {
616
        return Default;
617
    }
618

619
    const char* T = TypeName(Type);
620
    if (!T) {
621
        return Default;
622
    }
623

624
    DOMElement* pcElem = FindElement(_pGroupNode, T, Name);
625
    if (!pcElem) {
626
        return Default;
627
    }
628

629
    if (Type == ParamType::FCText) {
630
        Value = GetASCII(Name, Default);
631
    }
632
    else if (Type != ParamType::FCGroup) {
633
        Value = StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str();
634
    }
635
    return Value.c_str();
636
}
637

638
std::vector<std::pair<std::string, std::string>>
639
ParameterGrp::GetAttributeMap(ParamType Type, const char* sFilter) const
640
{
641
    std::vector<std::pair<std::string, std::string>> res;
642
    if (!_pGroupNode) {
643
        return res;
644
    }
645

646
    const char* T = TypeName(Type);
647
    if (!T) {
648
        return res;
649
    }
650

651
    std::string Name;
652

653
    DOMElement* pcTemp = FindElement(_pGroupNode, T);
654
    while (pcTemp) {
655
        Name = StrX(static_cast<DOMElement*>(pcTemp)
656
                        ->getAttributes()
657
                        ->getNamedItem(XStr("Name").unicodeForm())
658
                        ->getNodeValue())
659
                   .c_str();
660
        // check on filter condition
661
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
662
            if (Type == ParamType::FCGroup) {
663
                res.emplace_back(Name, std::string());
664
            }
665
            else {
666
                res.emplace_back(Name,
667
                                 StrX(static_cast<DOMElement*>(pcTemp)->getAttribute(
668
                                          XStr("Value").unicodeForm()))
669
                                     .c_str());
670
            }
671
        }
672
        pcTemp = FindNextElement(pcTemp, T);
673
    }
674
    return res;
675
}
676

677
void ParameterGrp::_Notify(ParamType Type, const char* Name, const char* Value)
678
{
679
    if (_Manager) {
680
        _Manager->signalParamChanged(this, Type, Name, Value);
681
    }
682
}
683

684
void ParameterGrp::_SetAttribute(ParamType T, const char* Name, const char* Value)
685
{
686
    const char* Type = TypeName(T);
687
    if (!Type) {
688
        return;
689
    }
690
    if (!_pGroupNode) {
691
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
692
            FC_WARN("Setting attribute " << Type << ":" << Name << " in an orphan group "
693
                                         << _cName);
694
        }
695
        return;
696
    }
697
    if (_Clearing) {
698
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
699
            FC_WARN("Adding attribute " << Type << ":" << Name << " while clearing " << GetPath());
700
        }
701
        return;
702
    }
703

704
    // find or create the Element
705
    DOMElement* pcElem = FindOrCreateElement(_pGroupNode, Type, Name);
706
    if (pcElem) {
707
        XStr attr("Value");
708
        // set the value only if different
709
        if (strcmp(StrX(pcElem->getAttribute(attr.unicodeForm())).c_str(), Value) != 0) {
710
            pcElem->setAttribute(attr.unicodeForm(), XStr(Value).unicodeForm());
711
            // trigger observer
712
            _Notify(T, Name, Value);
713
        }
714
        // For backward compatibility, old observer gets notified regardless of
715
        // value changes or not.
716
        Notify(Name);
717
    }
718
}
719

720
bool ParameterGrp::GetBool(const char* Name, bool bPreset) const
721
{
722
    if (!_pGroupNode) {
723
        return bPreset;
724
    }
725

726
    // check if Element in group
727
    DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
728
    // if not return preset
729
    if (!pcElem) {
730
        return bPreset;
731
    }
732

733
    // if yes check the value and return
734
    return (strcmp(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str(), "1") == 0);
735
}
736

737
void ParameterGrp::SetBool(const char* Name, bool bValue)
738
{
739
    _SetAttribute(ParamType::FCBool, Name, bValue ? "1" : "0");
740
}
741

742
std::vector<bool> ParameterGrp::GetBools(const char* sFilter) const
743
{
744
    std::vector<bool> vrValues;
745
    if (!_pGroupNode) {
746
        return vrValues;
747
    }
748

749
    std::string Name;
750

751
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool");
752
    while (pcTemp) {
753
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
754
        // check on filter condition
755
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
756
            if (strcmp(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(), "1") != 0) {
757
                vrValues.push_back(false);
758
            }
759
            else {
760
                vrValues.push_back(true);
761
            }
762
        }
763
        pcTemp = FindNextElement(pcTemp, "FCBool");
764
    }
765

766
    return vrValues;
767
}
768

769
std::vector<std::pair<std::string, bool>> ParameterGrp::GetBoolMap(const char* sFilter) const
770
{
771
    std::vector<std::pair<std::string, bool>> vrValues;
772
    if (!_pGroupNode) {
773
        return vrValues;
774
    }
775

776
    std::string Name;
777

778
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool");
779
    while (pcTemp) {
780
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
781
        // check on filter condition
782
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
783
            if (strcmp(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(), "1") != 0) {
784
                vrValues.emplace_back(Name, false);
785
            }
786
            else {
787
                vrValues.emplace_back(Name, true);
788
            }
789
        }
790
        pcTemp = FindNextElement(pcTemp, "FCBool");
791
    }
792

793
    return vrValues;
794
}
795

796
long ParameterGrp::GetInt(const char* Name, long lPreset) const
797
{
798
    if (!_pGroupNode) {
799
        return lPreset;
800
    }
801

802
    // check if Element in group
803
    DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
804
    // if not return preset
805
    if (!pcElem) {
806
        return lPreset;
807
    }
808
    // if yes check the value and return
809
    return atol(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str());
810
}
811

812
void ParameterGrp::SetInt(const char* Name, long lValue)
813
{
814
    std::string buf = fmt::sprintf("%li", lValue);
815
    _SetAttribute(ParamType::FCInt, Name, buf.c_str());
816
}
817

818
std::vector<long> ParameterGrp::GetInts(const char* sFilter) const
819
{
820
    std::vector<long> vrValues;
821
    if (!_pGroupNode) {
822
        return vrValues;
823
    }
824

825
    std::string Name;
826

827
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt");
828
    while (pcTemp) {
829
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
830
        // check on filter condition
831
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
832
            vrValues.push_back(
833
                atol(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str()));
834
        }
835
        pcTemp = FindNextElement(pcTemp, "FCInt");
836
    }
837

838
    return vrValues;
839
}
840

841
std::vector<std::pair<std::string, long>> ParameterGrp::GetIntMap(const char* sFilter) const
842
{
843
    std::vector<std::pair<std::string, long>> vrValues;
844
    if (!_pGroupNode) {
845
        return vrValues;
846
    }
847

848
    std::string Name;
849

850
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt");
851
    while (pcTemp) {
852
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
853
        // check on filter condition
854
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
855
            vrValues.emplace_back(
856
                Name,
857
                (atol(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str())));
858
        }
859
        pcTemp = FindNextElement(pcTemp, "FCInt");
860
    }
861

862
    return vrValues;
863
}
864

865
unsigned long ParameterGrp::GetUnsigned(const char* Name, unsigned long lPreset) const
866
{
867
    if (!_pGroupNode) {
868
        return lPreset;
869
    }
870

871
    // check if Element in group
872
    DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
873
    // if not return preset
874
    if (!pcElem) {
875
        return lPreset;
876
    }
877

878
    // if yes check the value and return
879
    const int base = 10;
880
    return strtoul(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str(), nullptr, base);
881
}
882

883
void ParameterGrp::SetUnsigned(const char* Name, unsigned long lValue)
884
{
885
    std::string buf = fmt::sprintf("%lu", lValue);
886
    _SetAttribute(ParamType::FCUInt, Name, buf.c_str());
887
}
888

889
std::vector<unsigned long> ParameterGrp::GetUnsigneds(const char* sFilter) const
890
{
891
    std::vector<unsigned long> vrValues;
892
    if (!_pGroupNode) {
893
        return vrValues;
894
    }
895

896
    std::string Name;
897
    const int base = 10;
898

899
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
900
    while (pcTemp) {
901
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
902
        // check on filter condition
903
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
904
            vrValues.push_back(
905
                strtoul(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(),
906
                        nullptr,
907
                        base));
908
        }
909
        pcTemp = FindNextElement(pcTemp, "FCUInt");
910
    }
911

912
    return vrValues;
913
}
914

915
std::vector<std::pair<std::string, unsigned long>>
916
ParameterGrp::GetUnsignedMap(const char* sFilter) const
917
{
918
    std::vector<std::pair<std::string, unsigned long>> vrValues;
919
    if (!_pGroupNode) {
920
        return vrValues;
921
    }
922

923
    std::string Name;
924
    const int base = 10;
925

926
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
927
    while (pcTemp) {
928
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
929
        // check on filter condition
930
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
931
            vrValues.emplace_back(
932
                Name,
933
                (strtoul(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(),
934
                         nullptr,
935
                         base)));
936
        }
937
        pcTemp = FindNextElement(pcTemp, "FCUInt");
938
    }
939

940
    return vrValues;
941
}
942

943
double ParameterGrp::GetFloat(const char* Name, double dPreset) const
944
{
945
    if (!_pGroupNode) {
946
        return dPreset;
947
    }
948

949
    // check if Element in group
950
    DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
951
    // if not return preset
952
    if (!pcElem) {
953
        return dPreset;
954
    }
955
    // if yes check the value and return
956
    return atof(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str());
957
}
958

959
void ParameterGrp::SetFloat(const char* Name, double dValue)
960
{
961
    // use %.12f instead of %f to handle values < 1.0e-6
962
    std::string buf = fmt::sprintf("%.12f", dValue);
963
    _SetAttribute(ParamType::FCFloat, Name, buf.c_str());
964
}
965

966
std::vector<double> ParameterGrp::GetFloats(const char* sFilter) const
967
{
968
    std::vector<double> vrValues;
969
    if (!_pGroupNode) {
970
        return vrValues;
971
    }
972

973
    std::string Name;
974

975
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
976
    while (pcTemp) {
977
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
978
        // check on filter condition
979
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
980
            vrValues.push_back(
981
                atof(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str()));
982
        }
983
        pcTemp = FindNextElement(pcTemp, "FCFloat");
984
    }
985

986
    return vrValues;
987
}
988

989
std::vector<std::pair<std::string, double>> ParameterGrp::GetFloatMap(const char* sFilter) const
990
{
991
    std::vector<std::pair<std::string, double>> vrValues;
992
    if (!_pGroupNode) {
993
        return vrValues;
994
    }
995

996
    std::string Name;
997

998
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
999
    while (pcTemp) {
1000
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
1001
        // check on filter condition
1002
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
1003
            vrValues.emplace_back(
1004
                Name,
1005
                (atof(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str())));
1006
        }
1007
        pcTemp = FindNextElement(pcTemp, "FCFloat");
1008
    }
1009

1010
    return vrValues;
1011
}
1012

1013

1014
void ParameterGrp::SetASCII(const char* Name, const char* sValue)
1015
{
1016
    if (!_pGroupNode) {
1017
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1018
            FC_WARN("Setting attribute " << "FCText:" << Name << " in an orphan group " << _cName);
1019
        }
1020
        return;
1021
    }
1022
    if (_Clearing) {
1023
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1024
            FC_WARN("Adding attribute " << "FCText:" << Name << " while clearing " << GetPath());
1025
        }
1026
        return;
1027
    }
1028

1029
    bool isNew = false;
1030
    DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
1031
    if (!pcElem) {
1032
        pcElem = CreateElement(_pGroupNode, "FCText", Name);
1033
        isNew = true;
1034
    }
1035
    if (pcElem) {
1036
        // and set the value
1037
        DOMNode* pcElem2 = pcElem->getFirstChild();
1038
        if (!pcElem2) {
1039
            XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* pDocument = _pGroupNode->getOwnerDocument();
1040
            DOMText* pText = pDocument->createTextNode(XUTF8Str(sValue).unicodeForm());
1041
            pcElem->appendChild(pText);
1042
            if (isNew || sValue[0] != 0) {
1043
                _Notify(ParamType::FCText, Name, sValue);
1044
            }
1045
        }
1046
        else if (strcmp(StrXUTF8(pcElem2->getNodeValue()).c_str(), sValue) != 0) {
1047
            pcElem2->setNodeValue(XUTF8Str(sValue).unicodeForm());
1048
            _Notify(ParamType::FCText, Name, sValue);
1049
        }
1050
        // trigger observer
1051
        Notify(Name);
1052
    }
1053
}
1054

1055
std::string ParameterGrp::GetASCII(const char* Name, const char* pPreset) const
1056
{
1057
    if (!_pGroupNode) {
1058
        return pPreset ? pPreset : "";
1059
    }
1060

1061
    // check if Element in group
1062
    DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
1063
    // if not return preset
1064
    if (!pcElem) {
1065
        if (!pPreset) {
1066
            return {};
1067
        }
1068
        return {pPreset};
1069
    }
1070
    // if yes check the value and return
1071
    DOMNode* pcElem2 = pcElem->getFirstChild();
1072
    if (pcElem2) {
1073
        return {StrXUTF8(pcElem2->getNodeValue()).c_str()};
1074
    }
1075
    return {};
1076
}
1077

1078
std::vector<std::string> ParameterGrp::GetASCIIs(const char* sFilter) const
1079
{
1080
    std::vector<std::string> vrValues;
1081
    if (!_pGroupNode) {
1082
        return vrValues;
1083
    }
1084

1085
    std::string Name;
1086

1087
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCText");
1088
    while (pcTemp) {
1089
        Name = StrXUTF8(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
1090
        // check on filter condition
1091
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
1092
            // retrieve the text element
1093
            DOMNode* pcElem2 = pcTemp->getFirstChild();
1094
            if (pcElem2) {
1095
                vrValues.emplace_back(StrXUTF8(pcElem2->getNodeValue()).c_str());
1096
            }
1097
            else {
1098
                vrValues.emplace_back("");  // For a string, an empty value is possible and allowed
1099
            }
1100
        }
1101
        pcTemp = FindNextElement(pcTemp, "FCText");
1102
    }
1103

1104
    return vrValues;
1105
}
1106

1107
std::vector<std::pair<std::string, std::string>>
1108
ParameterGrp::GetASCIIMap(const char* sFilter) const
1109
{
1110
    std::vector<std::pair<std::string, std::string>> vrValues;
1111
    if (!_pGroupNode) {
1112
        return vrValues;
1113
    }
1114

1115
    std::string Name;
1116

1117
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCText");
1118
    while (pcTemp) {
1119
        Name = StrXUTF8(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
1120
        // check on filter condition
1121
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
1122
            // retrieve the text element
1123
            DOMNode* pcElem2 = pcTemp->getFirstChild();
1124
            if (pcElem2) {
1125
                vrValues.emplace_back(Name, std::string(StrXUTF8(pcElem2->getNodeValue()).c_str()));
1126
            }
1127
            else {
1128
                vrValues.emplace_back(
1129
                    Name,
1130
                    std::string());  // For a string, an empty value is possible and allowed
1131
            }
1132
        }
1133
        pcTemp = FindNextElement(pcTemp, "FCText");
1134
    }
1135

1136
    return vrValues;
1137
}
1138

1139
//**************************************************************************
1140
// Access methods
1141

1142
void ParameterGrp::RemoveASCII(const char* Name)
1143
{
1144
    if (!_pGroupNode) {
1145
        return;
1146
    }
1147

1148
    // check if Element in group
1149
    DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
1150
    // if not return
1151
    if (!pcElem) {
1152
        return;
1153
    }
1154

1155
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1156
    node->release();
1157

1158
    // trigger observer
1159
    _Notify(ParamType::FCText, Name, nullptr);
1160
    Notify(Name);
1161
}
1162

1163
void ParameterGrp::RemoveBool(const char* Name)
1164
{
1165
    if (!_pGroupNode) {
1166
        return;
1167
    }
1168

1169
    // check if Element in group
1170
    DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
1171
    // if not return
1172
    if (!pcElem) {
1173
        return;
1174
    }
1175

1176
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1177
    node->release();
1178

1179
    // trigger observer
1180
    _Notify(ParamType::FCBool, Name, nullptr);
1181
    Notify(Name);
1182
}
1183

1184

1185
void ParameterGrp::RemoveFloat(const char* Name)
1186
{
1187
    if (!_pGroupNode) {
1188
        return;
1189
    }
1190

1191
    // check if Element in group
1192
    DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
1193
    // if not return
1194
    if (!pcElem) {
1195
        return;
1196
    }
1197

1198
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1199
    node->release();
1200

1201
    // trigger observer
1202
    _Notify(ParamType::FCFloat, Name, nullptr);
1203
    Notify(Name);
1204
}
1205

1206
void ParameterGrp::RemoveInt(const char* Name)
1207
{
1208
    if (!_pGroupNode) {
1209
        return;
1210
    }
1211

1212
    // check if Element in group
1213
    DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
1214
    // if not return
1215
    if (!pcElem) {
1216
        return;
1217
    }
1218

1219
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1220
    node->release();
1221

1222
    // trigger observer
1223
    _Notify(ParamType::FCInt, Name, nullptr);
1224
    Notify(Name);
1225
}
1226

1227
void ParameterGrp::RemoveUnsigned(const char* Name)
1228
{
1229
    if (!_pGroupNode) {
1230
        return;
1231
    }
1232

1233
    // check if Element in group
1234
    DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
1235
    // if not return
1236
    if (!pcElem) {
1237
        return;
1238
    }
1239

1240
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1241
    node->release();
1242

1243
    // trigger observer
1244
    _Notify(ParamType::FCUInt, Name, nullptr);
1245
    Notify(Name);
1246
}
1247

1248
void ParameterGrp::RemoveGrp(const char* Name)
1249
{
1250
    if (!_pGroupNode) {
1251
        return;
1252
    }
1253

1254
    auto it = _GroupMap.find(Name);
1255
    if (it == _GroupMap.end()) {
1256
        return;
1257
    }
1258

1259
    // If this or any of its children is referenced by an observer we do not
1260
    // delete the handle, just in case the group is later added again, or else
1261
    // those existing observer won't get any notification. BUT, we DO delete
1262
    // the underlying xml elements, so that we don't save the empty group
1263
    // later.
1264
    it->second->Clear(false);
1265
    if (!it->second->_Detached) {
1266
        it->second->_Detached = true;
1267
        _pGroupNode->removeChild(it->second->_pGroupNode);
1268
    }
1269
    if (it->second->ShouldRemove()) {
1270
        it->second->_Parent = nullptr;
1271
        it->second->_Manager = nullptr;
1272
        _GroupMap.erase(it);
1273
    }
1274

1275
    // trigger observer
1276
    Notify(Name);
1277
}
1278

1279
bool ParameterGrp::RenameGrp(const char* OldName, const char* NewName)
1280
{
1281
    if (!_pGroupNode) {
1282
        return false;
1283
    }
1284

1285
    auto it = _GroupMap.find(OldName);
1286
    if (it == _GroupMap.end()) {
1287
        return false;
1288
    }
1289
    auto jt = _GroupMap.find(NewName);
1290
    if (jt != _GroupMap.end()) {
1291
        return false;
1292
    }
1293

1294
    // rename group handle
1295
    _GroupMap[NewName] = _GroupMap[OldName];
1296
    _GroupMap.erase(OldName);
1297
    _GroupMap[NewName]->_cName = NewName;
1298

1299
    // check if Element in group
1300
    DOMElement* pcElem = FindElement(_pGroupNode, "FCParamGroup", OldName);
1301
    if (pcElem) {
1302
        pcElem->setAttribute(XStr("Name").unicodeForm(), XStr(NewName).unicodeForm());
1303
    }
1304

1305
    _Notify(ParamType::FCGroup, NewName, OldName);
1306
    return true;
1307
}
1308

1309
void ParameterGrp::Clear(bool notify)
1310
{
1311
    if (!_pGroupNode) {
1312
        return;
1313
    }
1314

1315
    Base::StateLocker guard(_Clearing);
1316

1317
    // early trigger notification of group removal when all its children
1318
    // hierarchies are intact.
1319
    _Notify(ParamType::FCGroup, nullptr, nullptr);
1320

1321
    // checking on references
1322
    for (auto it = _GroupMap.begin(); it != _GroupMap.end();) {
1323
        // If a group handle is referenced by some observer, then do not remove
1324
        // it but clear it, so that any existing observer can still get
1325
        // notification if the group is later on add back. We do remove the
1326
        // underlying xml element from its parent so that we won't save this
1327
        // empty group.
1328
        it->second->Clear(notify);
1329
        if (!it->second->_Detached) {
1330
            it->second->_Detached = true;
1331
            _pGroupNode->removeChild(it->second->_pGroupNode);
1332
        }
1333
        if (!it->second->ShouldRemove()) {
1334
            ++it;
1335
        }
1336
        else {
1337
            it->second->_Parent = nullptr;
1338
            it->second->_Manager = nullptr;
1339
            it = _GroupMap.erase(it);
1340
        }
1341
    }
1342

1343
    // Remove the rest of non-group nodes;
1344
    std::vector<std::pair<ParamType, std::string>> params;
1345
    for (DOMNode *child = _pGroupNode->getFirstChild(), *next = child; child != nullptr;
1346
         child = next) {
1347
        next = next->getNextSibling();
1348
        ParamType type = TypeValue(StrX(child->getNodeName()).c_str());
1349
        if (type != ParamType::FCInvalid && type != ParamType::FCGroup) {
1350
            params.emplace_back(type,
1351
                                StrX(child->getAttributes()
1352
                                         ->getNamedItem(XStr("Name").unicodeForm())
1353
                                         ->getNodeValue())
1354
                                    .c_str());
1355
        }
1356
        DOMNode* node = _pGroupNode->removeChild(child);
1357
        node->release();
1358
    }
1359

1360
    for (auto& v : params) {
1361
        _Notify(v.first, v.second.c_str(), nullptr);
1362
        if (notify) {
1363
            Notify(v.second.c_str());
1364
        }
1365
    }
1366

1367
    // trigger observer
1368
    Notify("");
1369
}
1370

1371
//**************************************************************************
1372
// Access methods
1373

1374
bool ParameterGrp::ShouldRemove() const
1375
{
1376
    if (this->getRefCount() > 1) {
1377
        return false;
1378
    }
1379

1380
    return std::all_of(_GroupMap.cbegin(), _GroupMap.cend(), [](const auto& it) {
1381
        return it.second->ShouldRemove();
1382
    });
1383
}
1384

1385
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
1386
ParameterGrp::FindElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
1387
                          const char* Type,
1388
                          const char* Name) const
1389
{
1390
    if (XMLString::compareString(Start->getNodeName(), XStr("FCParamGroup").unicodeForm()) != 0
1391
        && XMLString::compareString(Start->getNodeName(), XStr("FCParameters").unicodeForm())
1392
            != 0) {
1393
        Base::Console().Warning("FindElement: %s cannot have the element %s of type %s\n",
1394
                                StrX(Start->getNodeName()).c_str(),
1395
                                Name,
1396
                                Type);
1397
        return nullptr;
1398
    }
1399
    for (DOMNode* clChild = Start->getFirstChild(); clChild != nullptr;
1400
         clChild = clChild->getNextSibling()) {
1401
        if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
1402
            // the right node Type
1403
            if (!strcmp(Type, StrX(clChild->getNodeName()).c_str())) {
1404
                if (clChild->getAttributes()->getLength() > 0) {
1405
                    if (Name) {
1406
                        DOMNode* attr = FindAttribute(clChild, "Name");
1407
                        if (attr && !strcmp(Name, StrX(attr->getNodeValue()).c_str())) {
1408
                            return dynamic_cast<DOMElement*>(clChild);
1409
                        }
1410
                    }
1411
                    else {
1412
                        return dynamic_cast<DOMElement*>(clChild);
1413
                    }
1414
                }
1415
            }
1416
        }
1417
    }
1418
    return nullptr;
1419
}
1420

1421
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
1422
ParameterGrp::FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Prev, const char* Type) const
1423
{
1424
    DOMNode* clChild = Prev;
1425
    if (!clChild) {
1426
        return nullptr;
1427
    }
1428

1429
    while ((clChild = clChild->getNextSibling()) != nullptr) {
1430
        if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
1431
            // the right node Type
1432
            if (!strcmp(Type, StrX(clChild->getNodeName()).c_str())) {
1433
                return dynamic_cast<DOMElement*>(clChild);
1434
            }
1435
        }
1436
    }
1437
    return nullptr;
1438
}
1439

1440
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
1441
ParameterGrp::FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
1442
                                  const char* Type,
1443
                                  const char* Name)
1444
{
1445
    // first try to find it
1446
    DOMElement* pcElem = FindElement(Start, Type, Name);
1447
    if (pcElem) {
1448
        return pcElem;
1449
    }
1450

1451
    return CreateElement(Start, Type, Name);
1452
}
1453

1454
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*
1455
ParameterGrp::FindAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Node, const char* Name) const
1456
{
1457
    DOMNamedNodeMap* attr = Node->getAttributes();
1458
    if (attr) {
1459
        return attr->getNamedItem(XStr(Name).unicodeForm());
1460
    }
1461
    return nullptr;
1462
}
1463

1464
std::vector<std::pair<ParameterGrp::ParamType, std::string>>
1465
ParameterGrp::GetParameterNames(const char* sFilter) const
1466
{
1467
    std::vector<std::pair<ParameterGrp::ParamType, std::string>> res;
1468
    if (!_pGroupNode) {
1469
        return res;
1470
    }
1471

1472
    std::string Name;
1473

1474
    for (DOMNode* clChild = _pGroupNode->getFirstChild(); clChild != nullptr;
1475
         clChild = clChild->getNextSibling()) {
1476
        if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
1477
            StrX type(clChild->getNodeName());
1478
            ParamType Type = TypeValue(type.c_str());
1479
            if (Type != ParamType::FCInvalid && Type != ParamType::FCGroup) {
1480
                if (clChild->getAttributes()->getLength() > 0) {
1481
                    StrX name(clChild->getAttributes()
1482
                                  ->getNamedItem(XStr("Name").unicodeForm())
1483
                                  ->getNodeValue());
1484
                    if (!sFilter || strstr(name.c_str(), sFilter)) {
1485
                        res.emplace_back(Type, name.c_str());
1486
                    }
1487
                }
1488
            }
1489
        }
1490
    }
1491
    return res;
1492
}
1493

1494
void ParameterGrp::NotifyAll()
1495
{
1496
    // get all ints and notify
1497
    std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
1498
    for (const auto& it : IntMap) {
1499
        Notify(it.first.c_str());
1500
    }
1501

1502
    // get all booleans and notify
1503
    std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
1504
    for (const auto& it : BoolMap) {
1505
        Notify(it.first.c_str());
1506
    }
1507

1508
    // get all Floats and notify
1509
    std::vector<std::pair<std::string, double>> FloatMap = GetFloatMap();
1510
    for (const auto& it : FloatMap) {
1511
        Notify(it.first.c_str());
1512
    }
1513

1514
    // get all strings and notify
1515
    std::vector<std::pair<std::string, std::string>> StringMap = GetASCIIMap();
1516
    for (const auto& it : StringMap) {
1517
        Notify(it.first.c_str());
1518
    }
1519

1520
    // get all uints and notify
1521
    std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
1522
    for (const auto& it : UIntMap) {
1523
        Notify(it.first.c_str());
1524
    }
1525
}
1526

1527
void ParameterGrp::_Reset()
1528
{
1529
    _pGroupNode = nullptr;
1530
    for (auto& v : _GroupMap) {
1531
        v.second->_Reset();
1532
    }
1533
}
1534

1535
//**************************************************************************
1536
//**************************************************************************
1537
// ParameterSerializer
1538
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1539
ParameterSerializer::ParameterSerializer(std::string fn)
1540
    : filename(std::move(fn))
1541
{}
1542

1543
ParameterSerializer::~ParameterSerializer() = default;
1544

1545
void ParameterSerializer::SaveDocument(const ParameterManager& mgr)
1546
{
1547
    mgr.SaveDocument(filename.c_str());
1548
}
1549

1550
int ParameterSerializer::LoadDocument(ParameterManager& mgr)
1551
{
1552
    return mgr.LoadDocument(filename.c_str());
1553
}
1554

1555
bool ParameterSerializer::LoadOrCreateDocument(ParameterManager& mgr)
1556
{
1557
    return mgr.LoadOrCreateDocument(filename.c_str());
1558
}
1559

1560
//**************************************************************************
1561
//**************************************************************************
1562
// ParameterManager
1563
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1564

1565
static XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Auto;  // NOLINT
1566

1567
//**************************************************************************
1568
// Construction/Destruction
1569

1570
/** Default construction
1571
 */
1572
ParameterManager::ParameterManager()
1573
{
1574
    _Manager = this;
1575

1576
    // initialize the XML system
1577
    Init();
1578

1579
    // ---------------------------------------------------------------------------
1580
    //  Local data
1581
    //
1582
    //  gXmlFile
1583
    //      The path to the file to parser. Set via command line.
1584
    //
1585
    //  gDoNamespaces
1586
    //      Indicates whether namespace processing should be done.
1587
    //
1588
    //  gDoSchema
1589
    //      Indicates whether schema processing should be done.
1590
    //
1591
    //  gSchemaFullChecking
1592
    //      Indicates whether full schema constraint checking should be done.
1593
    //
1594
    //  gDoCreate
1595
    //      Indicates whether entity reference nodes needs to be created or not.
1596
    //      Defaults to false.
1597
    //
1598
    //  gOutputEncoding
1599
    //      The encoding we are to output in. If not set on the command line,
1600
    //      then it is defaults to the encoding of the input XML file.
1601
    //
1602
    //  gMyEOLSequence
1603
    //      The end of line sequence we are to output.
1604
    //
1605
    //  gSplitCdataSections
1606
    //      Indicates whether split-cdata-sections is to be enabled or not.
1607
    //
1608
    //  gDiscardDefaultContent
1609
    //      Indicates whether default content is discarded or not.
1610
    //
1611
    //  gUseFilter
1612
    //      Indicates if user wants to plug in the DOMPrintFilter.
1613
    //
1614
    //  gValScheme
1615
    //      Indicates what validation scheme to use. It defaults to 'auto', but
1616
    //      can be set via the -v= command.
1617
    //
1618
    // ---------------------------------------------------------------------------
1619

1620
    // NOLINTBEGIN
1621
    gIgnoreSave = false;
1622
    gDoNamespaces = false;
1623
    gDoSchema = false;
1624
    gSchemaFullChecking = false;
1625
    gDoCreate = true;
1626

1627
    gOutputEncoding = nullptr;
1628
    gMyEOLSequence = nullptr;
1629

1630
    gSplitCdataSections = true;
1631
    gDiscardDefaultContent = true;
1632
    gUseFilter = true;
1633
    gFormatPrettyPrint = true;
1634
    // NOLINTEND
1635
}
1636

1637
/** Destruction
1638
 * complete destruction of the object
1639
 */
1640
ParameterManager::~ParameterManager()
1641
{
1642
    _Reset();
1643
    delete _pDocument;
1644
    delete paramSerializer;
1645
}
1646

1647
Base::Reference<ParameterManager> ParameterManager::Create()
1648
{
1649
    return {new ParameterManager()};
1650
}
1651

1652
void ParameterManager::Init()
1653
{
1654
    static bool Init = false;
1655
    if (!Init) {
1656
        try {
1657
            XMLPlatformUtils::Initialize();
1658
        }
1659
        catch (const XMLException& toCatch) {
1660
#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN)
1661
            std::ostringstream err;
1662
#else
1663
            std::stringstream err;
1664
#endif
1665
            char* pMsg = XMLString::transcode(toCatch.getMessage());
1666
            err << "Error during Xerces-c Initialization.\n"
1667
                << "  Exception message:" << pMsg;
1668
            XMLString::release(&pMsg);
1669
            throw XMLBaseException(err.str().c_str());
1670
        }
1671
        Init = true;
1672
    }
1673
}
1674

1675
void ParameterManager::Terminate()
1676
{
1677
    XMLTools::terminate();
1678
    XMLPlatformUtils::Terminate();
1679
}
1680

1681
//**************************************************************************
1682
// Serializer handling
1683

1684
void ParameterManager::SetSerializer(ParameterSerializer* ps)
1685
{
1686
    if (paramSerializer != ps) {
1687
        delete paramSerializer;
1688
    }
1689
    paramSerializer = ps;
1690
}
1691

1692
bool ParameterManager::HasSerializer() const
1693
{
1694
    return (paramSerializer != nullptr);
1695
}
1696

1697
const std::string& ParameterManager::GetSerializeFileName() const
1698
{
1699
    static const std::string _dummy;
1700
    return paramSerializer ? paramSerializer->GetFileName() : _dummy;
1701
}
1702

1703
int ParameterManager::LoadDocument()
1704
{
1705
    if (paramSerializer) {
1706
        return paramSerializer->LoadDocument(*this);
1707
    }
1708

1709
    return -1;
1710
}
1711

1712
bool ParameterManager::LoadOrCreateDocument()
1713
{
1714
    if (paramSerializer) {
1715
        return paramSerializer->LoadOrCreateDocument(*this);
1716
    }
1717

1718
    return false;
1719
}
1720

1721
void ParameterManager::SaveDocument() const
1722
{
1723
    if (paramSerializer) {
1724
        paramSerializer->SaveDocument(*this);
1725
    }
1726
}
1727

1728
void ParameterManager::SetIgnoreSave(bool value)
1729
{
1730
    gIgnoreSave = value;
1731
}
1732

1733
bool ParameterManager::IgnoreSave() const
1734
{
1735
    return gIgnoreSave;
1736
}
1737

1738
namespace
1739
{
1740
QString getLockFile(const Base::FileInfo& file)
1741
{
1742
    QFileInfo fi(QDir::tempPath(), QString::fromStdString(file.fileName() + ".lock"));
1743
    return fi.absoluteFilePath();
1744
}
1745

1746
int getTimeout()
1747
{
1748
    const int timeout = 5000;
1749
    return timeout;
1750
}
1751
}  // namespace
1752

1753
//**************************************************************************
1754
// Document handling
1755

1756
bool ParameterManager::LoadOrCreateDocument(const char* sFileName)
1757
{
1758
    Base::FileInfo file(sFileName);
1759
    if (file.exists()) {
1760
        LoadDocument(sFileName);
1761
        return false;
1762
    }
1763

1764
    CreateDocument();
1765
    return true;
1766
}
1767

1768
int ParameterManager::LoadDocument(const char* sFileName)
1769
{
1770
    try {
1771
        Base::FileInfo file(sFileName);
1772
        QLockFile lock(getLockFile(file));
1773
        if (!lock.tryLock(getTimeout())) {
1774
            // Continue with empty config
1775
            CreateDocument();
1776
            SetIgnoreSave(true);
1777
            std::cerr << "Failed to access file for reading: " << sFileName << std::endl;
1778
            return 1;
1779
        }
1780
#if defined(FC_OS_WIN32)
1781
        std::wstring name = file.toStdWString();
1782
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1783
        LocalFileInputSource inputSource(reinterpret_cast<const XMLCh*>(name.c_str()));
1784
#else
1785
        LocalFileInputSource inputSource(XStr(file.filePath().c_str()).unicodeForm());
1786
#endif
1787
        return LoadDocument(inputSource);
1788
    }
1789
    catch (const Base::Exception& e) {
1790
        std::cerr << e.what() << std::endl;
1791
        throw;
1792
    }
1793
    catch (...) {
1794
        std::cerr << "An error occurred during parsing\n " << std::endl;
1795
        throw;
1796
    }
1797
}
1798

1799
int ParameterManager::LoadDocument(const XERCES_CPP_NAMESPACE_QUALIFIER InputSource& inputSource)
1800
{
1801
    //
1802
    //  Create our parser, then attach an error handler to the parser.
1803
    //  The parser will call back to methods of the ErrorHandler if it
1804
    //  discovers errors during the course of parsing the XML document.
1805
    //
1806
    auto parser = new XercesDOMParser;
1807
    parser->setValidationScheme(gValScheme);
1808
    parser->setDoNamespaces(gDoNamespaces);
1809
    parser->setDoSchema(gDoSchema);
1810
    parser->setValidationSchemaFullChecking(gSchemaFullChecking);
1811
    parser->setCreateEntityReferenceNodes(gDoCreate);
1812

1813
    auto errReporter = new DOMTreeErrorReporter();
1814
    parser->setErrorHandler(errReporter);
1815

1816
    //
1817
    //  Parse the XML file, catching any XML exceptions that might propagate
1818
    //  out of it.
1819
    //
1820
    bool errorsOccured = false;
1821
    try {
1822
        parser->parse(inputSource);
1823
    }
1824
    catch (const XMLException& e) {
1825
        std::cerr << "An error occurred during parsing\n   Message: " << StrX(e.getMessage())
1826
                  << std::endl;
1827
        errorsOccured = true;
1828
    }
1829
    catch (const DOMException& e) {
1830
        std::cerr << "A DOM error occurred during parsing\n   DOMException code: " << e.code
1831
                  << std::endl;
1832
        errorsOccured = true;
1833
    }
1834
    catch (...) {
1835
        std::cerr << "An error occurred during parsing\n " << std::endl;
1836
        errorsOccured = true;
1837
    }
1838

1839
    if (errorsOccured) {
1840
        delete parser;
1841
        delete errReporter;
1842
        return 0;
1843
    }
1844

1845
    _pDocument = parser->adoptDocument();
1846
    delete parser;
1847
    delete errReporter;
1848

1849
    if (!_pDocument) {
1850
        throw XMLBaseException("Malformed Parameter document: Invalid document");
1851
    }
1852

1853
    DOMElement* rootElem = _pDocument->getDocumentElement();
1854
    if (!rootElem) {
1855
        throw XMLBaseException("Malformed Parameter document: Root group not found");
1856
    }
1857

1858
    _pGroupNode = FindElement(rootElem, "FCParamGroup", "Root");
1859

1860
    if (!_pGroupNode) {
1861
        throw XMLBaseException("Malformed Parameter document: Root group not found");
1862
    }
1863

1864
    return 1;
1865
}
1866

1867
void ParameterManager::SaveDocument(const char* sFileName) const
1868
{
1869
    try {
1870
        Base::FileInfo file(sFileName);
1871
        QLockFile lock(getLockFile(file));
1872
        if (!lock.tryLock(getTimeout())) {
1873
            std::cerr << "Failed to access file for writing: " << sFileName << std::endl;
1874
            return;
1875
        }
1876
        //
1877
        // Plug in a format target to receive the resultant
1878
        // XML stream from the serializer.
1879
        //
1880
        // LocalFileFormatTarget prints the resultant XML stream
1881
        // to a file once it receives any thing from the serializer.
1882
        //
1883
        XMLFormatTarget* myFormTarget {};
1884
#if defined(FC_OS_WIN32)
1885
        std::wstring name = file.toStdWString();
1886
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1887
        myFormTarget = new LocalFileFormatTarget(reinterpret_cast<const XMLCh*>(name.c_str()));
1888
#else
1889
        myFormTarget = new LocalFileFormatTarget(file.filePath().c_str());
1890
#endif
1891
        SaveDocument(myFormTarget);
1892
        delete myFormTarget;
1893
    }
1894
    catch (XMLException& e) {
1895
        std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
1896
                  << StrX(e.getMessage()) << std::endl;
1897
    }
1898
}
1899

1900
void ParameterManager::SaveDocument(XMLFormatTarget* pFormatTarget) const
1901
{
1902
    try {
1903
        std::unique_ptr<DOMPrintFilter> myFilter;
1904
        std::unique_ptr<DOMErrorHandler> myErrorHandler;
1905

1906
        // NOLINTBEGIN
1907
        // get a serializer, an instance of DOMWriter
1908
        XMLCh tempStr[100];
1909
        XMLString::transcode("LS", tempStr, 99);
1910
        DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
1911
        DOMLSSerializer* theSerializer =
1912
            static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
1913
        // NOLINTEND
1914

1915
        // set user specified end of line sequence and output encoding
1916
        theSerializer->setNewLine(gMyEOLSequence);
1917

1918

1919
        //
1920
        // do the serialization through DOMWriter::writeNode();
1921
        //
1922
        if (_pDocument) {
1923
            DOMLSOutput* theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
1924
            theOutput->setEncoding(gOutputEncoding);
1925

1926
            if (gUseFilter) {
1927
                myFilter = std::make_unique<DOMPrintFilter>(
1928
                    DOMNodeFilter::SHOW_ELEMENT | DOMNodeFilter::SHOW_ATTRIBUTE
1929
                    | DOMNodeFilter::SHOW_DOCUMENT_TYPE | DOMNodeFilter::SHOW_TEXT);
1930
                theSerializer->setFilter(myFilter.get());
1931
            }
1932

1933
            // plug in user's own error handler
1934
            myErrorHandler = std::make_unique<DOMPrintErrorHandler>();
1935
            DOMConfiguration* config = theSerializer->getDomConfig();
1936

1937
            // NOLINTBEGIN
1938
            config->setParameter(XMLUni::fgDOMErrorHandler, myErrorHandler.get());
1939

1940
            // set feature if the serializer supports the feature/mode
1941
            if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections)) {
1942
                config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections);
1943
            }
1944

1945
            if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent,
1946
                                        gDiscardDefaultContent)) {
1947
                config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, gDiscardDefaultContent);
1948
            }
1949

1950
            if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint)) {
1951
                config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint);
1952
            }
1953
            // NOLINTEND
1954

1955
            theOutput->setByteStream(pFormatTarget);
1956
            theSerializer->write(_pDocument, theOutput);
1957

1958
            theOutput->release();
1959
        }
1960

1961
        theSerializer->release();
1962
    }
1963
    catch (XMLException& e) {
1964
        std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
1965
                  << StrX(e.getMessage()) << std::endl;
1966
    }
1967
}
1968

1969
void ParameterManager::CreateDocument()
1970
{
1971
    // creating a document from screatch
1972
    DOMImplementation* impl =
1973
        DOMImplementationRegistry::getDOMImplementation(XStr("Core").unicodeForm());
1974
    delete _pDocument;
1975
    _pDocument = impl->createDocument(nullptr,  // root element namespace URI.
1976
                                      XStr("FCParameters").unicodeForm(),  // root element name
1977
                                      nullptr);  // document type object (DTD).
1978

1979
    // creating the node for the root group
1980
    DOMElement* rootElem = _pDocument->getDocumentElement();
1981
    _pGroupNode = _pDocument->createElement(XStr("FCParamGroup").unicodeForm());
1982
    _pGroupNode->setAttribute(XStr("Name").unicodeForm(), XStr("Root").unicodeForm());
1983
    rootElem->appendChild(_pGroupNode);
1984
}
1985

1986
void ParameterManager::CheckDocument() const
1987
{
1988
    if (!_pDocument) {
1989
        return;
1990
    }
1991

1992
    try {
1993
        //
1994
        // Plug in a format target to receive the resultant
1995
        // XML stream from the serializer.
1996
        //
1997
        // LocalFileFormatTarget prints the resultant XML stream
1998
        // to a file once it receives any thing from the serializer.
1999
        //
2000
        MemBufFormatTarget myFormTarget;
2001
        SaveDocument(&myFormTarget);
2002

2003
        // Either use the file saved on disk or write the current XML into a buffer in memory
2004
        // const char* xmlFile = "...";
2005
        MemBufInputSource xmlFile(myFormTarget.getRawBuffer(), myFormTarget.getLen(), "(memory)");
2006

2007
        // Either load the XSD file from disk or use the built-in string
2008
        // const char* xsdFile = "...";
2009
        std::string xsdStr(xmlSchemeString);  // NOLINT
2010
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2011
        MemBufInputSource xsdFile(reinterpret_cast<const XMLByte*>(xsdStr.c_str()),
2012
                                  xsdStr.size(),
2013
                                  "Parameter.xsd");
2014

2015
        // See
2016
        // http://apache-xml-project.6118.n7.nabble.com/validating-xml-with-xsd-schema-td17515.html
2017
        //
2018
        XercesDOMParser parser;
2019
        Grammar* grammar = parser.loadGrammar(xsdFile, Grammar::SchemaGrammarType, true);
2020
        if (!grammar) {
2021
            Base::Console().Error("Grammar file cannot be loaded.\n");
2022
            return;
2023
        }
2024

2025
        parser.setExternalNoNamespaceSchemaLocation("Parameter.xsd");
2026
        // parser.setExitOnFirstFatalError(true);
2027
        // parser.setValidationConstraintFatal(true);
2028
        parser.cacheGrammarFromParse(true);
2029
        parser.setValidationScheme(XercesDOMParser::Val_Auto);
2030
        parser.setDoNamespaces(true);
2031
        parser.setDoSchema(true);
2032

2033
        DOMTreeErrorReporter errHandler;
2034
        parser.setErrorHandler(&errHandler);
2035
        parser.parse(xmlFile);
2036

2037
        if (parser.getErrorCount() > 0) {
2038
            Base::Console().Error("Unexpected XML structure detected: %zu errors\n",
2039
                                  parser.getErrorCount());
2040
        }
2041
    }
2042
    catch (XMLException& e) {
2043
        std::cerr << "An error occurred while checking document. Msg is:" << std::endl
2044
                  << StrX(e.getMessage()) << std::endl;
2045
    }
2046
}
2047

2048

2049
//**************************************************************************
2050
//**************************************************************************
2051
// DOMTreeErrorReporter
2052
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2053

2054
void DOMTreeErrorReporter::warning(const SAXParseException& /*exc*/)
2055
{
2056
    //
2057
    // Ignore all warnings.
2058
    //
2059
}
2060

2061
void DOMTreeErrorReporter::error(const SAXParseException& toCatch)
2062
{
2063
    fSawErrors = true;
2064
    std::cerr << "Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
2065
              << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
2066
              << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
2067
}
2068

2069
void DOMTreeErrorReporter::fatalError(const SAXParseException& toCatch)
2070
{
2071
    fSawErrors = true;
2072
    std::cerr << "Fatal Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
2073
              << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
2074
              << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
2075
}
2076

2077
void DOMTreeErrorReporter::resetErrors()
2078
{
2079
    // No-op in this case
2080
}
2081

2082

2083
//**************************************************************************
2084
//**************************************************************************
2085
// DOMPrintFilter
2086
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2087
DOMPrintFilter::DOMPrintFilter(ShowType whatToShow)
2088
    : fWhatToShow(whatToShow)
2089
{}
2090

2091
DOMPrintFilter::FilterAction DOMPrintFilter::acceptNode(const DOMNode* node) const
2092
{
2093
    if (XMLString::compareString(node->getNodeName(), XStr("FCParameters").unicodeForm()) == 0) {
2094
        // This node is supposed to have a single FCParamGroup and two text nodes.
2095
        // Over time it can happen that the text nodes collect extra newlines.
2096
        const DOMNodeList* children = node->getChildNodes();
2097
        for (XMLSize_t i = 0; i < children->getLength(); i++) {
2098
            DOMNode* child = children->item(i);
2099
            if (child->getNodeType() == DOMNode::TEXT_NODE) {
2100
                child->setNodeValue(XStr("\n").unicodeForm());
2101
            }
2102
        }
2103
    }
2104

2105
    // clang-format off
2106
    switch (node->getNodeType()) {
2107
        case DOMNode::TEXT_NODE: {
2108
            // Filter out text element if it is under a group node. Note text xml
2109
            // element is plain text in between tags, and we do not store any text
2110
            // there.
2111
            auto parent = node->getParentNode();
2112
            if (parent && XMLString::compareString(parent->getNodeName(),
2113
                                                   XStr("FCParamGroup").unicodeForm()) == 0) {
2114
                return DOMNodeFilter::FILTER_REJECT;
2115
            }
2116
            return DOMNodeFilter::FILTER_ACCEPT;
2117
        }
2118
        case DOMNode::DOCUMENT_TYPE_NODE:
2119
        case DOMNode::DOCUMENT_NODE: {
2120
            return DOMNodeFilter::FILTER_REJECT;  // no effect
2121
        }
2122
        default: {
2123
            return DOMNodeFilter::FILTER_ACCEPT;
2124
        }
2125
    }
2126
    // clang-format on
2127
}
2128

2129
//**************************************************************************
2130
//**************************************************************************
2131
// DOMPrintErrorHandler
2132
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2133

2134

2135
bool DOMPrintErrorHandler::handleError(const DOMError& domError)
2136
{
2137
    // Display whatever error message passed from the serializer
2138
    char* msg = XMLString::transcode(domError.getMessage());
2139
    std::cout << msg << std::endl;
2140
    XMLString::release(&msg);
2141

2142
    // Instructs the serializer to continue serialization if possible.
2143
    return true;
2144
}
2145

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

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

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

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