Index: src/libchcore/ConfigNodeContainer.cpp =================================================================== diff -u -N --- src/libchcore/ConfigNodeContainer.cpp (revision a4635addad389b9e117679437a3e1b64a739ea96) +++ src/libchcore/ConfigNodeContainer.cpp (revision 0) @@ -1,603 +0,0 @@ -// ============================================================================ -// Copyright (C) 2001-2014 by Jozef Starosczyk -// ixen@copyhandler.com -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU Library General Public License -// (version 2) as published by the Free Software Foundation; -// -// This program 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 General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this program; if not, write to the -// Free Software Foundation, Inc., -// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// ============================================================================ -#include "stdafx.h" -#include "ConfigNodeContainer.h" -#include -#include -#include "TCoreException.h" -#include "ErrorCodes.h" -#include - -namespace chcore -{ - namespace details - { - /////////////////////////////////////////////////////////////////////// - ChangeValue::ChangeValue(const TString& strNewValue) : m_strNewValue(strNewValue) - { - } - - void ChangeValue::operator()(ConfigNode& rNode) - { - m_bWasModified = false; - if (rNode.m_strValue.Get() != m_strNewValue) - { - rNode.m_strValue.Modify() = m_strNewValue; - m_bWasModified = true; - } - } - - bool ChangeValue::WasModified() const - { - return m_bWasModified; - } - - /////////////////////////////////////////////////////////////////////// - ChangeOrderAndValue::ChangeOrderAndValue(const TString& tNewValue, int iOrder) : - m_strNewValue(tNewValue), - m_iOrder(iOrder) - { - } - - void ChangeOrderAndValue::operator()(ConfigNode& rNode) - { - m_bWasModified = false; - if (rNode.m_strValue.Get() != m_strNewValue || rNode.m_iOrder != m_iOrder) - { - rNode.m_strValue = m_strNewValue; - rNode.m_iOrder = m_iOrder; - - m_bWasModified = true; - } - } - - bool ChangeOrderAndValue::WasModified() const - { - return m_bWasModified; - } - - /////////////////////////////////////////////////////////////////////// - ConfigNodeContainer::ConfigNodeContainer() : - m_bDelayedEnabled(false), - m_oidLastObjectID(0) - { - } - - ConfigNodeContainer::ConfigNodeContainer(const ConfigNodeContainer& rSrc) - { - boost::shared_lock lock(rSrc.m_lock); - m_mic = rSrc.m_mic; - m_strFilePath = rSrc.m_strFilePath; - m_setDelayedNotifications.Clear(); - m_bDelayedEnabled = false; - m_oidLastObjectID = rSrc.m_oidLastObjectID; - } - - ConfigNodeContainer& ConfigNodeContainer::operator=(const ConfigNodeContainer& rSrc) - { - if (this != &rSrc) - { - boost::unique_lock lock2(m_lock); - boost::shared_lock lock(rSrc.m_lock); - - m_mic = rSrc.m_mic; - m_strFilePath = rSrc.m_strFilePath; - m_bDelayedEnabled = false; - m_oidLastObjectID = rSrc.m_oidLastObjectID; - - m_setDelayedNotifications.Clear(); - } - - return *this; - } - - TStringArray ConfigNodeContainer::GetArrayValue(PCTSTR pszPropName, const TStringArray& rDefaultValue) const - { - TStringArray tArray; - - boost::shared_lock lock(m_lock); - - std::pair pairFnd - = m_mic.equal_range(boost::make_tuple(pszPropName)); - - if (pairFnd.first == m_mic.end()) - return rDefaultValue; - - while (pairFnd.first != m_mic.end() && pairFnd.first != pairFnd.second) - { - tArray.Add((*pairFnd.first).m_strValue); - ++pairFnd.first; - } - - return tArray; - } - - bool ConfigNodeContainer::GetArrayValueNoDefault(PCTSTR pszPropName, TStringArray& rValue) const - { - boost::shared_lock lock(m_lock); - - rValue.Clear(); - - std::pair pairFnd - = m_mic.equal_range(boost::make_tuple(pszPropName)); - - if (pairFnd.first == m_mic.end()) - return false; - - while (pairFnd.first != m_mic.end() && pairFnd.first != pairFnd.second) - { - rValue.Add((*pairFnd.first).m_strValue); - ++pairFnd.first; - } - - return true; - } - - bool ConfigNodeContainer::SetArrayValue(PCTSTR pszPropName, const TStringArray& rValue) - { - boost::unique_lock lock(m_lock); - - std::pair pairFnd - = m_mic.equal_range(boost::make_tuple(pszPropName)); - - if (pairFnd.first == m_mic.end()) - { - // insert new items - for (size_t stIndex = 0; stIndex < rValue.GetCount(); ++stIndex) - { - m_mic.insert(ConfigNode(++m_oidLastObjectID, pszPropName, boost::numeric_cast(stIndex), rValue.GetAt(stIndex))); - } - - return false; - } - else - { - // insert/modify/delete items (diff mode) - size_t stIndex = 0; - while (pairFnd.first != m_mic.end() && pairFnd.first != pairFnd.second) - { - if (stIndex < rValue.GetCount()) - { - // update existing item - ChangeOrderAndValue tChange(rValue.GetAt(stIndex), boost::numeric_cast(stIndex)); - m_mic.modify(pairFnd.first, std::ref(tChange)); - - ++pairFnd.first; - } - else - { - // delete this item - m_setRemovedObjects.Add(pairFnd.first->m_oidObjectID); - pairFnd.first = m_mic.erase(pairFnd.first); - } - - ++stIndex; - } - - while (stIndex < rValue.GetCount()) - { - // add items not added before (with new oids) - m_mic.insert(ConfigNode(++m_oidLastObjectID, pszPropName, boost::numeric_cast(stIndex), rValue.GetAt(stIndex))); - ++stIndex; - } - - return true; - } - } - - namespace - { - struct PredIsPrefixedWith - { - public: - PredIsPrefixedWith(PCTSTR pszPrefix, TRemovedObjects& rRemovedObjects) : m_strPrefix(pszPrefix), m_rRemovedObjects(rRemovedObjects) {} - PredIsPrefixedWith(const PredIsPrefixedWith& rSrc) : m_strPrefix(rSrc.m_strPrefix), m_rRemovedObjects(rSrc.m_rRemovedObjects) {} - PredIsPrefixedWith() = delete; - - PredIsPrefixedWith& operator=(const PredIsPrefixedWith&) = delete; - - bool operator()(const ConfigNode& rNode) const - { - if (rNode.m_strNodeName.Get().StartsWith(m_strPrefix.c_str())) - { - m_rRemovedObjects.Add(rNode.m_oidObjectID); - return true; - } - return false; - } - - TString m_strPrefix; - TRemovedObjects& m_rRemovedObjects; - }; - } - - void ConfigNodeContainer::DeleteNode(PCTSTR pszPropName) - { - boost::unique_lock lock(m_lock); - - PredIsPrefixedWith pred(pszPropName, m_setRemovedObjects); - - bool bWasFoundBefore = false; - ConfigNodeContainer::NodeContainer::iterator iterCurrent = m_mic.begin(); - while (iterCurrent != m_mic.end()) - { - // NOTE: PredIsPrefixedWith registers the object IDs as deleted in m_setRemovedObjects (for change management purposes) - if (pred(*iterCurrent)) - { - iterCurrent = m_mic.erase(iterCurrent); - bWasFoundBefore = true; - } - else if (bWasFoundBefore) - break; // as the elements are sorted, when we matched something earlier and now we don't - it means that there are no more matching elements - else - ++iterCurrent; - } - } - - bool ConfigNodeContainer::ExtractNodes(PCTSTR pszNode, ConfigNodeContainer& tNewContainer) const - { - bool bFound = false; - TString strReplace(pszNode); - strReplace += _T("."); - - boost::unique_lock dst_lock(tNewContainer.m_lock); - tNewContainer.m_mic.clear(); - - boost::shared_lock lock(m_lock); - - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - if (iter->m_strNodeName.Get().StartsWith(strReplace.c_str())) - { - bFound = true; - - TString strName = iter->m_strNodeName.Get(); - strName.MidSelf(strReplace.GetLength()); - - tNewContainer.m_mic.insert(ConfigNode(++tNewContainer.m_oidLastObjectID, strName, iter->GetOrder(), iter->m_strValue)); - } - } - - return bFound; - } - - bool ConfigNodeContainer::ExtractMultipleNodes(PCTSTR pszNode, std::vector& tNewContainers) const - { - bool bFound = false; - TString strReplace(pszNode); - strReplace += _T("["); - - boost::shared_lock lock(m_lock); - - size_t stLastIndex = std::numeric_limits::max(); - ConfigNodeContainer* pCurrentContainer = nullptr; - - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - if (iter->m_strNodeName.Get().StartsWith(strReplace.c_str())) - { - bFound = true; - - TString strName = iter->m_strNodeName.Get(); - strName.MidSelf(strReplace.GetLength()); - size_t stPos = strName.Find(_T("]")); - if (stPos == std::numeric_limits::max()) - throw TCoreException(eErr_InvalidData, L"] character not found", LOCATION); - if (strName.GetAt(stPos + 1) != _T('.')) - throw TCoreException(eErr_InvalidData, L". character not found", LOCATION); - - size_t stNodeIndex = boost::lexical_cast(strName.Left(stPos)); - if (stNodeIndex != stLastIndex) - { - tNewContainers.push_back(ConfigNodeContainer()); - pCurrentContainer = &tNewContainers.back(); - - stLastIndex = stNodeIndex; - } - - strName.Delete(0, stPos + 2); // skip "]." at the beginning - if (!pCurrentContainer) - throw TCoreException(eErr_InvalidPointer, L"pCurrentContainer", LOCATION); - - pCurrentContainer->m_mic.insert(ConfigNode(++pCurrentContainer->m_oidLastObjectID, strName, iter->GetOrder(), iter->m_strValue)); - } - } - - return bFound; - } - - void ConfigNodeContainer::ImportNodes(PCTSTR pszNode, const ConfigNodeContainer& tContainer) - { - boost::shared_lock src_lock(tContainer.m_lock); - boost::unique_lock lock(m_lock); - - // search for nodes in this container that starts with pszNode - TString strSearch(pszNode); - strSearch += _T("."); - - typedef std::pair PairInfo; - std::set setExistingNames; - - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - if (iter->m_strNodeName.Get().StartsWith(strSearch.c_str())) - { - setExistingNames.insert(std::make_pair(iter->m_strNodeName.Get(), iter->m_iOrder.Get())); - } - } - - NodeContainer::const_iterator iter = tContainer.m_mic.begin(); - for (; iter != tContainer.m_mic.end(); ++iter) - { - TString strNodeName = pszNode; - strNodeName += _T(".") + iter->m_strNodeName; - - std::set::iterator iterExisting = setExistingNames.find(std::make_pair(strNodeName, iter->m_iOrder.Get())); - if (iterExisting != setExistingNames.end()) - { - // node already exists - modify instead of delete+add - m_mic.modify(iter, ChangeValue(iter->m_strValue)); - - setExistingNames.erase(iterExisting); - } - else - { - // node does not exist - need to add new one - m_mic.insert(ConfigNode(++m_oidLastObjectID, strNodeName, iter->GetOrder(), iter->m_strValue)); - } - - // remove all nodes with names from setExisting - for(const PairInfo& pairNode : setExistingNames) - { - NodeContainer::iterator iterToRemove = m_mic.find(boost::make_tuple(pairNode.first, pairNode.second)); - if (iterToRemove != m_mic.end()) - m_setRemovedObjects.Add(iterToRemove->m_oidObjectID); - - m_mic.erase(iterToRemove); - } - } - } - - void ConfigNodeContainer::AddNodes(PCTSTR pszNode, const ConfigNodeContainer& tContainer) - { - boost::shared_lock src_lock(tContainer.m_lock); - boost::unique_lock lock(m_lock); - - // determine the current (max) number associated with the node name - TString strSearch(pszNode); - strSearch += _T("["); - - size_t stMaxNodeNumber = 0; - - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - TString strCurrentNode = iter->m_strNodeName.Get(); - if (strCurrentNode.StartsWith(strSearch.c_str())) - { - strCurrentNode.MidSelf(strSearch.GetLength()); - size_t stPos = strCurrentNode.Find(_T("]")); - if (stPos == std::numeric_limits::max()) - throw TCoreException(eErr_InvalidData, L"] character not found", LOCATION); - - size_t stNumber = boost::lexical_cast(strCurrentNode.Left(stPos).c_str()); - stMaxNodeNumber = std::max(stMaxNodeNumber, stNumber + 1); - } - } - - TString strNodePrefix = pszNode; - strNodePrefix += TString(_T("[")) + boost::lexical_cast(stMaxNodeNumber).c_str() + _T("]."); - - NodeContainer::const_iterator iter = tContainer.m_mic.begin(); - for (; iter != tContainer.m_mic.end(); ++iter) - { - TString strNodeName = strNodePrefix + iter->m_strNodeName; - - m_mic.insert(ConfigNode(++m_oidLastObjectID, strNodeName, iter->GetOrder(), iter->m_strValue)); - } - } - - void ConfigNodeContainer::ImportNode(TString strCurrentPath, const boost::property_tree::wiptree& rTree) - { - if (rTree.empty()) - return; - - // append separator for non-empty nodes - if (!strCurrentPath.IsEmpty()) - strCurrentPath += _T("."); - - TString strNewPath; - - // analyze subnodes (has only leaf-node(s), multiple non-leaf subnodes with same name, multiple subnodes with different names) - std::set setNodeNames; - bool bAllLeafNodes = true; - size_t stChildCount = 0; - for(const boost::property_tree::wiptree::value_type& rNode : rTree) - { - setNodeNames.insert(rNode.first.c_str()); - - if (!rNode.second.empty()) - bAllLeafNodes = false; - - ++stChildCount; - } - - // sanity check (either unique names or empty or an array with single name - size_t stNodeNamesCount = setNodeNames.size(); - if (stChildCount != stNodeNamesCount && stNodeNamesCount != 1) - throw TCoreException(eErr_InvalidData, L"Tree sanity check failed", LOCATION); - - enum EMode { eMode_LeafStringArrayEntries, eMode_LeafOrContainer, eMode_ContainerSplit }; - EMode eMode = eMode_LeafOrContainer; - - if (stNodeNamesCount == 1 && stChildCount > 1) - { - if (bAllLeafNodes) - eMode = eMode_LeafStringArrayEntries; - else - eMode = eMode_ContainerSplit; - } - - int iIndex = 0; - for(const boost::property_tree::wiptree::value_type& rNode : rTree) - { - switch (eMode) - { - case eMode_LeafStringArrayEntries: - { - strNewPath = strCurrentPath + rNode.first.c_str(); - m_mic.insert(ConfigNode(++m_oidLastObjectID, strNewPath, iIndex++, rNode.second.get_value().c_str())); - break; - } - case eMode_LeafOrContainer: - { - strNewPath = strCurrentPath + rNode.first.c_str(); - if (rNode.second.empty()) - { - // get leaf info - m_mic.insert(ConfigNode(++m_oidLastObjectID, strNewPath, 0, rNode.second.get_value().c_str())); - } - else - { - // traverse through the container - ImportNode(strNewPath, rNode.second); - } - - break; - } - case eMode_ContainerSplit: - { - strNewPath = strCurrentPath + rNode.first.c_str() + _T("[") + boost::lexical_cast(iIndex++).c_str() + _T("]"); - ImportNode(strNewPath, rNode.second); - break; - } - } - } - } - - void ConfigNodeContainer::ImportFromPropertyTree(const boost::property_tree::wiptree& rTree, boost::unique_lock&) - { - // boost::unique_lock lock(m_lock); // do not lock - locking is done outside - m_mic.clear(); - - // iterate through property tree - ImportNode(_T(""), rTree); -#ifdef _DEBUG - Dump(); -#endif - } - - void ConfigNodeContainer::ExportToPropertyTree(boost::property_tree::wiptree& rTree) const - { - rTree.clear(); - - size_t stLastBracketID = std::numeric_limits::max(); - TString strGroupNode; - - boost::property_tree::wiptree treeSubnodes; - - boost::shared_lock lock(m_lock); - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - const TString& rNodeName = iter->m_strNodeName.Get(); - - TString strNodeName = rNodeName; - - size_t stBracketPos = strNodeName.Find(_T("[")); - if (stBracketPos != TString::npos) - { - // there is a bracket in the node name - this element is a part of a hierarchy of similar nodes - size_t stSecondBracketPos = strNodeName.Find(_T("]"), stBracketPos + 1); - if (stSecondBracketPos == TString::npos) - throw TCoreException(eErr_InvalidData, L"] character not found", LOCATION); - - strGroupNode = strNodeName.Left(stBracketPos); - TString strSubnodeName = strNodeName.Mid(stSecondBracketPos + 1); - wchar_t chDot = 0; - if (!strSubnodeName.GetAt(0, chDot) || chDot != L'.') - throw TCoreException(eErr_InvalidData, L". character not found", LOCATION); - strSubnodeName.Delete(0, 1); - - size_t stBracketID = boost::lexical_cast(strNodeName.Mid(stBracketPos + 1, stSecondBracketPos - stBracketPos - 1)); - if (stBracketID != stLastBracketID) - { - // new ID - add new property tree node - if (!treeSubnodes.empty()) - { - rTree.add_child(strGroupNode.c_str(), treeSubnodes); - treeSubnodes.clear(); - } - } - - // same ID - add new element to existing property tree node - treeSubnodes.put(strSubnodeName.c_str(), iter->m_strValue.Get()); - stLastBracketID = stBracketID; - } - else - { - // add the subnodes from previous bracket-based entries - if (!treeSubnodes.empty()) - { - rTree.add_child(strGroupNode.c_str(), treeSubnodes); - treeSubnodes.clear(); - } - - // no bracket in the node name - this is just a standard entry - rTree.add(strNodeName.c_str(), iter->m_strValue.Get()); - } - } - - // add the last subnode if not empty - if (!treeSubnodes.empty()) - rTree.add_child(strGroupNode.c_str(), treeSubnodes); - } - -#ifdef _DEBUG - void ConfigNodeContainer::Dump() - { - const size_t stBufferSize = 1024; - TCHAR szBuffer[stBufferSize]; - - for (NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) - { - unsigned long long ullID = iter->m_oidObjectID; - int iNodeNameModified = iter->m_strNodeName.IsModified() ? 1 : 0; - int iNodeNameModified2 = iter->m_setModifications[ConfigNode::eMod_NodeName]; - int iOrderModified = iter->m_iOrder.IsModified() ? 1 : 0; - int iOrderModified2 = iter->m_setModifications[ConfigNode::eMod_Order]; - int iValueModified = iter->m_strValue.IsModified() ? 1 : 0; - int iValueModified2 = iter->m_setModifications[ConfigNode::eMod_Value]; - bool bAdded = iter->m_setModifications[ConfigNode::eMod_Added]; - - _sntprintf_s(szBuffer, stBufferSize, _TRUNCATE, _T("Node (oid %I64u): %s(%d/%d).%d(%d/%d) = %s(%d/%d)%s\n"), ullID, - iter->m_strNodeName.Get().c_str(), iNodeNameModified, iNodeNameModified2, - iter->m_iOrder.Get(), iOrderModified, iOrderModified2, - iter->m_strValue.Get().c_str(), iValueModified, iValueModified2, - bAdded ? L" [added]" : L""); - OutputDebugString(szBuffer); - } - } -#endif - - void ConfigNodeContainer::AddEntry(PCTSTR pszPropName, int iIndex, const TString& strValue) - { - std::pair pairInsert = m_mic.insert(ConfigNode(++m_oidLastObjectID, pszPropName, iIndex, strValue)); - pairInsert.first->m_setModifications.reset(); - } - } -}