"Professional CMake" book suggest the following: "Targets should build successfully with or without compiler support for precompiled headers. It should be considered an optimization, not a requirement. In particular, do not explicitly include a precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically generated precompile header on the compiler command line instead. This is more portable across the major compilers and is likely to be easier to maintain. It will also avoid warnings being generated from certain code checking tools like iwyu (include what you use)." Therefore, removed the "#include <PreCompiled.h>" from sources, also there is no need for the "#ifdef _PreComp_" anymore
2220 lines
68 KiB
C++
2220 lines
68 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Library General Public License (LGPL) *
|
|
* as published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* for detail see the LICENCE text file. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with FreeCAD; if not, write to the Free Software *
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
* USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <xercesc/dom/DOM.hpp>
|
|
#include <xercesc/framework/LocalFileFormatTarget.hpp>
|
|
#include <xercesc/framework/LocalFileInputSource.hpp>
|
|
#include <xercesc/framework/MemBufFormatTarget.hpp>
|
|
#include <xercesc/framework/MemBufInputSource.hpp>
|
|
#include <xercesc/parsers/XercesDOMParser.hpp>
|
|
#include <xercesc/sax/EntityResolver.hpp>
|
|
#include <xercesc/sax/ErrorHandler.hpp>
|
|
#include <xercesc/sax/SAXParseException.hpp>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include <QFileInfo>
|
|
#include <QLockFile>
|
|
#include <QDir>
|
|
|
|
#include <FCConfig.h>
|
|
|
|
#ifdef FC_OS_LINUX
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <fmt/printf.h>
|
|
|
|
#include "Parameter.h"
|
|
#include "Parameter.inl"
|
|
#include "Console.h"
|
|
#include "Exception.h"
|
|
#include "Tools.h"
|
|
|
|
FC_LOG_LEVEL_INIT("Parameter", true, true)
|
|
|
|
#ifndef XERCES_CPP_NAMESPACE_BEGIN
|
|
#define XERCES_CPP_NAMESPACE_QUALIFIER
|
|
using namespace XERCES_CPP_NAMESPACE;
|
|
#else
|
|
XERCES_CPP_NAMESPACE_USE
|
|
#endif
|
|
using namespace Base;
|
|
|
|
|
|
#include "XMLTools.h"
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// private classes declaration:
|
|
// - DOMTreeErrorReporter
|
|
// - StrX
|
|
// - DOMPrintFilter
|
|
// - DOMPrintErrorHandler
|
|
// - XStr
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
class DOMTreeErrorReporter: public ErrorHandler
|
|
{
|
|
public:
|
|
// -----------------------------------------------------------------------
|
|
// Implementation of the error handler interface
|
|
// -----------------------------------------------------------------------
|
|
void warning(const SAXParseException& toCatch) override;
|
|
void error(const SAXParseException& toCatch) override;
|
|
void fatalError(const SAXParseException& toCatch) override;
|
|
void resetErrors() override;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Getter methods
|
|
// -----------------------------------------------------------------------
|
|
bool getSawErrors() const;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Private data members
|
|
//
|
|
// fSawErrors
|
|
// This is set if we get any errors, and is queryable via a getter
|
|
// method. Its used by the main code to suppress output if there are
|
|
// errors.
|
|
// -----------------------------------------------------------------------
|
|
bool fSawErrors {false};
|
|
};
|
|
|
|
|
|
class DOMPrintFilter: public DOMLSSerializerFilter
|
|
{
|
|
public:
|
|
/** @name Constructors */
|
|
explicit DOMPrintFilter(ShowType whatToShow = DOMNodeFilter::SHOW_ALL);
|
|
//@{
|
|
|
|
/** @name Destructors */
|
|
~DOMPrintFilter() override = default;
|
|
//@{
|
|
|
|
/** @ interface from DOMWriterFilter */
|
|
FilterAction acceptNode(const XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* node) const override;
|
|
//@{
|
|
|
|
ShowType getWhatToShow() const override
|
|
{
|
|
return fWhatToShow;
|
|
}
|
|
|
|
// unimplemented copy ctor and assignment operator
|
|
DOMPrintFilter(const DOMPrintFilter&) = delete;
|
|
DOMPrintFilter(DOMPrintFilter&&) = delete;
|
|
DOMPrintFilter& operator=(const DOMPrintFilter&) = delete;
|
|
DOMPrintFilter& operator=(DOMPrintFilter&&) = delete;
|
|
|
|
ShowType fWhatToShow;
|
|
};
|
|
|
|
class DOMPrintErrorHandler: public DOMErrorHandler
|
|
{
|
|
public:
|
|
DOMPrintErrorHandler() = default;
|
|
~DOMPrintErrorHandler() override = default;
|
|
|
|
/** @name The error handler interface */
|
|
bool handleError(const DOMError& domError) override;
|
|
void resetErrors()
|
|
{}
|
|
|
|
/* Unimplemented constructors and operators */
|
|
DOMPrintErrorHandler(const DOMPrintErrorHandler&) = delete;
|
|
DOMPrintErrorHandler(DOMPrintErrorHandler&&) = delete;
|
|
void operator=(const DOMPrintErrorHandler&) = delete;
|
|
void operator=(DOMPrintErrorHandler&&) = delete;
|
|
};
|
|
|
|
|
|
inline bool DOMTreeErrorReporter::getSawErrors() const
|
|
{
|
|
return fSawErrors;
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// ParameterManager
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
/** Default construction
|
|
*/
|
|
ParameterGrp::ParameterGrp(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* GroupNode,
|
|
const char* sName,
|
|
ParameterGrp* Parent)
|
|
: _pGroupNode(GroupNode)
|
|
, _Parent(Parent)
|
|
{
|
|
if (sName) {
|
|
_cName = sName;
|
|
}
|
|
if (_Parent) {
|
|
_Manager = _Parent->_Manager;
|
|
}
|
|
}
|
|
|
|
|
|
/** Destruction
|
|
* complete destruction of the object
|
|
*/
|
|
ParameterGrp::~ParameterGrp()
|
|
{
|
|
for (auto& v : _GroupMap) {
|
|
v.second->_Parent = nullptr;
|
|
v.second->_Manager = nullptr;
|
|
}
|
|
if (_Detached && _pGroupNode) {
|
|
_pGroupNode->release();
|
|
}
|
|
}
|
|
|
|
//**************************************************************************
|
|
// Access methods
|
|
|
|
void ParameterGrp::copyTo(const Base::Reference<ParameterGrp>& Grp)
|
|
{
|
|
if (Grp == this) {
|
|
return;
|
|
}
|
|
|
|
// delete previous content
|
|
Grp->Clear(true);
|
|
|
|
// copy all
|
|
insertTo(Grp);
|
|
}
|
|
|
|
void ParameterGrp::insertTo(const Base::Reference<ParameterGrp>& Grp)
|
|
{
|
|
if (Grp == this) {
|
|
return;
|
|
}
|
|
|
|
// copy group
|
|
std::vector<Base::Reference<ParameterGrp>> Grps = GetGroups();
|
|
std::vector<Base::Reference<ParameterGrp>>::iterator It1;
|
|
for (It1 = Grps.begin(); It1 != Grps.end(); ++It1) {
|
|
(*It1)->insertTo(Grp->GetGroup((*It1)->GetGroupName()));
|
|
}
|
|
|
|
// copy strings
|
|
std::vector<std::pair<std::string, std::string>> StringMap = GetASCIIMap();
|
|
std::vector<std::pair<std::string, std::string>>::iterator It2;
|
|
for (It2 = StringMap.begin(); It2 != StringMap.end(); ++It2) {
|
|
Grp->SetASCII(It2->first.c_str(), It2->second.c_str());
|
|
}
|
|
|
|
// copy bool
|
|
std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
|
|
std::vector<std::pair<std::string, bool>>::iterator It3;
|
|
for (It3 = BoolMap.begin(); It3 != BoolMap.end(); ++It3) {
|
|
Grp->SetBool(It3->first.c_str(), It3->second);
|
|
}
|
|
|
|
// copy int
|
|
std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
|
|
std::vector<std::pair<std::string, long>>::iterator It4;
|
|
for (It4 = IntMap.begin(); It4 != IntMap.end(); ++It4) {
|
|
Grp->SetInt(It4->first.c_str(), It4->second);
|
|
}
|
|
|
|
// copy float
|
|
std::vector<std::pair<std::string, double>> FloatMap = GetFloatMap();
|
|
std::vector<std::pair<std::string, double>>::iterator It5;
|
|
for (It5 = FloatMap.begin(); It5 != FloatMap.end(); ++It5) {
|
|
Grp->SetFloat(It5->first.c_str(), It5->second);
|
|
}
|
|
|
|
// copy uint
|
|
std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
|
|
std::vector<std::pair<std::string, unsigned long>>::iterator It6;
|
|
for (It6 = UIntMap.begin(); It6 != UIntMap.end(); ++It6) {
|
|
Grp->SetUnsigned(It6->first.c_str(), It6->second);
|
|
}
|
|
}
|
|
|
|
void ParameterGrp::exportTo(const char* FileName)
|
|
{
|
|
auto Mngr = ParameterManager::Create();
|
|
|
|
Mngr->CreateDocument();
|
|
|
|
// copy all into the new document
|
|
insertTo(Base::Reference<ParameterGrp>(Mngr));
|
|
|
|
Mngr->SaveDocument(FileName);
|
|
}
|
|
|
|
void ParameterGrp::importFrom(const char* FileName)
|
|
{
|
|
auto Mngr = ParameterManager::Create();
|
|
|
|
if (Mngr->LoadDocument(FileName) != 1) {
|
|
throw FileException("ParameterGrp::import() cannot load document", FileName);
|
|
}
|
|
|
|
Mngr->copyTo(Base::Reference<ParameterGrp>(this));
|
|
}
|
|
|
|
void ParameterGrp::insert(const char* FileName)
|
|
{
|
|
auto Mngr = ParameterManager::Create();
|
|
|
|
if (Mngr->LoadDocument(FileName) != 1) {
|
|
throw FileException("ParameterGrp::import() cannot load document", FileName);
|
|
}
|
|
|
|
Mngr->insertTo(Base::Reference<ParameterGrp>(this));
|
|
}
|
|
|
|
void ParameterGrp::revert(const char* FileName)
|
|
{
|
|
auto Mngr = ParameterManager::Create();
|
|
|
|
if (Mngr->LoadDocument(FileName) != 1) {
|
|
throw FileException("ParameterGrp::revert() cannot load document", FileName);
|
|
}
|
|
|
|
revert(Base::Reference<ParameterGrp>(Mngr));
|
|
}
|
|
|
|
void ParameterGrp::revert(const Base::Reference<ParameterGrp>& Grp)
|
|
{
|
|
if (Grp == this) {
|
|
return;
|
|
}
|
|
|
|
for (auto& grp : Grp->GetGroups()) {
|
|
if (HasGroup(grp->GetGroupName())) {
|
|
GetGroup(grp->GetGroupName())->revert(grp);
|
|
}
|
|
}
|
|
|
|
for (const auto& v : Grp->GetASCIIMap()) {
|
|
if (GetASCII(v.first.c_str(), v.second.c_str()) == v.second) {
|
|
RemoveASCII(v.first.c_str());
|
|
}
|
|
}
|
|
|
|
for (const auto& v : Grp->GetBoolMap()) {
|
|
if (GetBool(v.first.c_str(), v.second) == v.second) {
|
|
RemoveBool(v.first.c_str());
|
|
}
|
|
}
|
|
|
|
for (const auto& v : Grp->GetIntMap()) {
|
|
if (GetInt(v.first.c_str(), v.second) == v.second) {
|
|
RemoveInt(v.first.c_str());
|
|
}
|
|
}
|
|
|
|
for (const auto& v : Grp->GetUnsignedMap()) {
|
|
if (GetUnsigned(v.first.c_str(), v.second) == v.second) {
|
|
RemoveUnsigned(v.first.c_str());
|
|
}
|
|
}
|
|
|
|
for (const auto& v : Grp->GetFloatMap()) {
|
|
if (GetFloat(v.first.c_str(), v.second) == v.second) {
|
|
RemoveFloat(v.first.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
|
|
ParameterGrp::CreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
|
|
const char* Type,
|
|
const char* Name)
|
|
{
|
|
if (XMLString::compareString(Start->getNodeName(), XStrLiteral("FCParamGroup").unicodeForm())
|
|
!= 0
|
|
&& XMLString::compareString(Start->getNodeName(), XStrLiteral("FCParameters").unicodeForm())
|
|
!= 0) {
|
|
Base::Console().warning("CreateElement: %s cannot have the element %s of type %s\n",
|
|
StrX(Start->getNodeName()).c_str(),
|
|
Name,
|
|
Type);
|
|
return nullptr;
|
|
}
|
|
|
|
if (_Detached && _Parent) {
|
|
// re-attach the group
|
|
_Parent->_GetGroup(_cName.c_str());
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* pDocument = Start->getOwnerDocument();
|
|
|
|
auto pcElem = pDocument->createElement(XStr(Type).unicodeForm());
|
|
pcElem->setAttribute(XStrLiteral("Name").unicodeForm(), XStr(Name).unicodeForm());
|
|
Start->appendChild(pcElem);
|
|
|
|
return pcElem;
|
|
}
|
|
|
|
Base::Reference<ParameterGrp> ParameterGrp::GetGroup(const char* Name)
|
|
{
|
|
if (!Name) {
|
|
throw Base::ValueError("Empty group name");
|
|
}
|
|
|
|
Base::Reference<ParameterGrp> hGrp = this;
|
|
std::vector<std::string> tokens;
|
|
boost::split(tokens, Name, boost::is_any_of("/"));
|
|
for (auto& token : tokens) {
|
|
boost::trim(token);
|
|
if (token.empty()) {
|
|
continue;
|
|
}
|
|
hGrp = hGrp->_GetGroup(token.c_str());
|
|
if (!hGrp) {
|
|
// The group is clearing. Return some dummy group to avoid caller
|
|
// crashing for backward compatibility.
|
|
hGrp = new ParameterGrp();
|
|
hGrp->_cName = Name;
|
|
break;
|
|
}
|
|
}
|
|
if (hGrp == this) {
|
|
throw Base::ValueError("Empty group name");
|
|
}
|
|
return hGrp;
|
|
}
|
|
|
|
Base::Reference<ParameterGrp> ParameterGrp::_GetGroup(const char* Name)
|
|
{
|
|
Base::Reference<ParameterGrp> rParamGrp;
|
|
if (!_pGroupNode) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Adding group " << Name << " in an orphan group " << _cName);
|
|
}
|
|
return rParamGrp;
|
|
}
|
|
if (_Clearing) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Adding group " << Name << " while clearing " << GetPath());
|
|
}
|
|
return rParamGrp;
|
|
}
|
|
|
|
DOMElement* pcTemp {};
|
|
|
|
// search if Group node already there
|
|
pcTemp = FindElement(_pGroupNode, "FCParamGroup", Name);
|
|
|
|
// already created?
|
|
if (!(rParamGrp = _GroupMap[Name]).isValid()) {
|
|
if (!pcTemp) {
|
|
pcTemp = CreateElement(_pGroupNode, "FCParamGroup", Name);
|
|
}
|
|
// create and register handle
|
|
rParamGrp = Base::Reference<ParameterGrp>(new ParameterGrp(pcTemp, Name, this));
|
|
_GroupMap[Name] = rParamGrp;
|
|
}
|
|
else if (!pcTemp) {
|
|
_pGroupNode->appendChild(rParamGrp->_pGroupNode);
|
|
rParamGrp->_Detached = false;
|
|
if (this->_Detached && this->_Parent) {
|
|
// Re-attach the group. Note that this may fail if the parent is
|
|
// clearing. That's why we check this->_Detached below.
|
|
this->_Parent->_GetGroup(_cName.c_str());
|
|
}
|
|
}
|
|
|
|
if (!pcTemp && !this->_Detached) {
|
|
_Notify(ParamType::FCGroup, Name, Name);
|
|
}
|
|
|
|
return rParamGrp;
|
|
}
|
|
|
|
std::string ParameterGrp::GetPath() const
|
|
{
|
|
std::string path;
|
|
if (_Parent && _Parent != _Manager) {
|
|
path = _Parent->GetPath();
|
|
}
|
|
if (!path.empty() && !_cName.empty()) {
|
|
path += "/";
|
|
}
|
|
path += _cName;
|
|
return path;
|
|
}
|
|
|
|
std::vector<Base::Reference<ParameterGrp>> ParameterGrp::GetGroups()
|
|
{
|
|
Base::Reference<ParameterGrp> rParamGrp;
|
|
std::vector<Base::Reference<ParameterGrp>> vrParamGrp;
|
|
|
|
if (!_pGroupNode) {
|
|
return vrParamGrp;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCParamGroup");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttributes()
|
|
->getNamedItem(XStrLiteral("Name").unicodeForm())
|
|
->getNodeValue())
|
|
.c_str();
|
|
// already created?
|
|
if (!(rParamGrp = _GroupMap[Name]).isValid()) {
|
|
rParamGrp = Base::Reference<ParameterGrp>(new ParameterGrp(pcTemp, Name.c_str(), this));
|
|
_GroupMap[Name] = rParamGrp;
|
|
}
|
|
vrParamGrp.push_back(rParamGrp);
|
|
// go to next
|
|
pcTemp = FindNextElement(pcTemp, "FCParamGroup");
|
|
}
|
|
|
|
return vrParamGrp;
|
|
}
|
|
|
|
/// test if this group is empty
|
|
bool ParameterGrp::IsEmpty() const
|
|
{
|
|
return !(_pGroupNode && _pGroupNode->getFirstChild());
|
|
}
|
|
|
|
/// test if a special sub group is in this group
|
|
bool ParameterGrp::HasGroup(const char* Name) const
|
|
{
|
|
if (_GroupMap.find(Name) != _GroupMap.end()) {
|
|
return true;
|
|
}
|
|
|
|
if (_pGroupNode && FindElement(_pGroupNode, "FCParamGroup", Name) != nullptr) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const char* ParameterGrp::TypeName(ParamType Type)
|
|
{
|
|
switch (Type) {
|
|
case ParamType::FCBool:
|
|
return "FCBool";
|
|
case ParamType::FCInt:
|
|
return "FCInt";
|
|
case ParamType::FCUInt:
|
|
return "FCUInt";
|
|
case ParamType::FCText:
|
|
return "FCText";
|
|
case ParamType::FCFloat:
|
|
return "FCFloat";
|
|
case ParamType::FCGroup:
|
|
return "FCParamGroup";
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ParameterGrp::ParamType ParameterGrp::TypeValue(const char* Name)
|
|
{
|
|
if (Name) {
|
|
if (boost::equals(Name, "FCBool")) {
|
|
return ParamType::FCBool;
|
|
}
|
|
if (boost::equals(Name, "FCInt")) {
|
|
return ParamType::FCInt;
|
|
}
|
|
if (boost::equals(Name, "FCUInt")) {
|
|
return ParamType::FCUInt;
|
|
}
|
|
if (boost::equals(Name, "FCText")) {
|
|
return ParamType::FCText;
|
|
}
|
|
if (boost::equals(Name, "FCFloat")) {
|
|
return ParamType::FCFloat;
|
|
}
|
|
if (boost::equals(Name, "FCParamGroup")) {
|
|
return ParamType::FCGroup;
|
|
}
|
|
}
|
|
return ParamType::FCInvalid;
|
|
}
|
|
|
|
void ParameterGrp::SetAttribute(ParamType Type, const char* Name, const char* Value)
|
|
{
|
|
switch (Type) {
|
|
case ParamType::FCBool:
|
|
case ParamType::FCInt:
|
|
case ParamType::FCUInt:
|
|
case ParamType::FCFloat:
|
|
return _SetAttribute(Type, Name, Value);
|
|
case ParamType::FCText:
|
|
return SetASCII(Name, Value);
|
|
case ParamType::FCGroup:
|
|
RenameGrp(Name, Value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParameterGrp::RemoveAttribute(ParamType Type, const char* Name)
|
|
{
|
|
switch (Type) {
|
|
case ParamType::FCBool:
|
|
return RemoveBool(Name);
|
|
case ParamType::FCInt:
|
|
return RemoveInt(Name);
|
|
case ParamType::FCUInt:
|
|
return RemoveUnsigned(Name);
|
|
case ParamType::FCText:
|
|
return RemoveASCII(Name);
|
|
case ParamType::FCFloat:
|
|
return RemoveFloat(Name);
|
|
case ParamType::FCGroup:
|
|
return RemoveGrp(Name);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* ParameterGrp::GetAttribute(ParamType Type,
|
|
const char* Name,
|
|
std::string& Value,
|
|
const char* Default) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return Default;
|
|
}
|
|
|
|
const char* T = TypeName(Type);
|
|
if (!T) {
|
|
return Default;
|
|
}
|
|
|
|
DOMElement* pcElem = FindElement(_pGroupNode, T, Name);
|
|
if (!pcElem) {
|
|
return Default;
|
|
}
|
|
|
|
if (Type == ParamType::FCText) {
|
|
Value = GetASCII(Name, Default);
|
|
}
|
|
else if (Type != ParamType::FCGroup) {
|
|
Value = StrX(pcElem->getAttribute(XStrLiteral("Value").unicodeForm())).c_str();
|
|
}
|
|
return Value.c_str();
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>>
|
|
ParameterGrp::GetAttributeMap(ParamType Type, const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, std::string>> res;
|
|
if (!_pGroupNode) {
|
|
return res;
|
|
}
|
|
|
|
const char* T = TypeName(Type);
|
|
if (!T) {
|
|
return res;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, T);
|
|
while (pcTemp) {
|
|
Name = StrX(static_cast<DOMElement*>(pcTemp)
|
|
->getAttributes()
|
|
->getNamedItem(XStrLiteral("Name").unicodeForm())
|
|
->getNodeValue())
|
|
.c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
if (Type == ParamType::FCGroup) {
|
|
res.emplace_back(Name, std::string());
|
|
}
|
|
else {
|
|
res.emplace_back(Name,
|
|
StrX(static_cast<DOMElement*>(pcTemp)->getAttribute(
|
|
XStrLiteral("Value").unicodeForm()))
|
|
.c_str());
|
|
}
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, T);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void ParameterGrp::_Notify(ParamType Type, const char* Name, const char* Value)
|
|
{
|
|
if (_Manager) {
|
|
_Manager->signalParamChanged(this, Type, Name, Value);
|
|
}
|
|
}
|
|
|
|
void ParameterGrp::_SetAttribute(ParamType T, const char* Name, const char* Value)
|
|
{
|
|
const char* Type = TypeName(T);
|
|
if (!Type) {
|
|
return;
|
|
}
|
|
if (!_pGroupNode) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Setting attribute " << Type << ":" << Name << " in an orphan group "
|
|
<< _cName);
|
|
}
|
|
return;
|
|
}
|
|
if (_Clearing) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Adding attribute " << Type << ":" << Name << " while clearing " << GetPath());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// find or create the Element
|
|
DOMElement* pcElem = FindOrCreateElement(_pGroupNode, Type, Name);
|
|
if (pcElem) {
|
|
XStr attr("Value");
|
|
// set the value only if different
|
|
if (strcmp(StrX(pcElem->getAttribute(attr.unicodeForm())).c_str(), Value) != 0) {
|
|
pcElem->setAttribute(attr.unicodeForm(), XStr(Value).unicodeForm());
|
|
// trigger observer
|
|
_Notify(T, Name, Value);
|
|
}
|
|
// For backward compatibility, old observer gets notified regardless of
|
|
// value changes or not.
|
|
Notify(Name);
|
|
}
|
|
}
|
|
|
|
bool ParameterGrp::GetBool(const char* Name, bool bPreset) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return bPreset;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
|
|
// if not return preset
|
|
if (!pcElem) {
|
|
return bPreset;
|
|
}
|
|
|
|
// if yes check the value and return
|
|
return (strcmp(StrX(pcElem->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(), "1")
|
|
== 0);
|
|
}
|
|
|
|
void ParameterGrp::SetBool(const char* Name, bool bValue)
|
|
{
|
|
_SetAttribute(ParamType::FCBool, Name, bValue ? "1" : "0");
|
|
}
|
|
|
|
std::vector<bool> ParameterGrp::GetBools(const char* sFilter) const
|
|
{
|
|
std::vector<bool> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
if (strcmp(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(), "1")
|
|
!= 0) {
|
|
vrValues.push_back(false);
|
|
}
|
|
else {
|
|
vrValues.push_back(true);
|
|
}
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCBool");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, bool>> ParameterGrp::GetBoolMap(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, bool>> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
if (strcmp(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(), "1")
|
|
!= 0) {
|
|
vrValues.emplace_back(Name, false);
|
|
}
|
|
else {
|
|
vrValues.emplace_back(Name, true);
|
|
}
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCBool");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
long ParameterGrp::GetInt(const char* Name, long lPreset) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return lPreset;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
|
|
// if not return preset
|
|
if (!pcElem) {
|
|
return lPreset;
|
|
}
|
|
// if yes check the value and return
|
|
return atol(StrX(pcElem->getAttribute(XStrLiteral("Value").unicodeForm())).c_str());
|
|
}
|
|
|
|
void ParameterGrp::SetInt(const char* Name, long lValue)
|
|
{
|
|
std::string buf = fmt::sprintf("%li", lValue);
|
|
_SetAttribute(ParamType::FCInt, Name, buf.c_str());
|
|
}
|
|
|
|
std::vector<long> ParameterGrp::GetInts(const char* sFilter) const
|
|
{
|
|
std::vector<long> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.push_back(
|
|
atol(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str()));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCInt");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, long>> ParameterGrp::GetIntMap(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, long>> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.emplace_back(
|
|
Name,
|
|
(atol(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str())));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCInt");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
unsigned long ParameterGrp::GetUnsigned(const char* Name, unsigned long lPreset) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return lPreset;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
|
|
// if not return preset
|
|
if (!pcElem) {
|
|
return lPreset;
|
|
}
|
|
|
|
// if yes check the value and return
|
|
const int base = 10;
|
|
return strtoul(StrX(pcElem->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(),
|
|
nullptr,
|
|
base);
|
|
}
|
|
|
|
void ParameterGrp::SetUnsigned(const char* Name, unsigned long lValue)
|
|
{
|
|
std::string buf = fmt::sprintf("%lu", lValue);
|
|
_SetAttribute(ParamType::FCUInt, Name, buf.c_str());
|
|
}
|
|
|
|
std::vector<unsigned long> ParameterGrp::GetUnsigneds(const char* sFilter) const
|
|
{
|
|
std::vector<unsigned long> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
const int base = 10;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.push_back(
|
|
strtoul(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(),
|
|
nullptr,
|
|
base));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCUInt");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, unsigned long>>
|
|
ParameterGrp::GetUnsignedMap(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, unsigned long>> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
const int base = 10;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.emplace_back(
|
|
Name,
|
|
(strtoul(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str(),
|
|
nullptr,
|
|
base)));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCUInt");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
double ParameterGrp::GetFloat(const char* Name, double dPreset) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return dPreset;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
|
|
// if not return preset
|
|
if (!pcElem) {
|
|
return dPreset;
|
|
}
|
|
// if yes check the value and return
|
|
return atof(StrX(pcElem->getAttribute(XStrLiteral("Value").unicodeForm())).c_str());
|
|
}
|
|
|
|
void ParameterGrp::SetFloat(const char* Name, double dValue)
|
|
{
|
|
// use %.12f instead of %f to handle values < 1.0e-6
|
|
std::string buf = fmt::sprintf("%.12f", dValue);
|
|
_SetAttribute(ParamType::FCFloat, Name, buf.c_str());
|
|
}
|
|
|
|
std::vector<double> ParameterGrp::GetFloats(const char* sFilter) const
|
|
{
|
|
std::vector<double> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.push_back(
|
|
atof(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str()));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCFloat");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, double>> ParameterGrp::GetFloatMap(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, double>> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat");
|
|
while (pcTemp) {
|
|
Name = StrX(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
vrValues.emplace_back(
|
|
Name,
|
|
(atof(StrX(pcTemp->getAttribute(XStrLiteral("Value").unicodeForm())).c_str())));
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCFloat");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
|
|
void ParameterGrp::SetASCII(const char* Name, const char* sValue)
|
|
{
|
|
if (!_pGroupNode) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Setting attribute " << "FCText:" << Name << " in an orphan group " << _cName);
|
|
}
|
|
return;
|
|
}
|
|
if (_Clearing) {
|
|
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
|
FC_WARN("Adding attribute " << "FCText:" << Name << " while clearing " << GetPath());
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool isNew = false;
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
|
|
if (!pcElem) {
|
|
pcElem = CreateElement(_pGroupNode, "FCText", Name);
|
|
isNew = true;
|
|
}
|
|
if (pcElem) {
|
|
// and set the value
|
|
DOMNode* pcElem2 = pcElem->getFirstChild();
|
|
if (!pcElem2) {
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* pDocument = _pGroupNode->getOwnerDocument();
|
|
DOMText* pText = pDocument->createTextNode(XUTF8Str(sValue).unicodeForm());
|
|
pcElem->appendChild(pText);
|
|
if (isNew || sValue[0] != 0) {
|
|
_Notify(ParamType::FCText, Name, sValue);
|
|
}
|
|
}
|
|
else if (strcmp(StrXUTF8(pcElem2->getNodeValue()).c_str(), sValue) != 0) {
|
|
pcElem2->setNodeValue(XUTF8Str(sValue).unicodeForm());
|
|
_Notify(ParamType::FCText, Name, sValue);
|
|
}
|
|
// trigger observer
|
|
Notify(Name);
|
|
}
|
|
}
|
|
|
|
std::string ParameterGrp::GetASCII(const char* Name, const char* pPreset) const
|
|
{
|
|
if (!_pGroupNode) {
|
|
return pPreset ? pPreset : "";
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
|
|
// if not return preset
|
|
if (!pcElem) {
|
|
if (!pPreset) {
|
|
return {};
|
|
}
|
|
return {pPreset};
|
|
}
|
|
// if yes check the value and return
|
|
DOMNode* pcElem2 = pcElem->getFirstChild();
|
|
if (pcElem2) {
|
|
return {StrXUTF8(pcElem2->getNodeValue()).c_str()};
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<std::string> ParameterGrp::GetASCIIs(const char* sFilter) const
|
|
{
|
|
std::vector<std::string> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCText");
|
|
while (pcTemp) {
|
|
Name = StrXUTF8(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
// retrieve the text element
|
|
DOMNode* pcElem2 = pcTemp->getFirstChild();
|
|
if (pcElem2) {
|
|
vrValues.emplace_back(StrXUTF8(pcElem2->getNodeValue()).c_str());
|
|
}
|
|
else {
|
|
vrValues.emplace_back(""); // For a string, an empty value is possible and allowed
|
|
}
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCText");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>>
|
|
ParameterGrp::GetASCIIMap(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<std::string, std::string>> vrValues;
|
|
if (!_pGroupNode) {
|
|
return vrValues;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
DOMElement* pcTemp = FindElement(_pGroupNode, "FCText");
|
|
while (pcTemp) {
|
|
Name = StrXUTF8(pcTemp->getAttribute(XStrLiteral("Name").unicodeForm())).c_str();
|
|
// check on filter condition
|
|
if (!sFilter || Name.find(sFilter) != std::string::npos) {
|
|
// retrieve the text element
|
|
DOMNode* pcElem2 = pcTemp->getFirstChild();
|
|
if (pcElem2) {
|
|
vrValues.emplace_back(Name, std::string(StrXUTF8(pcElem2->getNodeValue()).c_str()));
|
|
}
|
|
else {
|
|
vrValues.emplace_back(
|
|
Name,
|
|
std::string()); // For a string, an empty value is possible and allowed
|
|
}
|
|
}
|
|
pcTemp = FindNextElement(pcTemp, "FCText");
|
|
}
|
|
|
|
return vrValues;
|
|
}
|
|
|
|
//**************************************************************************
|
|
// Access methods
|
|
|
|
void ParameterGrp::RemoveASCII(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCText", Name);
|
|
// if not return
|
|
if (!pcElem) {
|
|
return;
|
|
}
|
|
|
|
DOMNode* node = _pGroupNode->removeChild(pcElem);
|
|
node->release();
|
|
|
|
// trigger observer
|
|
_Notify(ParamType::FCText, Name, nullptr);
|
|
Notify(Name);
|
|
}
|
|
|
|
void ParameterGrp::RemoveBool(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCBool", Name);
|
|
// if not return
|
|
if (!pcElem) {
|
|
return;
|
|
}
|
|
|
|
DOMNode* node = _pGroupNode->removeChild(pcElem);
|
|
node->release();
|
|
|
|
// trigger observer
|
|
_Notify(ParamType::FCBool, Name, nullptr);
|
|
Notify(Name);
|
|
}
|
|
|
|
|
|
void ParameterGrp::RemoveFloat(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCFloat", Name);
|
|
// if not return
|
|
if (!pcElem) {
|
|
return;
|
|
}
|
|
|
|
DOMNode* node = _pGroupNode->removeChild(pcElem);
|
|
node->release();
|
|
|
|
// trigger observer
|
|
_Notify(ParamType::FCFloat, Name, nullptr);
|
|
Notify(Name);
|
|
}
|
|
|
|
void ParameterGrp::RemoveInt(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCInt", Name);
|
|
// if not return
|
|
if (!pcElem) {
|
|
return;
|
|
}
|
|
|
|
DOMNode* node = _pGroupNode->removeChild(pcElem);
|
|
node->release();
|
|
|
|
// trigger observer
|
|
_Notify(ParamType::FCInt, Name, nullptr);
|
|
Notify(Name);
|
|
}
|
|
|
|
void ParameterGrp::RemoveUnsigned(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCUInt", Name);
|
|
// if not return
|
|
if (!pcElem) {
|
|
return;
|
|
}
|
|
|
|
DOMNode* node = _pGroupNode->removeChild(pcElem);
|
|
node->release();
|
|
|
|
// trigger observer
|
|
_Notify(ParamType::FCUInt, Name, nullptr);
|
|
Notify(Name);
|
|
}
|
|
|
|
Base::Color ParameterGrp::GetColor(const char* Name, Base::Color lPreset) const
|
|
{
|
|
auto packed = GetUnsigned(Name, lPreset.getPackedValue());
|
|
|
|
return Color(static_cast<uint32_t>(packed));
|
|
}
|
|
|
|
void ParameterGrp::SetColor(const char* Name, Base::Color lValue)
|
|
{
|
|
SetUnsigned(Name, lValue.getPackedValue());
|
|
}
|
|
|
|
std::vector<Base::Color> ParameterGrp::GetColors(const char* sFilter) const
|
|
{
|
|
auto packed = GetUnsigneds(sFilter);
|
|
std::vector<Base::Color> result;
|
|
|
|
std::transform(packed.begin(),
|
|
packed.end(),
|
|
std::back_inserter(result),
|
|
[](const unsigned long lValue) {
|
|
return Color(static_cast<uint32_t>(lValue));
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, Base::Color>>
|
|
ParameterGrp::GetColorMap(const char* sFilter) const
|
|
{
|
|
auto packed = GetUnsignedMap(sFilter);
|
|
std::vector<std::pair<std::string, Base::Color>> result;
|
|
|
|
std::transform(packed.begin(),
|
|
packed.end(),
|
|
std::back_inserter(result),
|
|
[](const std::pair<std::string, unsigned long>& lValue) {
|
|
return std::make_pair(lValue.first,
|
|
Color(static_cast<uint32_t>(lValue.second)));
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
void ParameterGrp::RemoveColor(const char* Name)
|
|
{
|
|
RemoveUnsigned(Name);
|
|
}
|
|
|
|
void ParameterGrp::RemoveGrp(const char* Name)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
auto it = _GroupMap.find(Name);
|
|
if (it == _GroupMap.end()) {
|
|
return;
|
|
}
|
|
|
|
// If this or any of its children is referenced by an observer we do not
|
|
// delete the handle, just in case the group is later added again, or else
|
|
// those existing observer won't get any notification. BUT, we DO delete
|
|
// the underlying xml elements, so that we don't save the empty group
|
|
// later.
|
|
it->second->Clear(false);
|
|
if (!it->second->_Detached) {
|
|
it->second->_Detached = true;
|
|
_pGroupNode->removeChild(it->second->_pGroupNode);
|
|
}
|
|
if (it->second->ShouldRemove()) {
|
|
it->second->_Parent = nullptr;
|
|
it->second->_Manager = nullptr;
|
|
_GroupMap.erase(it);
|
|
}
|
|
|
|
// trigger observer
|
|
Notify(Name);
|
|
}
|
|
|
|
bool ParameterGrp::RenameGrp(const char* OldName, const char* NewName)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return false;
|
|
}
|
|
|
|
auto it = _GroupMap.find(OldName);
|
|
if (it == _GroupMap.end()) {
|
|
return false;
|
|
}
|
|
auto jt = _GroupMap.find(NewName);
|
|
if (jt != _GroupMap.end()) {
|
|
return false;
|
|
}
|
|
|
|
// rename group handle
|
|
_GroupMap[NewName] = _GroupMap[OldName];
|
|
_GroupMap.erase(OldName);
|
|
_GroupMap[NewName]->_cName = NewName;
|
|
|
|
// check if Element in group
|
|
DOMElement* pcElem = FindElement(_pGroupNode, "FCParamGroup", OldName);
|
|
if (pcElem) {
|
|
pcElem->setAttribute(XStrLiteral("Name").unicodeForm(), XStr(NewName).unicodeForm());
|
|
}
|
|
|
|
_Notify(ParamType::FCGroup, NewName, OldName);
|
|
return true;
|
|
}
|
|
|
|
void ParameterGrp::Clear(bool notify)
|
|
{
|
|
if (!_pGroupNode) {
|
|
return;
|
|
}
|
|
|
|
Base::StateLocker guard(_Clearing);
|
|
|
|
// early trigger notification of group removal when all its children
|
|
// hierarchies are intact.
|
|
_Notify(ParamType::FCGroup, nullptr, nullptr);
|
|
|
|
// checking on references
|
|
for (auto it = _GroupMap.begin(); it != _GroupMap.end();) {
|
|
// If a group handle is referenced by some observer, then do not remove
|
|
// it but clear it, so that any existing observer can still get
|
|
// notification if the group is later on add back. We do remove the
|
|
// underlying xml element from its parent so that we won't save this
|
|
// empty group.
|
|
it->second->Clear(notify);
|
|
if (!it->second->_Detached) {
|
|
it->second->_Detached = true;
|
|
_pGroupNode->removeChild(it->second->_pGroupNode);
|
|
}
|
|
if (!it->second->ShouldRemove()) {
|
|
++it;
|
|
}
|
|
else {
|
|
it->second->_Parent = nullptr;
|
|
it->second->_Manager = nullptr;
|
|
it = _GroupMap.erase(it);
|
|
}
|
|
}
|
|
|
|
// Remove the rest of non-group nodes;
|
|
std::vector<std::pair<ParamType, std::string>> params;
|
|
for (DOMNode *child = _pGroupNode->getFirstChild(), *next = child; child != nullptr;
|
|
child = next) {
|
|
next = next->getNextSibling();
|
|
ParamType type = TypeValue(StrX(child->getNodeName()).c_str());
|
|
if (type != ParamType::FCInvalid && type != ParamType::FCGroup) {
|
|
params.emplace_back(type,
|
|
StrX(child->getAttributes()
|
|
->getNamedItem(XStrLiteral("Name").unicodeForm())
|
|
->getNodeValue())
|
|
.c_str());
|
|
}
|
|
DOMNode* node = _pGroupNode->removeChild(child);
|
|
node->release();
|
|
}
|
|
|
|
for (auto& v : params) {
|
|
_Notify(v.first, v.second.c_str(), nullptr);
|
|
if (notify) {
|
|
Notify(v.second.c_str());
|
|
}
|
|
}
|
|
|
|
// trigger observer
|
|
Notify("");
|
|
}
|
|
|
|
//**************************************************************************
|
|
// Access methods
|
|
|
|
bool ParameterGrp::ShouldRemove() const
|
|
{
|
|
if (this->getRefCount() > 1) {
|
|
return false;
|
|
}
|
|
|
|
return std::all_of(_GroupMap.cbegin(), _GroupMap.cend(), [](const auto& it) {
|
|
return it.second->ShouldRemove();
|
|
});
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
|
|
ParameterGrp::FindElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
|
|
const char* Type,
|
|
const char* Name) const
|
|
{
|
|
if (XMLString::compareString(Start->getNodeName(), XStrLiteral("FCParamGroup").unicodeForm())
|
|
!= 0
|
|
&& XMLString::compareString(Start->getNodeName(), XStrLiteral("FCParameters").unicodeForm())
|
|
!= 0) {
|
|
Base::Console().warning("FindElement: %s cannot have the element %s of type %s\n",
|
|
StrX(Start->getNodeName()).c_str(),
|
|
Name,
|
|
Type);
|
|
return nullptr;
|
|
}
|
|
const XStr xType(Type);
|
|
const XStr xName(Name);
|
|
for (DOMNode* clChild = Start->getFirstChild(); clChild != nullptr;
|
|
clChild = clChild->getNextSibling()) {
|
|
if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
|
|
// the right node Type
|
|
if (!XMLString::compareString(xType.unicodeForm(), clChild->getNodeName())) {
|
|
auto attrs = clChild->getAttributes();
|
|
if (attrs->getLength() > 0) {
|
|
if (Name) {
|
|
DOMNode* attr = attrs->getNamedItem(XStrLiteral("Name").unicodeForm());
|
|
if (attr
|
|
&& !XMLString::compareString(xName.unicodeForm(),
|
|
attr->getNodeValue())) {
|
|
return dynamic_cast<DOMElement*>(clChild);
|
|
}
|
|
}
|
|
else {
|
|
return dynamic_cast<DOMElement*>(clChild);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
|
|
ParameterGrp::FindNextElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Prev, const char* Type) const
|
|
{
|
|
DOMNode* clChild = Prev;
|
|
if (!clChild) {
|
|
return nullptr;
|
|
}
|
|
|
|
const XStr xType(Type);
|
|
while ((clChild = clChild->getNextSibling()) != nullptr) {
|
|
if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
|
|
// the right node Type
|
|
if (!XMLString::compareString(xType.unicodeForm(), clChild->getNodeName())) {
|
|
return dynamic_cast<DOMElement*>(clChild);
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*
|
|
ParameterGrp::FindOrCreateElement(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Start,
|
|
const char* Type,
|
|
const char* Name)
|
|
{
|
|
// first try to find it
|
|
DOMElement* pcElem = FindElement(Start, Type, Name);
|
|
if (pcElem) {
|
|
return pcElem;
|
|
}
|
|
|
|
return CreateElement(Start, Type, Name);
|
|
}
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*
|
|
ParameterGrp::FindAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* Node, const char* Name) const
|
|
{
|
|
DOMNamedNodeMap* attr = Node->getAttributes();
|
|
if (attr) {
|
|
return attr->getNamedItem(XStr(Name).unicodeForm());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::pair<ParameterGrp::ParamType, std::string>>
|
|
ParameterGrp::GetParameterNames(const char* sFilter) const
|
|
{
|
|
std::vector<std::pair<ParameterGrp::ParamType, std::string>> res;
|
|
if (!_pGroupNode) {
|
|
return res;
|
|
}
|
|
|
|
std::string Name;
|
|
|
|
for (DOMNode* clChild = _pGroupNode->getFirstChild(); clChild != nullptr;
|
|
clChild = clChild->getNextSibling()) {
|
|
if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) {
|
|
StrX type(clChild->getNodeName());
|
|
ParamType Type = TypeValue(type.c_str());
|
|
if (Type != ParamType::FCInvalid && Type != ParamType::FCGroup) {
|
|
if (clChild->getAttributes()->getLength() > 0) {
|
|
StrX name(clChild->getAttributes()
|
|
->getNamedItem(XStrLiteral("Name").unicodeForm())
|
|
->getNodeValue());
|
|
if (!sFilter || strstr(name.c_str(), sFilter)) {
|
|
res.emplace_back(Type, name.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void ParameterGrp::NotifyAll()
|
|
{
|
|
// get all ints and notify
|
|
std::vector<std::pair<std::string, long>> IntMap = GetIntMap();
|
|
for (const auto& it : IntMap) {
|
|
Notify(it.first.c_str());
|
|
}
|
|
|
|
// get all booleans and notify
|
|
std::vector<std::pair<std::string, bool>> BoolMap = GetBoolMap();
|
|
for (const auto& it : BoolMap) {
|
|
Notify(it.first.c_str());
|
|
}
|
|
|
|
// get all Floats and notify
|
|
std::vector<std::pair<std::string, double>> FloatMap = GetFloatMap();
|
|
for (const auto& it : FloatMap) {
|
|
Notify(it.first.c_str());
|
|
}
|
|
|
|
// get all strings and notify
|
|
std::vector<std::pair<std::string, std::string>> StringMap = GetASCIIMap();
|
|
for (const auto& it : StringMap) {
|
|
Notify(it.first.c_str());
|
|
}
|
|
|
|
// get all uints and notify
|
|
std::vector<std::pair<std::string, unsigned long>> UIntMap = GetUnsignedMap();
|
|
for (const auto& it : UIntMap) {
|
|
Notify(it.first.c_str());
|
|
}
|
|
}
|
|
|
|
void ParameterGrp::_Reset()
|
|
{
|
|
_pGroupNode = nullptr;
|
|
for (auto& v : _GroupMap) {
|
|
v.second->_Reset();
|
|
}
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// ParameterSerializer
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ParameterSerializer::ParameterSerializer(std::string fn)
|
|
: filename(std::move(fn))
|
|
{}
|
|
|
|
ParameterSerializer::~ParameterSerializer() = default;
|
|
|
|
void ParameterSerializer::SaveDocument(const ParameterManager& mgr)
|
|
{
|
|
mgr.SaveDocument(filename.c_str());
|
|
}
|
|
|
|
int ParameterSerializer::LoadDocument(ParameterManager& mgr)
|
|
{
|
|
return mgr.LoadDocument(filename.c_str());
|
|
}
|
|
|
|
bool ParameterSerializer::LoadOrCreateDocument(ParameterManager& mgr)
|
|
{
|
|
return mgr.LoadOrCreateDocument(filename.c_str());
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// ParameterManager
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
static XercesDOMParser::ValSchemes gValScheme = XercesDOMParser::Val_Auto; // NOLINT
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
/** Default construction
|
|
*/
|
|
ParameterManager::ParameterManager()
|
|
{
|
|
_Manager = this;
|
|
|
|
// initialize the XML system
|
|
Init();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Local data
|
|
//
|
|
// gXmlFile
|
|
// The path to the file to parser. Set via command line.
|
|
//
|
|
// gDoNamespaces
|
|
// Indicates whether namespace processing should be done.
|
|
//
|
|
// gDoSchema
|
|
// Indicates whether schema processing should be done.
|
|
//
|
|
// gSchemaFullChecking
|
|
// Indicates whether full schema constraint checking should be done.
|
|
//
|
|
// gDoCreate
|
|
// Indicates whether entity reference nodes needs to be created or not.
|
|
// Defaults to false.
|
|
//
|
|
// gOutputEncoding
|
|
// The encoding we are to output in. If not set on the command line,
|
|
// then it is defaults to the encoding of the input XML file.
|
|
//
|
|
// gMyEOLSequence
|
|
// The end of line sequence we are to output.
|
|
//
|
|
// gSplitCdataSections
|
|
// Indicates whether split-cdata-sections is to be enabled or not.
|
|
//
|
|
// gDiscardDefaultContent
|
|
// Indicates whether default content is discarded or not.
|
|
//
|
|
// gUseFilter
|
|
// Indicates if user wants to plug in the DOMPrintFilter.
|
|
//
|
|
// gValScheme
|
|
// Indicates what validation scheme to use. It defaults to 'auto', but
|
|
// can be set via the -v= command.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// NOLINTBEGIN
|
|
gIgnoreSave = false;
|
|
gDoNamespaces = false;
|
|
gDoSchema = false;
|
|
gSchemaFullChecking = false;
|
|
gDoCreate = true;
|
|
|
|
gOutputEncoding = nullptr;
|
|
gMyEOLSequence = nullptr;
|
|
|
|
gSplitCdataSections = true;
|
|
gDiscardDefaultContent = true;
|
|
gUseFilter = true;
|
|
gFormatPrettyPrint = true;
|
|
// NOLINTEND
|
|
}
|
|
|
|
/** Destruction
|
|
* complete destruction of the object
|
|
*/
|
|
ParameterManager::~ParameterManager()
|
|
{
|
|
_Reset();
|
|
delete _pDocument;
|
|
delete paramSerializer;
|
|
}
|
|
|
|
Base::Reference<ParameterManager> ParameterManager::Create()
|
|
{
|
|
return {new ParameterManager()};
|
|
}
|
|
|
|
void ParameterManager::Init()
|
|
{
|
|
static bool Init = false;
|
|
if (!Init) {
|
|
try {
|
|
XMLPlatformUtils::Initialize();
|
|
}
|
|
catch (const XMLException& toCatch) {
|
|
#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN)
|
|
std::ostringstream err;
|
|
#else
|
|
std::stringstream err;
|
|
#endif
|
|
char* pMsg = XMLString::transcode(toCatch.getMessage());
|
|
err << "Error during Xerces-c Initialization.\n"
|
|
<< " Exception message:" << pMsg;
|
|
XMLString::release(&pMsg);
|
|
throw XMLBaseException(err.str().c_str());
|
|
}
|
|
Init = true;
|
|
}
|
|
}
|
|
|
|
void ParameterManager::Terminate()
|
|
{
|
|
XMLTools::terminate();
|
|
XMLPlatformUtils::Terminate();
|
|
}
|
|
|
|
//**************************************************************************
|
|
// Serializer handling
|
|
|
|
void ParameterManager::SetSerializer(ParameterSerializer* ps)
|
|
{
|
|
if (paramSerializer != ps) {
|
|
delete paramSerializer;
|
|
}
|
|
paramSerializer = ps;
|
|
}
|
|
|
|
bool ParameterManager::HasSerializer() const
|
|
{
|
|
return (paramSerializer != nullptr);
|
|
}
|
|
|
|
const std::string& ParameterManager::GetSerializeFileName() const
|
|
{
|
|
static const std::string _dummy;
|
|
return paramSerializer ? paramSerializer->GetFileName() : _dummy;
|
|
}
|
|
|
|
int ParameterManager::LoadDocument()
|
|
{
|
|
if (paramSerializer) {
|
|
return paramSerializer->LoadDocument(*this);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool ParameterManager::LoadOrCreateDocument()
|
|
{
|
|
if (paramSerializer) {
|
|
return paramSerializer->LoadOrCreateDocument(*this);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ParameterManager::SaveDocument() const
|
|
{
|
|
if (paramSerializer) {
|
|
paramSerializer->SaveDocument(*this);
|
|
}
|
|
}
|
|
|
|
void ParameterManager::SetIgnoreSave(bool value)
|
|
{
|
|
gIgnoreSave = value;
|
|
}
|
|
|
|
bool ParameterManager::IgnoreSave() const
|
|
{
|
|
return gIgnoreSave;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
QString getLockFile(const Base::FileInfo& file)
|
|
{
|
|
QFileInfo fi(QDir::tempPath(), QString::fromStdString(file.fileName() + ".lock"));
|
|
return fi.absoluteFilePath();
|
|
}
|
|
|
|
int getTimeout()
|
|
{
|
|
const int timeout = 5000;
|
|
return timeout;
|
|
}
|
|
} // namespace
|
|
|
|
//**************************************************************************
|
|
// Document handling
|
|
|
|
bool ParameterManager::LoadOrCreateDocument(const char* sFileName)
|
|
{
|
|
Base::FileInfo file(sFileName);
|
|
if (file.exists()) {
|
|
LoadDocument(sFileName);
|
|
return false;
|
|
}
|
|
|
|
CreateDocument();
|
|
return true;
|
|
}
|
|
|
|
int ParameterManager::LoadDocument(const char* sFileName)
|
|
{
|
|
try {
|
|
Base::FileInfo file(sFileName);
|
|
QLockFile lock(getLockFile(file));
|
|
if (!lock.tryLock(getTimeout())) {
|
|
// Continue with empty config
|
|
CreateDocument();
|
|
SetIgnoreSave(true);
|
|
std::cerr << "Failed to access file for reading: " << sFileName << std::endl;
|
|
return 1;
|
|
}
|
|
#if defined(FC_OS_WIN32)
|
|
std::wstring name = file.toStdWString();
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
LocalFileInputSource inputSource(reinterpret_cast<const XMLCh*>(name.c_str()));
|
|
#else
|
|
LocalFileInputSource inputSource(XStr(file.filePath().c_str()).unicodeForm());
|
|
#endif
|
|
return LoadDocument(inputSource);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
std::cerr << e.what() << std::endl;
|
|
throw;
|
|
}
|
|
catch (...) {
|
|
std::cerr << "An error occurred during parsing\n " << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
class NoOpEntityResolver: public XERCES_CPP_NAMESPACE_QUALIFIER EntityResolver
|
|
{
|
|
public:
|
|
InputSource* resolveEntity(const XMLCh* const publicId, const XMLCh* const systemId) override
|
|
{
|
|
(void)publicId;
|
|
(void)systemId;
|
|
return nullptr; // Block all external entity resolution
|
|
}
|
|
};
|
|
|
|
int ParameterManager::LoadDocument(const XERCES_CPP_NAMESPACE_QUALIFIER InputSource& inputSource)
|
|
{
|
|
//
|
|
// Create our parser, then attach an error handler to the parser.
|
|
// The parser will call back to methods of the ErrorHandler if it
|
|
// discovers errors during the course of parsing the XML document.
|
|
//
|
|
auto parser = std::make_unique<XercesDOMParser>();
|
|
auto entityBlocker = std::make_unique<NoOpEntityResolver>();
|
|
parser->setValidationScheme(gValScheme);
|
|
parser->setDoNamespaces(gDoNamespaces);
|
|
parser->setDoSchema(gDoSchema);
|
|
parser->setValidationSchemaFullChecking(gSchemaFullChecking);
|
|
parser->setCreateEntityReferenceNodes(gDoCreate);
|
|
parser->setDisableDefaultEntityResolution(true);
|
|
parser->setEntityResolver(entityBlocker.get());
|
|
|
|
auto errReporter = std::make_unique<DOMTreeErrorReporter>();
|
|
parser->setErrorHandler(errReporter.get());
|
|
|
|
//
|
|
// Parse the XML file, catching any XML exceptions that might propagate
|
|
// out of it.
|
|
//
|
|
bool errorsOccurred = false;
|
|
try {
|
|
parser->parse(inputSource);
|
|
}
|
|
catch (const XMLException& e) {
|
|
std::cerr << "An error occurred during parsing\n Message: " << StrX(e.getMessage())
|
|
<< "\n";
|
|
errorsOccurred = true;
|
|
}
|
|
catch (const DOMException& e) {
|
|
std::cerr << "A DOM error occurred during parsing\n DOMException code: " << e.code
|
|
<< "\n";
|
|
errorsOccurred = true;
|
|
}
|
|
catch (...) {
|
|
std::cerr << "An error occurred during parsing\n " << "\n";
|
|
errorsOccurred = true;
|
|
}
|
|
|
|
if (errorsOccurred) {
|
|
return 0;
|
|
}
|
|
|
|
_pDocument = parser->adoptDocument();
|
|
|
|
if (!_pDocument) {
|
|
throw XMLBaseException("Malformed Parameter document: Invalid document");
|
|
}
|
|
|
|
DOMElement* rootElem = _pDocument->getDocumentElement();
|
|
if (!rootElem) {
|
|
throw XMLBaseException("Malformed Parameter document: Root group not found");
|
|
}
|
|
|
|
_pGroupNode = FindElement(rootElem, "FCParamGroup", "Root");
|
|
|
|
if (!_pGroupNode) {
|
|
throw XMLBaseException("Malformed Parameter document: Root group not found");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void ParameterManager::SaveDocument(const char* sFileName) const
|
|
{
|
|
try {
|
|
Base::FileInfo file(sFileName);
|
|
QLockFile lock(getLockFile(file));
|
|
if (!lock.tryLock(getTimeout())) {
|
|
std::cerr << "Failed to access file for writing: " << sFileName << std::endl;
|
|
return;
|
|
}
|
|
//
|
|
// Plug in a format target to receive the resultant
|
|
// XML stream from the serializer.
|
|
//
|
|
// LocalFileFormatTarget prints the resultant XML stream
|
|
// to a file once it receives any thing from the serializer.
|
|
//
|
|
XMLFormatTarget* myFormTarget {};
|
|
#if defined(FC_OS_WIN32)
|
|
std::wstring name = file.toStdWString();
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
myFormTarget = new LocalFileFormatTarget(reinterpret_cast<const XMLCh*>(name.c_str()));
|
|
#else
|
|
myFormTarget = new LocalFileFormatTarget(file.filePath().c_str());
|
|
#endif
|
|
SaveDocument(myFormTarget);
|
|
delete myFormTarget;
|
|
}
|
|
catch (XMLException& e) {
|
|
std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
|
|
<< StrX(e.getMessage()) << std::endl;
|
|
}
|
|
}
|
|
|
|
void ParameterManager::SaveDocument(XMLFormatTarget* pFormatTarget) const
|
|
{
|
|
try {
|
|
std::unique_ptr<DOMPrintFilter> myFilter;
|
|
std::unique_ptr<DOMErrorHandler> myErrorHandler;
|
|
|
|
// NOLINTBEGIN
|
|
// get a serializer, an instance of DOMWriter
|
|
XMLCh tempStr[100];
|
|
XMLString::transcode("LS", tempStr, 99);
|
|
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
|
|
DOMLSSerializer* theSerializer =
|
|
static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
|
|
// NOLINTEND
|
|
|
|
// set user specified end of line sequence and output encoding
|
|
theSerializer->setNewLine(gMyEOLSequence);
|
|
|
|
|
|
//
|
|
// do the serialization through DOMWriter::writeNode();
|
|
//
|
|
if (_pDocument) {
|
|
DOMLSOutput* theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
|
|
theOutput->setEncoding(gOutputEncoding);
|
|
|
|
if (gUseFilter) {
|
|
myFilter = std::make_unique<DOMPrintFilter>(
|
|
DOMNodeFilter::SHOW_ELEMENT | DOMNodeFilter::SHOW_ATTRIBUTE
|
|
| DOMNodeFilter::SHOW_DOCUMENT_TYPE | DOMNodeFilter::SHOW_TEXT);
|
|
theSerializer->setFilter(myFilter.get());
|
|
}
|
|
|
|
// plug in user's own error handler
|
|
myErrorHandler = std::make_unique<DOMPrintErrorHandler>();
|
|
DOMConfiguration* config = theSerializer->getDomConfig();
|
|
|
|
// NOLINTBEGIN
|
|
config->setParameter(XMLUni::fgDOMErrorHandler, myErrorHandler.get());
|
|
|
|
// set feature if the serializer supports the feature/mode
|
|
if (config->canSetParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections)) {
|
|
config->setParameter(XMLUni::fgDOMWRTSplitCdataSections, gSplitCdataSections);
|
|
}
|
|
|
|
if (config->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent,
|
|
gDiscardDefaultContent)) {
|
|
config->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, gDiscardDefaultContent);
|
|
}
|
|
|
|
if (config->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint)) {
|
|
config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, gFormatPrettyPrint);
|
|
}
|
|
// NOLINTEND
|
|
|
|
theOutput->setByteStream(pFormatTarget);
|
|
theSerializer->write(_pDocument, theOutput);
|
|
|
|
theOutput->release();
|
|
}
|
|
|
|
theSerializer->release();
|
|
}
|
|
catch (XMLException& e) {
|
|
std::cerr << "An error occurred during creation of output transcoder. Msg is:" << std::endl
|
|
<< StrX(e.getMessage()) << std::endl;
|
|
}
|
|
}
|
|
|
|
void ParameterManager::CreateDocument()
|
|
{
|
|
// creating a document from screatch
|
|
DOMImplementation* impl =
|
|
DOMImplementationRegistry::getDOMImplementation(XStrLiteral("Core").unicodeForm());
|
|
delete _pDocument;
|
|
_pDocument =
|
|
impl->createDocument(nullptr, // root element namespace URI.
|
|
XStrLiteral("FCParameters").unicodeForm(), // root element name
|
|
nullptr); // document type object (DTD).
|
|
|
|
// creating the node for the root group
|
|
DOMElement* rootElem = _pDocument->getDocumentElement();
|
|
_pGroupNode = _pDocument->createElement(XStrLiteral("FCParamGroup").unicodeForm());
|
|
_pGroupNode->setAttribute(XStrLiteral("Name").unicodeForm(), XStrLiteral("Root").unicodeForm());
|
|
rootElem->appendChild(_pGroupNode);
|
|
}
|
|
|
|
void ParameterManager::CheckDocument() const
|
|
{
|
|
if (!_pDocument) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
//
|
|
// Plug in a format target to receive the resultant
|
|
// XML stream from the serializer.
|
|
//
|
|
// LocalFileFormatTarget prints the resultant XML stream
|
|
// to a file once it receives any thing from the serializer.
|
|
//
|
|
MemBufFormatTarget myFormTarget;
|
|
SaveDocument(&myFormTarget);
|
|
|
|
// Either use the file saved on disk or write the current XML into a buffer in memory
|
|
// const char* xmlFile = "...";
|
|
MemBufInputSource xmlFile(myFormTarget.getRawBuffer(), myFormTarget.getLen(), "(memory)");
|
|
|
|
// Either load the XSD file from disk or use the built-in string
|
|
// const char* xsdFile = "...";
|
|
std::string xsdStr(xmlSchemeString); // NOLINT
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
MemBufInputSource xsdFile(reinterpret_cast<const XMLByte*>(xsdStr.c_str()),
|
|
xsdStr.size(),
|
|
"Parameter.xsd");
|
|
|
|
// See
|
|
// http://apache-xml-project.6118.n7.nabble.com/validating-xml-with-xsd-schema-td17515.html
|
|
//
|
|
XercesDOMParser parser;
|
|
Grammar* grammar = parser.loadGrammar(xsdFile, Grammar::SchemaGrammarType, true);
|
|
if (!grammar) {
|
|
Base::Console().error("Grammar file cannot be loaded.\n");
|
|
return;
|
|
}
|
|
|
|
parser.setExternalNoNamespaceSchemaLocation("Parameter.xsd");
|
|
// parser.setExitOnFirstFatalError(true);
|
|
// parser.setValidationConstraintFatal(true);
|
|
parser.cacheGrammarFromParse(true);
|
|
parser.setValidationScheme(XercesDOMParser::Val_Auto);
|
|
parser.setDoNamespaces(true);
|
|
parser.setDoSchema(true);
|
|
parser.setDisableDefaultEntityResolution(true);
|
|
|
|
DOMTreeErrorReporter errHandler;
|
|
parser.setErrorHandler(&errHandler);
|
|
parser.parse(xmlFile);
|
|
|
|
if (parser.getErrorCount() > 0) {
|
|
Base::Console().error("Unexpected XML structure detected: %zu errors\n",
|
|
parser.getErrorCount());
|
|
}
|
|
}
|
|
catch (XMLException& e) {
|
|
std::cerr << "An error occurred while checking document. Msg is:" << std::endl
|
|
<< StrX(e.getMessage()) << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// DOMTreeErrorReporter
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
void DOMTreeErrorReporter::warning(const SAXParseException& /*exc*/)
|
|
{
|
|
//
|
|
// Ignore all warnings.
|
|
//
|
|
}
|
|
|
|
void DOMTreeErrorReporter::error(const SAXParseException& toCatch)
|
|
{
|
|
fSawErrors = true;
|
|
std::cerr << "Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
|
|
<< toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
|
|
<< "\n Message: " << StrX(toCatch.getMessage()) << std::endl;
|
|
}
|
|
|
|
void DOMTreeErrorReporter::fatalError(const SAXParseException& toCatch)
|
|
{
|
|
fSawErrors = true;
|
|
std::cerr << "Fatal Error at file \"" << StrX(toCatch.getSystemId()) << "\", line "
|
|
<< toCatch.getLineNumber() << ", column " << toCatch.getColumnNumber()
|
|
<< "\n Message: " << StrX(toCatch.getMessage()) << std::endl;
|
|
}
|
|
|
|
void DOMTreeErrorReporter::resetErrors()
|
|
{
|
|
// No-op in this case
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// DOMPrintFilter
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
DOMPrintFilter::DOMPrintFilter(ShowType whatToShow)
|
|
: fWhatToShow(whatToShow)
|
|
{}
|
|
|
|
DOMPrintFilter::FilterAction DOMPrintFilter::acceptNode(const DOMNode* node) const
|
|
{
|
|
if (XMLString::compareString(node->getNodeName(), XStrLiteral("FCParameters").unicodeForm())
|
|
== 0) {
|
|
// This node is supposed to have a single FCParamGroup and two text nodes.
|
|
// Over time it can happen that the text nodes collect extra newlines.
|
|
const DOMNodeList* children = node->getChildNodes();
|
|
for (XMLSize_t i = 0; i < children->getLength(); i++) {
|
|
DOMNode* child = children->item(i);
|
|
if (child->getNodeType() == DOMNode::TEXT_NODE) {
|
|
child->setNodeValue(XStrLiteral("\n").unicodeForm());
|
|
}
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
switch (node->getNodeType()) {
|
|
case DOMNode::TEXT_NODE: {
|
|
// Filter out text element if it is under a group node. Note text xml
|
|
// element is plain text in between tags, and we do not store any text
|
|
// there.
|
|
auto parent = node->getParentNode();
|
|
if (parent && XMLString::compareString(parent->getNodeName(),
|
|
XStrLiteral("FCParamGroup").unicodeForm()) == 0) {
|
|
return DOMNodeFilter::FILTER_REJECT;
|
|
}
|
|
return DOMNodeFilter::FILTER_ACCEPT;
|
|
}
|
|
case DOMNode::DOCUMENT_TYPE_NODE:
|
|
case DOMNode::DOCUMENT_NODE: {
|
|
return DOMNodeFilter::FILTER_REJECT; // no effect
|
|
}
|
|
default: {
|
|
return DOMNodeFilter::FILTER_ACCEPT;
|
|
}
|
|
}
|
|
// clang-format on
|
|
}
|
|
|
|
//**************************************************************************
|
|
//**************************************************************************
|
|
// DOMPrintErrorHandler
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
bool DOMPrintErrorHandler::handleError(const DOMError& domError)
|
|
{
|
|
// Display whatever error message passed from the serializer
|
|
char* msg = XMLString::transcode(domError.getMessage());
|
|
std::cout << msg << std::endl;
|
|
XMLString::release(&msg);
|
|
|
|
// Instructs the serializer to continue serialization if possible.
|
|
return true;
|
|
}
|