FreeCAD

Форк
0
/
Parameter.cpp 
2093 строки · 64.2 Кб
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
#ifdef FC_OS_LINUX
45
#include <unistd.h>
46
#endif
47

48
#include <boost/algorithm/string.hpp>
49

50
#include "Parameter.h"
51
#include "Parameter.inl"
52
#include "Console.h"
53
#include "Exception.h"
54
#include "Tools.h"
55

56
FC_LOG_LEVEL_INIT("Parameter", true, true)
57

58

59
XERCES_CPP_NAMESPACE_USE
60
using namespace Base;
61

62

63
#include "XMLTools.h"
64

65
//**************************************************************************
66
//**************************************************************************
67
// private classes declaration:
68
// - DOMTreeErrorReporter
69
// - StrX
70
// - DOMPrintFilter
71
// - DOMPrintErrorHandler
72
// - XStr
73
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
74

75

76
class DOMTreeErrorReporter: public ErrorHandler
77
{
78
public:
79
    // -----------------------------------------------------------------------
80
    //  Implementation of the error handler interface
81
    // -----------------------------------------------------------------------
82
    void warning(const SAXParseException& toCatch) override;
83
    void error(const SAXParseException& toCatch) override;
84
    void fatalError(const SAXParseException& toCatch) override;
85
    void resetErrors() override;
86

87
    // -----------------------------------------------------------------------
88
    //  Getter methods
89
    // -----------------------------------------------------------------------
90
    bool getSawErrors() const;
91

92
    // -----------------------------------------------------------------------
93
    //  Private data members
94
    //
95
    //  fSawErrors
96
    //      This is set if we get any errors, and is queryable via a getter
97
    //      method. Its used by the main code to suppress output if there are
98
    //      errors.
99
    // -----------------------------------------------------------------------
100
    bool fSawErrors {false};
101
};
102

103

104
class DOMPrintFilter: public DOMLSSerializerFilter
105
{
106
public:
107
    /** @name Constructors */
108
    explicit DOMPrintFilter(ShowType whatToShow = DOMNodeFilter::SHOW_ALL);
109
    //@{
110

111
    /** @name Destructors */
112
    ~DOMPrintFilter() override = default;
113
    //@{
114

115
    /** @ interface from DOMWriterFilter */
116
    FilterAction acceptNode(const XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node) const override;
117
    //@{
118

119
    ShowType getWhatToShow() const override
120
    {
121
        return fWhatToShow;
122
    }
123

124
    // unimplemented copy ctor and assignment operator
125
    DOMPrintFilter(const DOMPrintFilter&) = delete;
126
    DOMPrintFilter(DOMPrintFilter&&) = delete;
127
    DOMPrintFilter& operator=(const DOMPrintFilter&) = delete;
128
    DOMPrintFilter& operator=(DOMPrintFilter&&) = delete;
129

130
    ShowType fWhatToShow;
131
};
132

133
class DOMPrintErrorHandler: public DOMErrorHandler
134
{
135
public:
136
    DOMPrintErrorHandler() = default;
137
    ~DOMPrintErrorHandler() override = default;
138

139
    /** @name The error handler interface */
140
    bool handleError(const DOMError& domError) override;
141
    void resetErrors()
142
    {}
143

144
    /* Unimplemented constructors and operators */
145
    DOMPrintErrorHandler(const DOMPrintErrorHandler&) = delete;
146
    DOMPrintErrorHandler(DOMPrintErrorHandler&&) = delete;
147
    void operator=(const DOMPrintErrorHandler&) = delete;
148
    void operator=(DOMPrintErrorHandler&&) = delete;
149
};
150

151

152
inline bool DOMTreeErrorReporter::getSawErrors() const
153
{
154
    return fSawErrors;
155
}
156

157

158
//**************************************************************************
159
//**************************************************************************
160
// ParameterManager
161
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
162

163

164
//**************************************************************************
165
// Construction/Destruction
166

167

168
/** Default construction
169
 */
170
ParameterGrp::ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* GroupNode,
171
                           const char* sName,
172
                           ParameterGrp* Parent)
173
    : Base::Handled()
174
    , Subject<const char*>()
175
    , _pGroupNode(GroupNode)
176
    , _Parent(Parent)
177
{
178
    if (sName) {
179
        _cName = sName;
180
    }
181
    if (_Parent) {
182
        _Manager = _Parent->_Manager;
183
    }
184
}
185

186

187
/** Destruction
188
 * complete destruction of the object
189
 */
190
ParameterGrp::~ParameterGrp()
191
{
192
    for (auto& v : _GroupMap) {
193
        v.second->_Parent = nullptr;
194
        v.second->_Manager = nullptr;
195
    }
196
    if (_Detached && _pGroupNode) {
197
        _pGroupNode->release();
198
    }
199
}
200

201
//**************************************************************************
202
// Access methods
203

204
void ParameterGrp::copyTo(Base::Reference<ParameterGrp> Grp)
205
{
206
    if (Grp == this) {
207
        return;
208
    }
209

210
    // delete previous content
211
    Grp->Clear(true);
212

213
    // copy all
214
    insertTo(Grp);
215
}
216

217
void ParameterGrp::insertTo(Base::Reference<ParameterGrp> Grp)
218
{
219
    if (Grp == this) {
220
        return;
221
    }
222

223
    // copy group
224
    std::vector<Base::Reference<ParameterGrp>> Grps = GetGroups();
225
    std::vector<Base::Reference<ParameterGrp>>::iterator It1;
226
    for (It1 = Grps.begin(); It1 != Grps.end(); ++It1) {
227
        (*It1)->insertTo(Grp->GetGroup((*It1)->GetGroupName()));
228
    }
229

230
    // copy strings
231
    std::vector<std::pair<std::string, std::string>> StringMap = GetASCIIMap();
232
    std::vector<std::pair<std::string, std::string>>::iterator It2;
233
    for (It2 = StringMap.begin(); It2 != StringMap.end(); ++It2) {
234
        Grp->SetASCII(It2->first.c_str(), It2->second.c_str());
235
    }
236

237
    // copy bool
238
    std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
239
    std::vector<std::pair<std::string, bool>>::iterator It3;
240
    for (It3 = BoolMap.begin(); It3 != BoolMap.end(); ++It3) {
241
        Grp->SetBool(It3->first.c_str(), It3->second);
242
    }
243

244
    // copy int
245
    std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
246
    std::vector<std::pair<std::string, long>>::iterator It4;
247
    for (It4 = IntMap.begin(); It4 != IntMap.end(); ++It4) {
248
        Grp->SetInt(It4->first.c_str(), It4->second);
249
    }
250

251
    // copy float
252
    std::vector<std::pair<std::string, double>> FloatMap = GetFloatMap();
253
    std::vector<std::pair<std::string, double>>::iterator It5;
254
    for (It5 = FloatMap.begin(); It5 != FloatMap.end(); ++It5) {
255
        Grp->SetFloat(It5->first.c_str(), It5->second);
256
    }
257

258
    // copy uint
259
    std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
260
    std::vector<std::pair<std::string, unsigned long>>::iterator It6;
261
    for (It6 = UIntMap.begin(); It6 != UIntMap.end(); ++It6) {
262
        Grp->SetUnsigned(It6->first.c_str(), It6->second);
263
    }
264
}
265

266
void ParameterGrp::exportTo(const char* FileName)
267
{
268
    auto Mngr = ParameterManager::Create();
269

270
    Mngr->CreateDocument();
271

272
    // copy all into the new document
273
    insertTo(Base::Reference<ParameterGrp>(Mngr));
274

275
    Mngr->SaveDocument(FileName);
276
}
277

278
void ParameterGrp::importFrom(const char* FileName)
279
{
280
    auto Mngr = ParameterManager::Create();
281

282
    if (Mngr->LoadDocument(FileName) != 1) {
283
        throw FileException("ParameterGrp::import() cannot load document", FileName);
284
    }
285

286
    Mngr->copyTo(Base::Reference<ParameterGrp>(this));
287
}
288

289
void ParameterGrp::insert(const char* FileName)
290
{
291
    auto Mngr = ParameterManager::Create();
292

293
    if (Mngr->LoadDocument(FileName) != 1) {
294
        throw FileException("ParameterGrp::import() cannot load document", FileName);
295
    }
296

297
    Mngr->insertTo(Base::Reference<ParameterGrp>(this));
298
}
299

300
void ParameterGrp::revert(const char* FileName)
301
{
302
    auto Mngr = ParameterManager::Create();
303

304
    if (Mngr->LoadDocument(FileName) != 1) {
305
        throw FileException("ParameterGrp::revert() cannot load document", FileName);
306
    }
307

308
    revert(Base::Reference<ParameterGrp>(Mngr));
309
}
310

311
void ParameterGrp::revert(Base::Reference<ParameterGrp> Grp)
312
{
313
    if (Grp == this) {
314
        return;
315
    }
316

317
    for (auto& grp : Grp->GetGroups()) {
318
        if (HasGroup(grp->GetGroupName())) {
319
            GetGroup(grp->GetGroupName())->revert(grp);
320
        }
321
    }
322

323
    for (const auto& v : Grp->GetASCIIMap()) {
324
        if (GetASCII(v.first.c_str(), v.second.c_str()) == v.second) {
325
            RemoveASCII(v.first.c_str());
326
        }
327
    }
328

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

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

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

347
    for (const auto& v : Grp->GetFloatMap()) {
348
        if (GetFloat(v.first.c_str(), v.second) == v.second) {
349
            RemoveFloat(v.first.c_str());
350
        }
351
    }
352
}
353

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

369
    if (_Detached && _Parent) {
370
        // re-attach the group
371
        _Parent->_GetGroup(_cName.c_str());
372
    }
373

374
    XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* pDocument = Start->getOwnerDocument();
375

376
    auto pcElem = pDocument->createElement(XStr(Type).unicodeForm());
377
    pcElem->setAttribute(XStr("Name").unicodeForm(), XStr(Name).unicodeForm());
378
    Start->appendChild(pcElem);
379

380
    return pcElem;
381
}
382

383
Base::Reference<ParameterGrp> ParameterGrp::GetGroup(const char* Name)
384
{
385
    if (!Name) {
386
        throw Base::ValueError("Empty group name");
387
    }
388

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

412
Base::Reference<ParameterGrp> ParameterGrp::_GetGroup(const char* Name)
413
{
414
    Base::Reference<ParameterGrp> rParamGrp;
415
    if (!_pGroupNode) {
416
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
417
            FC_WARN("Adding group " << Name << " in an orphan group " << _cName);
418
        }
419
        return rParamGrp;
420
    }
421
    if (_Clearing) {
422
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
423
            FC_WARN("Adding group " << Name << " while clearing " << GetPath());
424
        }
425
        return rParamGrp;
426
    }
427

428
    DOMElement* pcTemp {};
429

430
    // search if Group node already there
431
    pcTemp = FindElement(_pGroupNode, "FCParamGroup", Name);
432

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

452
    if (!pcTemp && !this->_Detached) {
453
        _Notify(ParamType::FCGroup, Name, Name);
454
    }
455

456
    return rParamGrp;
457
}
458

459
std::string ParameterGrp::GetPath() const
460
{
461
    std::string path;
462
    if (_Parent && _Parent != _Manager) {
463
        path = _Parent->GetPath();
464
    }
465
    if (!path.empty() && !_cName.empty()) {
466
        path += "/";
467
    }
468
    path += _cName;
469
    return path;
470
}
471

472
std::vector<Base::Reference<ParameterGrp>> ParameterGrp::GetGroups()
473
{
474
    Base::Reference<ParameterGrp> rParamGrp;
475
    std::vector<Base::Reference<ParameterGrp>> vrParamGrp;
476

477
    if (!_pGroupNode) {
478
        return vrParamGrp;
479
    }
480

481
    std::string Name;
482

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

498
    return vrParamGrp;
499
}
500

501
/// test if this group is empty
502
bool ParameterGrp::IsEmpty() const
503
{
504
    return !(_pGroupNode && _pGroupNode->getFirstChild());
505
}
506

507
/// test if a special sub group is in this group
508
bool ParameterGrp::HasGroup(const char* Name) const
509
{
510
    if (_GroupMap.find(Name) != _GroupMap.end()) {
511
        return true;
512
    }
513

514
    if (_pGroupNode && FindElement(_pGroupNode, "FCParamGroup", Name) != nullptr) {
515
        return true;
516
    }
517

518
    return false;
519
}
520

521
const char* ParameterGrp::TypeName(ParamType Type)
522
{
523
    switch (Type) {
524
        case ParamType::FCBool:
525
            return "FCBool";
526
        case ParamType::FCInt:
527
            return "FCInt";
528
        case ParamType::FCUInt:
529
            return "FCUInt";
530
        case ParamType::FCText:
531
            return "FCText";
532
        case ParamType::FCFloat:
533
            return "FCFloat";
534
        case ParamType::FCGroup:
535
            return "FCParamGroup";
536
        default:
537
            return nullptr;
538
    }
539
}
540

541
ParameterGrp::ParamType ParameterGrp::TypeValue(const char* Name)
542
{
543
    if (Name) {
544
        if (boost::equals(Name, "FCBool")) {
545
            return ParamType::FCBool;
546
        }
547
        if (boost::equals(Name, "FCInt")) {
548
            return ParamType::FCInt;
549
        }
550
        if (boost::equals(Name, "FCUInt")) {
551
            return ParamType::FCUInt;
552
        }
553
        if (boost::equals(Name, "FCText")) {
554
            return ParamType::FCText;
555
        }
556
        if (boost::equals(Name, "FCFloat")) {
557
            return ParamType::FCFloat;
558
        }
559
        if (boost::equals(Name, "FCParamGroup")) {
560
            return ParamType::FCGroup;
561
        }
562
    }
563
    return ParamType::FCInvalid;
564
}
565

566
void ParameterGrp::SetAttribute(ParamType Type, const char* Name, const char* Value)
567
{
568
    switch (Type) {
569
        case ParamType::FCBool:
570
        case ParamType::FCInt:
571
        case ParamType::FCUInt:
572
        case ParamType::FCFloat:
573
            return _SetAttribute(Type, Name, Value);
574
        case ParamType::FCText:
575
            return SetASCII(Name, Value);
576
        case ParamType::FCGroup:
577
            RenameGrp(Name, Value);
578
            break;
579
        default:
580
            break;
581
    }
582
}
583

584
void ParameterGrp::RemoveAttribute(ParamType Type, const char* Name)
585
{
586
    switch (Type) {
587
        case ParamType::FCBool:
588
            return RemoveBool(Name);
589
        case ParamType::FCInt:
590
            return RemoveInt(Name);
591
        case ParamType::FCUInt:
592
            return RemoveUnsigned(Name);
593
        case ParamType::FCText:
594
            return RemoveASCII(Name);
595
        case ParamType::FCFloat:
596
            return RemoveFloat(Name);
597
        case ParamType::FCGroup:
598
            return RemoveGrp(Name);
599
        default:
600
            break;
601
    }
602
}
603

604
const char* ParameterGrp::GetAttribute(ParamType Type,
605
                                       const char* Name,
606
                                       std::string& Value,
607
                                       const char* Default) const
608
{
609
    if (!_pGroupNode) {
610
        return Default;
611
    }
612

613
    const char* T = TypeName(Type);
614
    if (!T) {
615
        return Default;
616
    }
617

618
    DOMElement* pcElem = FindElement(_pGroupNode, T, Name);
619
    if (!pcElem) {
620
        return Default;
621
    }
622

623
    if (Type == ParamType::FCText) {
624
        Value = GetASCII(Name, Default);
625
    }
626
    else if (Type != ParamType::FCGroup) {
627
        Value = StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str();
628
    }
629
    return Value.c_str();
630
}
631

632
std::vector<std::pair<std::string, std::string>>
633
ParameterGrp::GetAttributeMap(ParamType Type, const char* sFilter) const
634
{
635
    std::vector<std::pair<std::string, std::string>> res;
636
    if (!_pGroupNode) {
637
        return res;
638
    }
639

640
    const char* T = TypeName(Type);
641
    if (!T) {
642
        return res;
643
    }
644

645
    std::string Name;
646

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

671
void ParameterGrp::_Notify(ParamType Type, const char* Name, const char* Value)
672
{
673
    if (_Manager) {
674
        _Manager->signalParamChanged(this, Type, Name, Value);
675
    }
676
}
677

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

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

714
bool ParameterGrp::GetBool(const char* Name, bool bPreset) const
715
{
716
    if (!_pGroupNode) {
717
        return bPreset;
718
    }
719

720
    // check if Element in group
721
    DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
722
    // if not return preset
723
    if (!pcElem) {
724
        return bPreset;
725
    }
726

727
    // if yes check the value and return
728
    return (strcmp(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str(), "1") == 0);
729
}
730

731
void ParameterGrp::SetBool(const char* Name, bool bValue)
732
{
733
    _SetAttribute(ParamType::FCBool, Name, bValue ? "1" : "0");
734
}
735

736
std::vector<bool> ParameterGrp::GetBools(const char* sFilter) const
737
{
738
    std::vector<bool> vrValues;
739
    if (!_pGroupNode) {
740
        return vrValues;
741
    }
742

743
    std::string Name;
744

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

760
    return vrValues;
761
}
762

763
std::vector<std::pair<std::string, bool>> ParameterGrp::GetBoolMap(const char* sFilter) const
764
{
765
    std::vector<std::pair<std::string, bool>> vrValues;
766
    if (!_pGroupNode) {
767
        return vrValues;
768
    }
769

770
    std::string Name;
771

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

787
    return vrValues;
788
}
789

790
long ParameterGrp::GetInt(const char* Name, long lPreset) const
791
{
792
    if (!_pGroupNode) {
793
        return lPreset;
794
    }
795

796
    // check if Element in group
797
    DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
798
    // if not return preset
799
    if (!pcElem) {
800
        return lPreset;
801
    }
802
    // if yes check the value and return
803
    return atol(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str());
804
}
805

806
void ParameterGrp::SetInt(const char* Name, long lValue)
807
{
808
    char cBuf[256];
809
    sprintf(cBuf, "%li", lValue);
810
    _SetAttribute(ParamType::FCInt, Name, cBuf);
811
}
812

813
std::vector<long> ParameterGrp::GetInts(const char* sFilter) const
814
{
815
    std::vector<long> vrValues;
816
    if (!_pGroupNode) {
817
        return vrValues;
818
    }
819

820
    std::string Name;
821

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

833
    return vrValues;
834
}
835

836
std::vector<std::pair<std::string, long>> ParameterGrp::GetIntMap(const char* sFilter) const
837
{
838
    std::vector<std::pair<std::string, long>> vrValues;
839
    if (!_pGroupNode) {
840
        return vrValues;
841
    }
842

843
    std::string Name;
844

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

857
    return vrValues;
858
}
859

860
unsigned long ParameterGrp::GetUnsigned(const char* Name, unsigned long lPreset) const
861
{
862
    if (!_pGroupNode) {
863
        return lPreset;
864
    }
865

866
    // check if Element in group
867
    DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
868
    // if not return preset
869
    if (!pcElem) {
870
        return lPreset;
871
    }
872
    // if yes check the value and return
873
    return strtoul(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str(), nullptr, 10);
874
}
875

876
void ParameterGrp::SetUnsigned(const char* Name, unsigned long lValue)
877
{
878
    char cBuf[256];
879
    sprintf(cBuf, "%lu", lValue);
880
    _SetAttribute(ParamType::FCUInt, Name, cBuf);
881
}
882

883
std::vector<unsigned long> ParameterGrp::GetUnsigneds(const char* sFilter) const
884
{
885
    std::vector<unsigned long> vrValues;
886
    if (!_pGroupNode) {
887
        return vrValues;
888
    }
889

890
    std::string Name;
891

892
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
893
    while (pcTemp) {
894
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
895
        // check on filter condition
896
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
897
            vrValues.push_back(
898
                strtoul(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(),
899
                        nullptr,
900
                        10));
901
        }
902
        pcTemp = FindNextElement(pcTemp, "FCUInt");
903
    }
904

905
    return vrValues;
906
}
907

908
std::vector<std::pair<std::string, unsigned long>>
909
ParameterGrp::GetUnsignedMap(const char* sFilter) const
910
{
911
    std::vector<std::pair<std::string, unsigned long>> vrValues;
912
    if (!_pGroupNode) {
913
        return vrValues;
914
    }
915

916
    std::string Name;
917

918
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
919
    while (pcTemp) {
920
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
921
        // check on filter condition
922
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
923
            vrValues.emplace_back(
924
                Name,
925
                (strtoul(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(),
926
                         nullptr,
927
                         10)));
928
        }
929
        pcTemp = FindNextElement(pcTemp, "FCUInt");
930
    }
931

932
    return vrValues;
933
}
934

935
double ParameterGrp::GetFloat(const char* Name, double dPreset) const
936
{
937
    if (!_pGroupNode) {
938
        return dPreset;
939
    }
940

941
    // check if Element in group
942
    DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
943
    // if not return preset
944
    if (!pcElem) {
945
        return dPreset;
946
    }
947
    // if yes check the value and return
948
    return atof(StrX(pcElem->getAttribute(XStr("Value").unicodeForm())).c_str());
949
}
950

951
void ParameterGrp::SetFloat(const char* Name, double dValue)
952
{
953
    char cBuf[256];
954
    sprintf(cBuf, "%.12f", dValue);  // use %.12f instead of %f to handle values < 1.0e-6
955
    _SetAttribute(ParamType::FCFloat, Name, cBuf);
956
}
957

958
std::vector<double> ParameterGrp::GetFloats(const char* sFilter) const
959
{
960
    std::vector<double> vrValues;
961
    if (!_pGroupNode) {
962
        return vrValues;
963
    }
964

965
    std::string Name;
966

967
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
968
    while (pcTemp) {
969
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
970
        // check on filter condition
971
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
972
            vrValues.push_back(
973
                atof(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str()));
974
        }
975
        pcTemp = FindNextElement(pcTemp, "FCFloat");
976
    }
977

978
    return vrValues;
979
}
980

981
std::vector<std::pair<std::string, double>> ParameterGrp::GetFloatMap(const char* sFilter) const
982
{
983
    std::vector<std::pair<std::string, double>> vrValues;
984
    if (!_pGroupNode) {
985
        return vrValues;
986
    }
987

988
    std::string Name;
989

990
    DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
991
    while (pcTemp) {
992
        Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str();
993
        // check on filter condition
994
        if (!sFilter || Name.find(sFilter) != std::string::npos) {
995
            vrValues.emplace_back(
996
                Name,
997
                (atof(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str())));
998
        }
999
        pcTemp = FindNextElement(pcTemp, "FCFloat");
1000
    }
1001

1002
    return vrValues;
1003
}
1004

1005

1006
void ParameterGrp::SetASCII(const char* Name, const char* sValue)
1007
{
1008
    if (!_pGroupNode) {
1009
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1010
            FC_WARN("Setting attribute "
1011
                    << "FCText:" << Name << " in an orphan group " << _cName);
1012
        }
1013
        return;
1014
    }
1015
    if (_Clearing) {
1016
        if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1017
            FC_WARN("Adding attribute "
1018
                    << "FCText:" << Name << " while clearing " << GetPath());
1019
        }
1020
        return;
1021
    }
1022

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

1049
std::string ParameterGrp::GetASCII(const char* Name, const char* pPreset) const
1050
{
1051
    if (!_pGroupNode) {
1052
        return pPreset ? pPreset : "";
1053
    }
1054

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

1072
std::vector<std::string> ParameterGrp::GetASCIIs(const char* sFilter) const
1073
{
1074
    std::vector<std::string> vrValues;
1075
    if (!_pGroupNode) {
1076
        return vrValues;
1077
    }
1078

1079
    std::string Name;
1080

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

1098
    return vrValues;
1099
}
1100

1101
std::vector<std::pair<std::string, std::string>>
1102
ParameterGrp::GetASCIIMap(const char* sFilter) const
1103
{
1104
    std::vector<std::pair<std::string, std::string>> vrValues;
1105
    if (!_pGroupNode) {
1106
        return vrValues;
1107
    }
1108

1109
    std::string Name;
1110

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

1130
    return vrValues;
1131
}
1132

1133
//**************************************************************************
1134
// Access methods
1135

1136
void ParameterGrp::RemoveASCII(const char* Name)
1137
{
1138
    if (!_pGroupNode) {
1139
        return;
1140
    }
1141

1142
    // check if Element in group
1143
    DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
1144
    // if not return
1145
    if (!pcElem) {
1146
        return;
1147
    }
1148

1149
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1150
    node->release();
1151

1152
    // trigger observer
1153
    _Notify(ParamType::FCText, Name, nullptr);
1154
    Notify(Name);
1155
}
1156

1157
void ParameterGrp::RemoveBool(const char* Name)
1158
{
1159
    if (!_pGroupNode) {
1160
        return;
1161
    }
1162

1163
    // check if Element in group
1164
    DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
1165
    // if not return
1166
    if (!pcElem) {
1167
        return;
1168
    }
1169

1170
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1171
    node->release();
1172

1173
    // trigger observer
1174
    _Notify(ParamType::FCBool, Name, nullptr);
1175
    Notify(Name);
1176
}
1177

1178

1179
void ParameterGrp::RemoveFloat(const char* Name)
1180
{
1181
    if (!_pGroupNode) {
1182
        return;
1183
    }
1184

1185
    // check if Element in group
1186
    DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
1187
    // if not return
1188
    if (!pcElem) {
1189
        return;
1190
    }
1191

1192
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1193
    node->release();
1194

1195
    // trigger observer
1196
    _Notify(ParamType::FCFloat, Name, nullptr);
1197
    Notify(Name);
1198
}
1199

1200
void ParameterGrp::RemoveInt(const char* Name)
1201
{
1202
    if (!_pGroupNode) {
1203
        return;
1204
    }
1205

1206
    // check if Element in group
1207
    DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
1208
    // if not return
1209
    if (!pcElem) {
1210
        return;
1211
    }
1212

1213
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1214
    node->release();
1215

1216
    // trigger observer
1217
    _Notify(ParamType::FCInt, Name, nullptr);
1218
    Notify(Name);
1219
}
1220

1221
void ParameterGrp::RemoveUnsigned(const char* Name)
1222
{
1223
    if (!_pGroupNode) {
1224
        return;
1225
    }
1226

1227
    // check if Element in group
1228
    DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
1229
    // if not return
1230
    if (!pcElem) {
1231
        return;
1232
    }
1233

1234
    DOMNode* node = _pGroupNode->removeChild(pcElem);
1235
    node->release();
1236

1237
    // trigger observer
1238
    _Notify(ParamType::FCUInt, Name, nullptr);
1239
    Notify(Name);
1240
}
1241

1242
void ParameterGrp::RemoveGrp(const char* Name)
1243
{
1244
    if (!_pGroupNode) {
1245
        return;
1246
    }
1247

1248
    auto it = _GroupMap.find(Name);
1249
    if (it == _GroupMap.end()) {
1250
        return;
1251
    }
1252

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

1269
    // trigger observer
1270
    Notify(Name);
1271
}
1272

1273
bool ParameterGrp::RenameGrp(const char* OldName, const char* NewName)
1274
{
1275
    if (!_pGroupNode) {
1276
        return false;
1277
    }
1278

1279
    auto it = _GroupMap.find(OldName);
1280
    if (it == _GroupMap.end()) {
1281
        return false;
1282
    }
1283
    auto jt = _GroupMap.find(NewName);
1284
    if (jt != _GroupMap.end()) {
1285
        return false;
1286
    }
1287

1288
    // rename group handle
1289
    _GroupMap[NewName] = _GroupMap[OldName];
1290
    _GroupMap.erase(OldName);
1291
    _GroupMap[NewName]->_cName = NewName;
1292

1293
    // check if Element in group
1294
    DOMElement* pcElem = FindElement(_pGroupNode, "FCParamGroup", OldName);
1295
    if (pcElem) {
1296
        pcElem->setAttribute(XStr("Name").unicodeForm(), XStr(NewName).unicodeForm());
1297
    }
1298

1299
    _Notify(ParamType::FCGroup, NewName, OldName);
1300
    return true;
1301
}
1302

1303
void ParameterGrp::Clear(bool notify)
1304
{
1305
    if (!_pGroupNode) {
1306
        return;
1307
    }
1308

1309
    Base::StateLocker guard(_Clearing);
1310

1311
    // early trigger notification of group removal when all its children
1312
    // hierarchies are intact.
1313
    _Notify(ParamType::FCGroup, nullptr, nullptr);
1314

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

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

1354
    for (auto& v : params) {
1355
        _Notify(v.first, v.second.c_str(), nullptr);
1356
        if (notify) {
1357
            Notify(v.second.c_str());
1358
        }
1359
    }
1360

1361
    // trigger observer
1362
    Notify("");
1363
}
1364

1365
//**************************************************************************
1366
// Access methods
1367

1368
bool ParameterGrp::ShouldRemove() const
1369
{
1370
    if (this->getRefCount() > 1) {
1371
        return false;
1372
    }
1373

1374
    return std::all_of(_GroupMap.cbegin(), _GroupMap.cend(), [](const auto& it) {
1375
        return it.second->ShouldRemove();
1376
    });
1377
}
1378

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

1415
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
1416
ParameterGrp::FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Prev, const char* Type) const
1417
{
1418
    DOMNode* clChild = Prev;
1419
    if (!clChild) {
1420
        return nullptr;
1421
    }
1422

1423
    while ((clChild = clChild->getNextSibling()) != nullptr) {
1424
        if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
1425
            // the right node Type
1426
            if (!strcmp(Type, StrX(clChild->getNodeName()).c_str())) {
1427
                return static_cast<DOMElement*>(clChild);
1428
            }
1429
        }
1430
    }
1431
    return nullptr;
1432
}
1433

1434
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
1435
ParameterGrp::FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
1436
                                  const char* Type,
1437
                                  const char* Name)
1438
{
1439
    // first try to find it
1440
    DOMElement* pcElem = FindElement(Start, Type, Name);
1441
    if (pcElem) {
1442
        return pcElem;
1443
    }
1444

1445
    return CreateElement(Start, Type, Name);
1446
}
1447

1448
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*
1449
ParameterGrp::FindAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Node, const char* Name) const
1450
{
1451
    DOMNamedNodeMap* attr = Node->getAttributes();
1452
    if (attr) {
1453
        return attr->getNamedItem(XStr(Name).unicodeForm());
1454
    }
1455
    return nullptr;
1456
}
1457

1458
std::vector<std::pair<ParameterGrp::ParamType, std::string>>
1459
ParameterGrp::GetParameterNames(const char* sFilter) const
1460
{
1461
    std::vector<std::pair<ParameterGrp::ParamType, std::string>> res;
1462
    if (!_pGroupNode) {
1463
        return res;
1464
    }
1465

1466
    std::string Name;
1467

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

1488
void ParameterGrp::NotifyAll()
1489
{
1490
    // get all ints and notify
1491
    std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
1492
    for (const auto& it : IntMap) {
1493
        Notify(it.first.c_str());
1494
    }
1495

1496
    // get all booleans and notify
1497
    std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
1498
    for (const auto& it : BoolMap) {
1499
        Notify(it.first.c_str());
1500
    }
1501

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

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

1514
    // get all uints and notify
1515
    std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
1516
    for (const auto& it : UIntMap) {
1517
        Notify(it.first.c_str());
1518
    }
1519
}
1520

1521
void ParameterGrp::_Reset()
1522
{
1523
    _pGroupNode = nullptr;
1524
    for (auto& v : _GroupMap) {
1525
        v.second->_Reset();
1526
    }
1527
}
1528

1529
//**************************************************************************
1530
//**************************************************************************
1531
// ParameterSerializer
1532
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1533
ParameterSerializer::ParameterSerializer(const std::string& fn)
1534
    : filename(fn)
1535
{}
1536

1537
ParameterSerializer::~ParameterSerializer() = default;
1538

1539
void ParameterSerializer::SaveDocument(const ParameterManager& mgr)
1540
{
1541
    mgr.SaveDocument(filename.c_str());
1542
}
1543

1544
int ParameterSerializer::LoadDocument(ParameterManager& mgr)
1545
{
1546
    return mgr.LoadDocument(filename.c_str());
1547
}
1548

1549
bool ParameterSerializer::LoadOrCreateDocument(ParameterManager& mgr)
1550
{
1551
    return mgr.LoadOrCreateDocument(filename.c_str());
1552
}
1553

1554
//**************************************************************************
1555
//**************************************************************************
1556
// ParameterManager
1557
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1558

1559
static XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Auto;
1560

1561
//**************************************************************************
1562
// Construction/Destruction
1563

1564
/** Default construction
1565
 */
1566
ParameterManager::ParameterManager()
1567
{
1568
    _Manager = this;
1569

1570
    // initialize the XML system
1571
    Init();
1572

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

1614
    gDoNamespaces = false;
1615
    gDoSchema = false;
1616
    gSchemaFullChecking = false;
1617
    gDoCreate = true;
1618

1619
    gOutputEncoding = nullptr;
1620
    gMyEOLSequence = nullptr;
1621

1622
    gSplitCdataSections = true;
1623
    gDiscardDefaultContent = true;
1624
    gUseFilter = true;
1625
    gFormatPrettyPrint = true;
1626
}
1627

1628
/** Destruction
1629
 * complete destruction of the object
1630
 */
1631
ParameterManager::~ParameterManager()
1632
{
1633
    _Reset();
1634
    delete _pDocument;
1635
    delete paramSerializer;
1636
}
1637

1638
Base::Reference<ParameterManager> ParameterManager::Create()
1639
{
1640
    return {new ParameterManager()};
1641
}
1642

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

1666
void ParameterManager::Terminate()
1667
{
1668
    XMLTools::terminate();
1669
    XMLPlatformUtils::Terminate();
1670
}
1671

1672
//**************************************************************************
1673
// Serializer handling
1674

1675
void ParameterManager::SetSerializer(ParameterSerializer* ps)
1676
{
1677
    if (paramSerializer != ps) {
1678
        delete paramSerializer;
1679
    }
1680
    paramSerializer = ps;
1681
}
1682

1683
bool ParameterManager::HasSerializer() const
1684
{
1685
    return (paramSerializer != nullptr);
1686
}
1687

1688
const std::string& ParameterManager::GetSerializeFileName() const
1689
{
1690
    static const std::string _dummy;
1691
    return paramSerializer ? paramSerializer->GetFileName() : _dummy;
1692
}
1693

1694
int ParameterManager::LoadDocument()
1695
{
1696
    if (paramSerializer) {
1697
        return paramSerializer->LoadDocument(*this);
1698
    }
1699

1700
    return -1;
1701
}
1702

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

1709
    return false;
1710
}
1711

1712
void ParameterManager::SaveDocument() const
1713
{
1714
    if (paramSerializer) {
1715
        paramSerializer->SaveDocument(*this);
1716
    }
1717
}
1718

1719
//**************************************************************************
1720
// Document handling
1721

1722
bool ParameterManager::LoadOrCreateDocument(const char* sFileName)
1723
{
1724
    Base::FileInfo file(sFileName);
1725
    if (file.exists()) {
1726
        LoadDocument(sFileName);
1727
        return false;
1728
    }
1729

1730
    CreateDocument();
1731
    return true;
1732
}
1733

1734
int ParameterManager::LoadDocument(const char* sFileName)
1735
{
1736
    Base::FileInfo file(sFileName);
1737

1738
    try {
1739
#if defined(FC_OS_WIN32)
1740
        LocalFileInputSource inputSource(
1741
            reinterpret_cast<const XMLCh*>(file.toStdWString().c_str()));
1742
#else
1743
        LocalFileInputSource inputSource(XStr(file.filePath().c_str()).unicodeForm());
1744
#endif
1745
        return LoadDocument(inputSource);
1746
    }
1747
    catch (const Base::Exception& e) {
1748
        std::cerr << e.what() << std::endl;
1749
        throw;
1750
    }
1751
    catch (...) {
1752
        std::cerr << "An error occurred during parsing\n " << std::endl;
1753
        throw;
1754
    }
1755
}
1756

1757
int ParameterManager::LoadDocument(const XERCES_CPP_NAMESPACE_QUALIFIER InputSource& inputSource)
1758
{
1759
    //
1760
    //  Create our parser, then attach an error handler to the parser.
1761
    //  The parser will call back to methods of the ErrorHandler if it
1762
    //  discovers errors during the course of parsing the XML document.
1763
    //
1764
    XercesDOMParser* parser = new XercesDOMParser;
1765
    parser->setValidationScheme(gValScheme);
1766
    parser->setDoNamespaces(gDoNamespaces);
1767
    parser->setDoSchema(gDoSchema);
1768
    parser->setValidationSchemaFullChecking(gSchemaFullChecking);
1769
    parser->setCreateEntityReferenceNodes(gDoCreate);
1770

1771
    DOMTreeErrorReporter* errReporter = new DOMTreeErrorReporter();
1772
    parser->setErrorHandler(errReporter);
1773

1774
    //
1775
    //  Parse the XML file, catching any XML exceptions that might propagate
1776
    //  out of it.
1777
    //
1778
    bool errorsOccured = false;
1779
    try {
1780
        parser->parse(inputSource);
1781
    }
1782

1783
    catch (const XMLException& e) {
1784
        std::cerr << "An error occurred during parsing\n   Message: " << StrX(e.getMessage())
1785
                  << std::endl;
1786
        errorsOccured = true;
1787
    }
1788

1789
    catch (const DOMException& e) {
1790
        std::cerr << "A DOM error occurred during parsing\n   DOMException code: " << e.code
1791
                  << std::endl;
1792
        errorsOccured = true;
1793
    }
1794

1795
    catch (...) {
1796
        std::cerr << "An error occurred during parsing\n " << std::endl;
1797
        errorsOccured = true;
1798
    }
1799

1800
    if (errorsOccured) {
1801
        delete parser;
1802
        delete errReporter;
1803
        return 0;
1804
    }
1805

1806
    _pDocument = parser->adoptDocument();
1807
    delete parser;
1808
    delete errReporter;
1809

1810
    if (!_pDocument) {
1811
        throw XMLBaseException("Malformed Parameter document: Invalid document");
1812
    }
1813

1814
    DOMElement* rootElem = _pDocument->getDocumentElement();
1815
    if (!rootElem) {
1816
        throw XMLBaseException("Malformed Parameter document: Root group not found");
1817
    }
1818

1819
    _pGroupNode = FindElement(rootElem, "FCParamGroup", "Root");
1820

1821
    if (!_pGroupNode) {
1822
        throw XMLBaseException("Malformed Parameter document: Root group not found");
1823
    }
1824

1825
    return 1;
1826
}
1827

1828
void ParameterManager::SaveDocument(const char* sFileName) const
1829
{
1830
    Base::FileInfo file(sFileName);
1831

1832
    try {
1833
        //
1834
        // Plug in a format target to receive the resultant
1835
        // XML stream from the serializer.
1836
        //
1837
        // LocalFileFormatTarget prints the resultant XML stream
1838
        // to a file once it receives any thing from the serializer.
1839
        //
1840
#if defined(FC_OS_WIN32)
1841
        XMLFormatTarget* myFormTarget =
1842
            new LocalFileFormatTarget(reinterpret_cast<const XMLCh*>(file.toStdWString().c_str()));
1843
#else
1844
        XMLFormatTarget* myFormTarget = new LocalFileFormatTarget(file.filePath().c_str());
1845
#endif
1846
        SaveDocument(myFormTarget);
1847
        delete myFormTarget;
1848
    }
1849
    catch (XMLException& e) {
1850
        std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
1851
                  << StrX(e.getMessage()) << std::endl;
1852
    }
1853
}
1854

1855
void ParameterManager::SaveDocument(XMLFormatTarget* pFormatTarget) const
1856
{
1857
    try {
1858
        std::unique_ptr<DOMPrintFilter> myFilter;
1859
        std::unique_ptr<DOMErrorHandler> myErrorHandler;
1860

1861
        // get a serializer, an instance of DOMWriter
1862
        XMLCh tempStr[100];
1863
        XMLString::transcode("LS", tempStr, 99);
1864
        DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
1865
        DOMLSSerializer* theSerializer =
1866
            static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
1867

1868
        // set user specified end of line sequence and output encoding
1869
        theSerializer->setNewLine(gMyEOLSequence);
1870

1871

1872
        //
1873
        // do the serialization through DOMWriter::writeNode();
1874
        //
1875
        if (_pDocument) {
1876
            DOMLSOutput* theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
1877
            theOutput->setEncoding(gOutputEncoding);
1878

1879
            if (gUseFilter) {
1880
                myFilter = std::make_unique<DOMPrintFilter>(
1881
                    DOMNodeFilter::SHOW_ELEMENT | DOMNodeFilter::SHOW_ATTRIBUTE
1882
                    | DOMNodeFilter::SHOW_DOCUMENT_TYPE | DOMNodeFilter::SHOW_TEXT);
1883
                theSerializer->setFilter(myFilter.get());
1884
            }
1885

1886
            // plug in user's own error handler
1887
            myErrorHandler = std::make_unique<DOMPrintErrorHandler>();
1888
            DOMConfiguration* config = theSerializer->getDomConfig();
1889
            config->setParameter(XMLUni::fgDOMErrorHandler, myErrorHandler.get());
1890

1891
            // set feature if the serializer supports the feature/mode
1892
            if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections)) {
1893
                config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections);
1894
            }
1895

1896
            if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent,
1897
                                        gDiscardDefaultContent)) {
1898
                config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, gDiscardDefaultContent);
1899
            }
1900

1901
            if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint)) {
1902
                config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint);
1903
            }
1904

1905
            theOutput->setByteStream(pFormatTarget);
1906
            theSerializer->write(_pDocument, theOutput);
1907

1908
            theOutput->release();
1909
        }
1910

1911
        theSerializer->release();
1912
    }
1913
    catch (XMLException& e) {
1914
        std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
1915
                  << StrX(e.getMessage()) << std::endl;
1916
    }
1917
}
1918

1919
void ParameterManager::CreateDocument()
1920
{
1921
    // creating a document from screatch
1922
    DOMImplementation* impl =
1923
        DOMImplementationRegistry::getDOMImplementation(XStr("Core").unicodeForm());
1924
    delete _pDocument;
1925
    _pDocument = impl->createDocument(nullptr,  // root element namespace URI.
1926
                                      XStr("FCParameters").unicodeForm(),  // root element name
1927
                                      nullptr);  // document type object (DTD).
1928

1929
    // creating the node for the root group
1930
    DOMElement* rootElem = _pDocument->getDocumentElement();
1931
    _pGroupNode = _pDocument->createElement(XStr("FCParamGroup").unicodeForm());
1932
    _pGroupNode->setAttribute(XStr("Name").unicodeForm(), XStr("Root").unicodeForm());
1933
    rootElem->appendChild(_pGroupNode);
1934
}
1935

1936
void ParameterManager::CheckDocument() const
1937
{
1938
    if (!_pDocument) {
1939
        return;
1940
    }
1941

1942
    try {
1943
        //
1944
        // Plug in a format target to receive the resultant
1945
        // XML stream from the serializer.
1946
        //
1947
        // LocalFileFormatTarget prints the resultant XML stream
1948
        // to a file once it receives any thing from the serializer.
1949
        //
1950
        MemBufFormatTarget myFormTarget;
1951
        SaveDocument(&myFormTarget);
1952

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

1957
        // Either load the XSD file from disk or use the built-in string
1958
        // const char* xsdFile = "...";
1959
        std::string xsdStr(xmlSchemeString);
1960
        MemBufInputSource xsdFile(reinterpret_cast<const XMLByte*>(xsdStr.c_str()),
1961
                                  xsdStr.size(),
1962
                                  "Parameter.xsd");
1963

1964
        // See
1965
        // http://apache-xml-project.6118.n7.nabble.com/validating-xml-with-xsd-schema-td17515.html
1966
        //
1967
        XercesDOMParser parser;
1968
        Grammar* grammar = parser.loadGrammar(xsdFile, Grammar::SchemaGrammarType, true);
1969
        if (!grammar) {
1970
            Base::Console().Error("Grammar file cannot be loaded.\n");
1971
            return;
1972
        }
1973

1974
        parser.setExternalNoNamespaceSchemaLocation("Parameter.xsd");
1975
        // parser.setExitOnFirstFatalError(true);
1976
        // parser.setValidationConstraintFatal(true);
1977
        parser.cacheGrammarFromParse(true);
1978
        parser.setValidationScheme(XercesDOMParser::Val_Auto);
1979
        parser.setDoNamespaces(true);
1980
        parser.setDoSchema(true);
1981

1982
        DOMTreeErrorReporter errHandler;
1983
        parser.setErrorHandler(&errHandler);
1984
        parser.parse(xmlFile);
1985

1986
        if (parser.getErrorCount() > 0) {
1987
            Base::Console().Error("Unexpected XML structure detected: %zu errors\n",
1988
                                  parser.getErrorCount());
1989
        }
1990
    }
1991
    catch (XMLException& e) {
1992
        std::cerr << "An error occurred while checking document. Msg is:" << std::endl
1993
                  << StrX(e.getMessage()) << std::endl;
1994
    }
1995
}
1996

1997

1998
//**************************************************************************
1999
//**************************************************************************
2000
// DOMTreeErrorReporter
2001
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2002

2003
void DOMTreeErrorReporter::warning(const SAXParseException& /*exc*/)
2004
{
2005
    //
2006
    // Ignore all warnings.
2007
    //
2008
}
2009

2010
void DOMTreeErrorReporter::error(const SAXParseException& toCatch)
2011
{
2012
    fSawErrors = true;
2013
    std::cerr << "Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
2014
              << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
2015
              << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
2016
}
2017

2018
void DOMTreeErrorReporter::fatalError(const SAXParseException& toCatch)
2019
{
2020
    fSawErrors = true;
2021
    std::cerr << "Fatal Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
2022
              << toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
2023
              << "\n   Message: " << StrX(toCatch.getMessage()) << std::endl;
2024
}
2025

2026
void DOMTreeErrorReporter::resetErrors()
2027
{
2028
    // No-op in this case
2029
}
2030

2031

2032
//**************************************************************************
2033
//**************************************************************************
2034
// DOMPrintFilter
2035
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2036
DOMPrintFilter::DOMPrintFilter(ShowType whatToShow)
2037
    : fWhatToShow(whatToShow)
2038
{}
2039

2040
DOMPrintFilter::FilterAction DOMPrintFilter::acceptNode(const DOMNode* node) const
2041
{
2042
    if (XMLString::compareString(node->getNodeName(), XStr("FCParameters").unicodeForm()) == 0) {
2043
        // This node is supposed to have a single FCParamGroup and two text nodes.
2044
        // Over time it can happen that the text nodes collect extra newlines.
2045
        const DOMNodeList* children = node->getChildNodes();
2046
        for (XMLSize_t i = 0; i < children->getLength(); i++) {
2047
            DOMNode* child = children->item(i);
2048
            if (child->getNodeType() == DOMNode::TEXT_NODE) {
2049
                child->setNodeValue(XStr("\n").unicodeForm());
2050
            }
2051
        }
2052
    }
2053

2054
    switch (node->getNodeType()) {
2055
        case DOMNode::TEXT_NODE: {
2056
            // Filter out text element if it is under a group node. Note text xml
2057
            // element is plain text in between tags, and we do not store any text
2058
            // there.
2059
            auto parent = node->getParentNode();
2060
            if (parent
2061
                && XMLString::compareString(parent->getNodeName(),
2062
                                            XStr("FCParamGroup").unicodeForm())
2063
                    == 0) {
2064
                return DOMNodeFilter::FILTER_REJECT;
2065
            }
2066
            return DOMNodeFilter::FILTER_ACCEPT;
2067
        }
2068
        case DOMNode::DOCUMENT_TYPE_NODE:
2069
        case DOMNode::DOCUMENT_NODE: {
2070
            return DOMNodeFilter::FILTER_REJECT;  // no effect
2071
        }
2072
        default: {
2073
            return DOMNodeFilter::FILTER_ACCEPT;
2074
        }
2075
    }
2076
}
2077

2078
//**************************************************************************
2079
//**************************************************************************
2080
// DOMPrintErrorHandler
2081
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2082

2083

2084
bool DOMPrintErrorHandler::handleError(const DOMError& domError)
2085
{
2086
    // Display whatever error message passed from the serializer
2087
    char* msg = XMLString::transcode(domError.getMessage());
2088
    std::cout << msg << std::endl;
2089
    XMLString::release(&msg);
2090

2091
    // Instructs the serializer to continue serialization if possible.
2092
    return true;
2093
}
2094

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

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

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

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