/*************************************************************************** * Copyright (c) 2002 Jürgen Riegel * * * * 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 "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #include #include #include #ifdef FC_OS_LINUX #include #endif #include #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) XERCES_CPP_NAMESPACE_USE 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& Grp) { if (Grp == this) { return; } // delete previous content Grp->Clear(true); // copy all insertTo(Grp); } void ParameterGrp::insertTo(const Base::Reference& Grp) { if (Grp == this) { return; } // copy group std::vector> Grps = GetGroups(); std::vector>::iterator It1; for (It1 = Grps.begin(); It1 != Grps.end(); ++It1) { (*It1)->insertTo(Grp->GetGroup((*It1)->GetGroupName())); } // copy strings std::vector> StringMap = GetASCIIMap(); std::vector>::iterator It2; for (It2 = StringMap.begin(); It2 != StringMap.end(); ++It2) { Grp->SetASCII(It2->first.c_str(), It2->second.c_str()); } // copy bool std::vector> BoolMap = GetBoolMap(); std::vector>::iterator It3; for (It3 = BoolMap.begin(); It3 != BoolMap.end(); ++It3) { Grp->SetBool(It3->first.c_str(), It3->second); } // copy int std::vector> IntMap = GetIntMap(); std::vector>::iterator It4; for (It4 = IntMap.begin(); It4 != IntMap.end(); ++It4) { Grp->SetInt(It4->first.c_str(), It4->second); } // copy float std::vector> FloatMap = GetFloatMap(); std::vector>::iterator It5; for (It5 = FloatMap.begin(); It5 != FloatMap.end(); ++It5) { Grp->SetFloat(It5->first.c_str(), It5->second); } // copy uint std::vector> UIntMap = GetUnsignedMap(); std::vector>::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(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(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(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(Mngr)); } void ParameterGrp::revert(const Base::Reference& 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(), XStr("FCParamGroup").unicodeForm()) != 0 && XMLString::compareString(Start->getNodeName(), XStr("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(XStr("Name").unicodeForm(), XStr(Name).unicodeForm()); Start->appendChild(pcElem); return pcElem; } Base::Reference ParameterGrp::GetGroup(const char* Name) { if (!Name) { throw Base::ValueError("Empty group name"); } Base::Reference hGrp = this; std::vector 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::_GetGroup(const char* Name) { Base::Reference 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(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> ParameterGrp::GetGroups() { Base::Reference rParamGrp; std::vector> vrParamGrp; if (!_pGroupNode) { return vrParamGrp; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCParamGroup"); while (pcTemp) { Name = StrX(pcTemp->getAttributes()->getNamedItem(XStr("Name").unicodeForm())->getNodeValue()) .c_str(); // already created? if (!(rParamGrp = _GroupMap[Name]).isValid()) { rParamGrp = Base::Reference(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(XStr("Value").unicodeForm())).c_str(); } return Value.c_str(); } std::vector> ParameterGrp::GetAttributeMap(ParamType Type, const char* sFilter) const { std::vector> 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(pcTemp) ->getAttributes() ->getNamedItem(XStr("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(pcTemp)->getAttribute( XStr("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(XStr("Value").unicodeForm())).c_str(), "1") == 0); } void ParameterGrp::SetBool(const char* Name, bool bValue) { _SetAttribute(ParamType::FCBool, Name, bValue ? "1" : "0"); } std::vector ParameterGrp::GetBools(const char* sFilter) const { std::vector vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { if (strcmp(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(), "1") != 0) { vrValues.push_back(false); } else { vrValues.push_back(true); } } pcTemp = FindNextElement(pcTemp, "FCBool"); } return vrValues; } std::vector> ParameterGrp::GetBoolMap(const char* sFilter) const { std::vector> vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCBool"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { if (strcmp(StrX(pcTemp->getAttribute(XStr("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(XStr("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 ParameterGrp::GetInts(const char* sFilter) const { std::vector vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.push_back( atol(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str())); } pcTemp = FindNextElement(pcTemp, "FCInt"); } return vrValues; } std::vector> ParameterGrp::GetIntMap(const char* sFilter) const { std::vector> vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCInt"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.emplace_back( Name, (atol(StrX(pcTemp->getAttribute(XStr("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(XStr("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 ParameterGrp::GetUnsigneds(const char* sFilter) const { std::vector vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; const int base = 10; DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.push_back( strtoul(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str(), nullptr, base)); } pcTemp = FindNextElement(pcTemp, "FCUInt"); } return vrValues; } std::vector> ParameterGrp::GetUnsignedMap(const char* sFilter) const { std::vector> vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; const int base = 10; DOMElement* pcTemp = FindElement(_pGroupNode, "FCUInt"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.emplace_back( Name, (strtoul(StrX(pcTemp->getAttribute(XStr("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(XStr("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 ParameterGrp::GetFloats(const char* sFilter) const { std::vector vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.push_back( atof(StrX(pcTemp->getAttribute(XStr("Value").unicodeForm())).c_str())); } pcTemp = FindNextElement(pcTemp, "FCFloat"); } return vrValues; } std::vector> ParameterGrp::GetFloatMap(const char* sFilter) const { std::vector> vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCFloat"); while (pcTemp) { Name = StrX(pcTemp->getAttribute(XStr("Name").unicodeForm())).c_str(); // check on filter condition if (!sFilter || Name.find(sFilter) != std::string::npos) { vrValues.emplace_back( Name, (atof(StrX(pcTemp->getAttribute(XStr("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 ParameterGrp::GetASCIIs(const char* sFilter) const { std::vector vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCText"); while (pcTemp) { Name = StrXUTF8(pcTemp->getAttribute(XStr("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> ParameterGrp::GetASCIIMap(const char* sFilter) const { std::vector> vrValues; if (!_pGroupNode) { return vrValues; } std::string Name; DOMElement* pcTemp = FindElement(_pGroupNode, "FCText"); while (pcTemp) { Name = StrXUTF8(pcTemp->getAttribute(XStr("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); } 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(XStr("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> 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(XStr("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(), XStr("FCParamGroup").unicodeForm()) != 0 && XMLString::compareString(Start->getNodeName(), XStr("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; } for (DOMNode* clChild = Start->getFirstChild(); clChild != nullptr; clChild = clChild->getNextSibling()) { if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { // the right node Type if (!strcmp(Type, StrX(clChild->getNodeName()).c_str())) { if (clChild->getAttributes()->getLength() > 0) { if (Name) { DOMNode* attr = FindAttribute(clChild, "Name"); if (attr && !strcmp(Name, StrX(attr->getNodeValue()).c_str())) { return dynamic_cast(clChild); } } else { return dynamic_cast(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; } while ((clChild = clChild->getNextSibling()) != nullptr) { if (clChild->getNodeType() == DOMNode::ELEMENT_NODE) { // the right node Type if (!strcmp(Type, StrX(clChild->getNodeName()).c_str())) { return dynamic_cast(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> ParameterGrp::GetParameterNames(const char* sFilter) const { std::vector> 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(XStr("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> IntMap = GetIntMap(); for (const auto& it : IntMap) { Notify(it.first.c_str()); } // get all booleans and notify std::vector> BoolMap = GetBoolMap(); for (const auto& it : BoolMap) { Notify(it.first.c_str()); } // get all Floats and notify std::vector> FloatMap = GetFloatMap(); for (const auto& it : FloatMap) { Notify(it.first.c_str()); } // get all strings and notify std::vector> StringMap = GetASCIIMap(); for (const auto& it : StringMap) { Notify(it.first.c_str()); } // get all uints and notify std::vector> 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::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(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; } } 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 = new XercesDOMParser; parser->setValidationScheme(gValScheme); parser->setDoNamespaces(gDoNamespaces); parser->setDoSchema(gDoSchema); parser->setValidationSchemaFullChecking(gSchemaFullChecking); parser->setCreateEntityReferenceNodes(gDoCreate); auto errReporter = new DOMTreeErrorReporter(); parser->setErrorHandler(errReporter); // // Parse the XML file, catching any XML exceptions that might propagate // out of it. // bool errorsOccured = false; try { parser->parse(inputSource); } catch (const XMLException& e) { std::cerr << "An error occurred during parsing\n Message: " << StrX(e.getMessage()) << std::endl; errorsOccured = true; } catch (const DOMException& e) { std::cerr << "A DOM error occurred during parsing\n DOMException code: " << e.code << std::endl; errorsOccured = true; } catch (...) { std::cerr << "An error occurred during parsing\n " << std::endl; errorsOccured = true; } if (errorsOccured) { delete parser; delete errReporter; return 0; } _pDocument = parser->adoptDocument(); delete parser; delete errReporter; 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(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 myFilter; std::unique_ptr 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(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(impl)->createLSOutput(); theOutput->setEncoding(gOutputEncoding); if (gUseFilter) { myFilter = std::make_unique( 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(); 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(XStr("Core").unicodeForm()); delete _pDocument; _pDocument = impl->createDocument(nullptr, // root element namespace URI. XStr("FCParameters").unicodeForm(), // root element name nullptr); // document type object (DTD). // creating the node for the root group DOMElement* rootElem = _pDocument->getDocumentElement(); _pGroupNode = _pDocument->createElement(XStr("FCParamGroup").unicodeForm()); _pGroupNode->setAttribute(XStr("Name").unicodeForm(), XStr("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(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); 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(), XStr("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(XStr("\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(), XStr("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; }