Index: src/ch/ch.cpp =================================================================== diff -u -rcbe8851b4ff9b0f235e2c700174370b8884cb844 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/ch/ch.cpp (.../ch.cpp) (revision cbe8851b4ff9b0f235e2c700174370b8884cb844) +++ src/ch/ch.cpp (.../ch.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -26,6 +26,7 @@ #include "CrashDlg.h" #include "../common/version.h" #include "TCommandLineParser.h" +#include "../libchcore/TStringSet.h" #ifdef _DEBUG #define new DEBUG_NEW Index: src/libchcore/ConfigNode.cpp =================================================================== diff -u --- src/libchcore/ConfigNode.cpp (revision 0) +++ src/libchcore/ConfigNode.cpp (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -0,0 +1,47 @@ +// ============================================================================ +// 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 "ConfigNode.h" + +BEGIN_CHCORE_NAMESPACE + +namespace details +{ + + ConfigNode::ConfigNode(size_t stObjectID, const TString& strNodeName, int iOrder, const TString& strValue) : + m_stObjectID(stObjectID), + m_iOrder(m_setModifications, iOrder), + m_strNodeName(m_setModifications, strNodeName), + m_strValue(m_setModifications, strValue) + { + m_setModifications[eMod_Added] = true; + } + + TString ConfigNode::GetNodeName() const + { + return m_strNodeName; + } + + int ConfigNode::GetOrder() const + { + return m_iOrder; + } +} + +END_CHCORE_NAMESPACE Index: src/libchcore/ConfigNode.h =================================================================== diff -u --- src/libchcore/ConfigNode.h (revision 0) +++ src/libchcore/ConfigNode.h (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -0,0 +1,63 @@ +// ============================================================================ +// 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. +// ============================================================================ +#ifndef __TCONFIGNODE_H__ +#define __TCONFIGNODE_H__ + +#include "TString.h" +#include +#include +#include "TSharedModificationTracker.h" + +BEGIN_CHCORE_NAMESPACE + +namespace details +{ + class ConfigNode + { + public: + ConfigNode(size_t stObjectID, const TString& strNodeName, int iOrder, const TString& strValue); + + TString GetNodeName() const; + int GetOrder() const; + + public: + enum EModifications + { + eMod_None = 0, + eMod_Added = 1, + eMod_NodeName, + eMod_Value, + eMod_Order, + + eMod_Last + }; + + typedef std::bitset Bitset; + mutable Bitset m_setModifications; + + size_t m_stObjectID; + TSharedModificationTracker m_iOrder; + TSharedModificationTracker m_strNodeName; + TSharedModificationTracker m_strValue; + }; +} + +END_CHCORE_NAMESPACE + +#endif Index: src/libchcore/ConfigNodeContainer.cpp =================================================================== diff -u --- src/libchcore/ConfigNodeContainer.cpp (revision 0) +++ src/libchcore/ConfigNodeContainer.cpp (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -0,0 +1,564 @@ +// ============================================================================ +// 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 + +BEGIN_CHCORE_NAMESPACE + +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_stLastObjectID(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_stLastObjectID = rSrc.m_stLastObjectID; + } + + 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_stLastObjectID = rSrc.m_stLastObjectID; + + 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) + { + bool bResult = false; + + 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_stLastObjectID, 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, tChange); + bResult |= tChange.WasModified(); + + ++pairFnd.first; + } + else + { + // delete this item + m_setRemovedObjects.Add(pairFnd.first->m_stObjectID); + 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_stLastObjectID, pszPropName, boost::numeric_cast(stIndex), rValue.GetAt(stIndex))); + ++stIndex; + } + + return true; + } + } + + void ConfigNodeContainer::DeleteNode(PCTSTR pszPropName) + { + boost::unique_lock lock(m_lock); + + std::pair pairFnd + = m_mic.equal_range(boost::make_tuple(pszPropName)); + + if(pairFnd.first == m_mic.end()) + return; + + ConfigNodeContainer::NodeContainer::const_iterator iter = pairFnd.first; + while(iter != m_mic.end() && iter != pairFnd.second) + { + m_setRemovedObjects.Add(iter->m_stObjectID); + } + + m_mic.erase(pairFnd.first, pairFnd.second); + } + + 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)) + { + bFound = true; + + TString strName = iter->m_strNodeName.Get(); + strName.MidSelf(strReplace.GetLength()); + + tNewContainer.m_mic.insert(ConfigNode(++tNewContainer.m_stLastObjectID, 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 = NULL; + + for(NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) + { + if(iter->m_strNodeName.Get().StartsWith(strReplace)) + { + 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_CORE_EXCEPTION(eErr_InvalidData); + + size_t stNodeIndex = boost::lexical_cast(strName.Left(stPos)); + if(stNodeIndex != stLastIndex) + { + tNewContainers.push_back(ConfigNodeContainer()); + pCurrentContainer = &tNewContainers.back(); + + stLastIndex = stNodeIndex; + } + + pCurrentContainer->m_mic.insert(ConfigNode(++pCurrentContainer->m_stLastObjectID, strName.Mid(stPos + 1), 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)) + { + setExistingNames.insert(std::make_pair(iter->m_strNodeName.Get(), iter->m_iOrder)); + } + } + + 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)); + 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_stLastObjectID, strNodeName, iter->GetOrder(), iter->m_strValue)); + } + + // remove all nodes with names from setExisting + BOOST_FOREACH(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_stObjectID); + + 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)) + { + strCurrentNode.MidSelf(strSearch.GetLength()); + size_t stPos = strCurrentNode.Find(_T("]")); + if(stPos == std::numeric_limits::max()) + THROW_CORE_EXCEPTION(eErr_InvalidData); + + size_t stNumber = boost::lexical_cast(strCurrentNode.Left(stPos)); + 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_stLastObjectID, 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; + BOOST_FOREACH(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_CORE_EXCEPTION(eErr_InvalidData); + + enum EMode { eMode_LeafStringArrayEntries, eMode_LeafOrContainer, eMode_ContainerSplit, eMode_ContainerPassThrough }; + EMode eMode = eMode_LeafStringArrayEntries; + + if(stNodeNamesCount == 1 && stChildCount > 1) + { + if(bAllLeafNodes) + eMode = eMode_LeafStringArrayEntries; + else + eMode = eMode_ContainerSplit; + } + else + eMode = eMode_LeafOrContainer; + + int iIndex = 0; + BOOST_FOREACH(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_stLastObjectID, 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_stLastObjectID, 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; + } + case eMode_ContainerPassThrough: + { + 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); + + Dump(); + } + + void ConfigNodeContainer::ExportToPropertyTree(boost::property_tree::wiptree& rTree) const + { + rTree.clear(); + + size_t stLastBracketID = std::numeric_limits::max(); + TString strGroupNode; + + TCHAR szData[1024]; +szData; + boost::property_tree::wiptree treeSubnodes; + + int iNode = 0; +iNode; + 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(); + + // tmp + //_sntprintf_s(szData, 1024, _TRUNCATE, _T("ExportToPropertyTree (%ld): %s\n"), iNode++, (PCTSTR)rNodeName); +// OutputDebugString(szData); + // /tmp + + 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_CORE_EXCEPTION(eErr_InvalidData); + + strGroupNode = strNodeName.Left(stBracketPos); + TString strSubnodeName = strNodeName.Mid(stSecondBracketPos + 1); + + size_t stBracketID = boost::lexical_cast(strNodeName.Mid(stBracketPos, stSecondBracketPos - stBracketPos - 1)); + if(stBracketID != stLastBracketID) + { + // new ID - add new property tree node + if(!treeSubnodes.empty()) + { + rTree.add_child((PCTSTR)strGroupNode, treeSubnodes); + treeSubnodes.clear(); + } + } + + // same ID - add new element to existing property tree node + treeSubnodes.put((PCTSTR)strSubnodeName, iter->m_strValue); + } + else + { + // add the subnodes from previous bracket-based entries + if(!treeSubnodes.empty()) + { + rTree.add_child((PCTSTR)strGroupNode, treeSubnodes); + treeSubnodes.clear(); + } + + // no bracket in the node name - this is just a standard entry + rTree.add((PCTSTR)strNodeName, iter->m_strValue); + } + } + + // add the last subnode if not empty + if(!treeSubnodes.empty()) + rTree.add_child((PCTSTR)strGroupNode, treeSubnodes); + } + + void ConfigNodeContainer::Dump() + { + const size_t stBufferSize = 1024; + TCHAR szBuffer[stBufferSize]; + + for(NodeContainer::const_iterator iter = m_mic.begin(); iter != m_mic.end(); ++iter) + { + _sntprintf_s(szBuffer, stBufferSize, _TRUNCATE, _T("Node (oid %I64u): %s.%ld = %s\n"), iter->m_stObjectID, (PCTSTR)iter->m_strNodeName.Get(), iter->m_iOrder.Get(), (PCTSTR)iter->m_strValue.Get()); + OutputDebugString(szBuffer); + } + } + + void ConfigNodeContainer::AddEntry(PCTSTR pszPropName, int iIndex, const TString& strValue) + { + m_mic.insert(ConfigNode(++m_stLastObjectID, pszPropName, iIndex, strValue)); + } +} + +END_CHCORE_NAMESPACE Index: src/libchcore/ConfigNodeContainer.h =================================================================== diff -u --- src/libchcore/ConfigNodeContainer.h (revision 0) +++ src/libchcore/ConfigNodeContainer.h (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -0,0 +1,268 @@ +// ============================================================================ +// 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. +// ============================================================================ +#ifndef __CONFIGNODECONTAINER_H__ +#define __CONFIGNODECONTAINER_H__ + +#include "TSharedModificationTracker.h" +#include "ConfigNode.h" +#include + +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable: 4100 4702 4512) + #include +#pragma warning(pop) +#include "TStringSet.h" +#include "TStringArray.h" +#include "TRemovedObjects.h" +#include +#include + +BEGIN_CHCORE_NAMESPACE + +namespace details +{ + struct ChangeValue + { + ChangeValue(const TString& strNewValue); + + void operator()(ConfigNode& rNode); + + bool WasModified() const; + + private: + TString m_strNewValue; + bool m_bWasModified; + }; + + struct ChangeOrderAndValue + { + ChangeOrderAndValue(const TString& tNewValue, int iOrder); + + void operator()(ConfigNode& rNode); + + bool WasModified() const; + + private: + TString m_strNewValue; + int m_iOrder; + bool m_bWasModified; + }; + + struct ConfigNodeContainer + { + public: + ConfigNodeContainer(); + ConfigNodeContainer(const ConfigNodeContainer& rSrc); + + ConfigNodeContainer& operator=(const ConfigNodeContainer& rSrc); + + void AddEntry(PCTSTR pszPropName, int iIndex, const TString& strValue); + + // get/set single values + template + T GetValue(PCTSTR pszPropName, const T& rDefaultValue) const + { + boost::shared_lock lock(m_lock); + T tResult = rDefaultValue; + + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + tResult = boost::lexical_cast((PCTSTR)(*iterFnd).m_strValue.Get()); + + return tResult; + } + + template<> + TString GetValue(PCTSTR pszPropName, const TString& rDefaultValue) const + { + boost::shared_lock lock(m_lock); + TString tResult = rDefaultValue; + + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + tResult = (*iterFnd).m_strValue; + + return tResult; + } + + template<> + bool GetValue(PCTSTR pszPropName, const bool& bDefaultValue) const + { + boost::shared_lock lock(m_lock); + bool bResult = bDefaultValue; + + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + if((*iterFnd).m_strValue.Get().CompareNoCase(_T("false")) == 0) + bResult = false; + else if((*iterFnd).m_strValue.Get().CompareNoCase(_T("true")) == 0) + bResult = true; + else + bResult = boost::lexical_cast((PCTSTR)(*iterFnd).m_strValue.Get()); + } + + return bResult; + } + + template + bool GetValueNoDefault(PCTSTR pszPropName, T& rValue) const + { + boost::shared_lock lock(m_lock); + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + rValue = boost::lexical_cast((PCTSTR)(*iterFnd).m_strValue.Get()); + return true; + } + + return false; + } + + template<> + bool GetValueNoDefault(PCTSTR pszPropName, TString& rValue) const + { + boost::shared_lock lock(m_lock); + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + rValue = (*iterFnd).m_strValue; + return true; + } + + return false; + } + + template<> + bool GetValueNoDefault(PCTSTR pszPropName, bool& rValue) const + { + boost::shared_lock lock(m_lock); + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + const TString& strValue = (*iterFnd).m_strValue.Get(); + if(strValue.CompareNoCase(_T("false")) == 0) + rValue = false; + else if(strValue.CompareNoCase(_T("true")) == 0) + rValue = true; + else + rValue = boost::lexical_cast((PCTSTR)strValue); + return true; + } + + return false; + } + + template + bool SetValue(PCTSTR pszPropName, const T& rValue) + { + boost::unique_lock lock(m_lock); + + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + ChangeValue tChange(boost::lexical_cast(rValue).c_str()); + m_mic.modify(iterFnd, tChange); + return tChange.WasModified(); + } + else + { + m_mic.insert(ConfigNode(++m_stLastObjectID, pszPropName, 0, boost::lexical_cast(rValue).c_str())); + return true; + } + } + + template<> + bool SetValue(PCTSTR pszPropName, const bool& bValue) + { + boost::unique_lock lock(m_lock); + + ConfigNodeContainer::NodeContainer::const_iterator iterFnd = m_mic.find(boost::make_tuple(pszPropName, 0)); + if(iterFnd != m_mic.end()) + { + ChangeValue tChange(boost::lexical_cast(bValue ? _T("true") : _T("false")).c_str()); + m_mic.modify(iterFnd, tChange); + return tChange.WasModified(); + } + else + { + m_mic.insert(ConfigNode(++m_stLastObjectID, pszPropName, 0, boost::lexical_cast(bValue).c_str())); + return true; + } + } + + // vector-based values + TStringArray GetArrayValue(PCTSTR pszPropName, const TStringArray& rDefaultValue) const; + bool GetArrayValueNoDefault(PCTSTR pszPropName, TStringArray& rValue) const; + bool SetArrayValue(PCTSTR pszPropName, const TStringArray& rValue); + + // deletion + void DeleteNode(PCTSTR pszPropName); + + // extracting nodes + bool ExtractNodes(PCTSTR pszNode, ConfigNodeContainer& tNewContainer) const; + bool ExtractMultipleNodes(PCTSTR pszNode, std::vector& tNewContainers) const; + + void ImportNodes(PCTSTR pszNode, const ConfigNodeContainer& tContainer); // replaces specified node with data from tContainer + void AddNodes(PCTSTR pszNode, const ConfigNodeContainer& tContainer); // adds specified config as a newly numbered node in this container + + void ImportFromPropertyTree(const boost::property_tree::wiptree& rTree, boost::unique_lock&); + void ExportToPropertyTree(boost::property_tree::wiptree& rTree) const; + + // debugging + void Dump(); + + private: + void ImportNode(TString strCurrentPath, const boost::property_tree::wiptree& rTree); + + public: + typedef boost::multi_index_container, + boost::multi_index::const_mem_fun + > + > + > + > NodeContainer; + + NodeContainer m_mic; + + TString m_strFilePath; + + boost::signals2::signal m_notifier; + TStringSet m_setDelayedNotifications; + bool m_bDelayedEnabled; + size_t m_stLastObjectID; + + TRemovedObjects m_setRemovedObjects; + + mutable boost::shared_mutex m_lock; + }; +} + +END_CHCORE_NAMESPACE + +#endif Index: src/libchcore/TConfig.cpp =================================================================== diff -u -r960167a493c3ae7ecbdc7e8c2b91619106d7a685 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TConfig.cpp (.../TConfig.cpp) (revision 960167a493c3ae7ecbdc7e8c2b91619106d7a685) +++ src/libchcore/TConfig.cpp (.../TConfig.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -26,93 +26,50 @@ #include #include #include "../libicpf/exception.h" -#include "TBinarySerializer.h" -#include "SerializationHelpers.h" #include "TConfigArray.h" #pragma warning(push) #pragma warning(disable: 4702 4512) -#include + #include #pragma warning(pop) + #include #include #include "TConfigNotifier.h" +#include "ConfigNodeContainer.h" +#include "ErrorCodes.h" +#include "TCoreException.h" +#include "TRowData.h" +#include "ISerializerRowData.h" BEGIN_CHCORE_NAMESPACE ///////////////////////////////////////////////////////////////////////////////////////////// -// common set/get templates -template -bool InternalGetValue(const boost::property_tree::wiptree& rTree, PCTSTR pszPropName, T& tOutValue, boost::shared_mutex& rLock) -{ - boost::shared_lock lock(rLock); - - boost::optional tValue = rTree.get_optional(pszPropName); - if(tValue.is_initialized()) - { - tOutValue = tValue.get(); - return true; - } - else - return false; -} - -template -bool InternalSetValue(boost::property_tree::wiptree& rTree, bool& bModified, boost::shared_mutex& rLock, PCTSTR pszPropName, const T& tNewValue) -{ - // separate scope for mutex (to avoid calling notifier inside critical section) - boost::upgrade_lock lock(rLock); - - boost::optional tValue = rTree.get_optional(pszPropName); - if(!tValue.is_initialized() || tValue.get() != tNewValue) - { - boost::upgrade_to_unique_lock upgraded_lock(lock); - rTree.put(pszPropName, tNewValue); - bModified = true; - return true; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////////// // class TConfig +using namespace details; + TConfig::TConfig() : - m_bDelayedEnabled(false), - m_bModified(false) + m_pImpl(new details::ConfigNodeContainer) { } TConfig::TConfig(const TConfig& rSrc) : - m_bDelayedEnabled(false), - m_bModified(rSrc.m_bModified) + m_pImpl(new details::ConfigNodeContainer(*rSrc.m_pImpl)) { - boost::shared_lock lock(rSrc.m_lock); - - m_propTree = rSrc.m_propTree; - m_strFilePath = rSrc.m_strFilePath; } TConfig& TConfig::operator=(const TConfig& rSrc) { if(this != &rSrc) - { - boost::shared_lock src_lock(rSrc.m_lock); - boost::unique_lock lock(m_lock); + *m_pImpl = *rSrc.m_pImpl; - m_propTree = rSrc.m_propTree; - m_bModified = rSrc.m_bModified; - m_strFilePath = rSrc.m_strFilePath; - m_bDelayedEnabled = false; - m_setDelayedNotifications.Clear(); - } - return *this; } TConfig::~TConfig() { + delete m_pImpl; } // read/write @@ -121,491 +78,340 @@ if(!pszFile) THROW(_T("Invalid argument"), 0, 0, 0); - boost::unique_lock lock(m_lock); + // convert our underlying data to a property tree (currently probably the easiest way to convert data to xml + boost::property_tree::wiptree tPropertyTree; + std::wifstream ifs(pszFile, std::ios_base::in); + boost::property_tree::xml_parser::read_xml(ifs, tPropertyTree); + + boost::unique_lock lock(GetImpl()->m_lock); + // Note: we need to store filename for later use BEFORE trying to open a file // since it might be nonexistent, but we still would like to store config to this file later - ClearNL(); // also clears m_bModified - m_strFilePath = pszFile; + ClearNL(); + GetImpl()->m_strFilePath = pszFile; - std::wifstream ifs(m_strFilePath, std::ios_base::in); - try - { - boost::property_tree::xml_parser::read_xml(ifs, m_propTree); - } - catch(...) - { - m_propTree.clear(); - throw; - } + GetImpl()->ImportFromPropertyTree(tPropertyTree, lock); } -void TConfig::Write(bool bOnlyIfModified) +void TConfig::Write() { - boost::shared_lock lock(m_lock); - if(!bOnlyIfModified || m_bModified) - { - std::wofstream ofs(m_strFilePath, std::ios_base::out); + // NOTE: locking is done inside ExportToPropertyTree() + boost::property_tree::wiptree tPropertyTree; + GetImpl()->ExportToPropertyTree(tPropertyTree); - boost::property_tree::xml_parser::write_xml(ofs, m_propTree); - m_bModified = false; - } + std::wofstream ofs(GetImpl()->m_strFilePath, std::ios_base::out); + boost::property_tree::xml_parser::write_xml(ofs, tPropertyTree); } -void TConfig::SerializeLoad(TReadBinarySerializer& rSerializer) +void TConfig::ReadFromString(const TString& strInput) { - using namespace Serializers; + if(strInput.IsEmpty()) + THROW(_T("Invalid argument"), 0, 0, 0); - boost::property_tree::wiptree propTree; + boost::property_tree::wiptree tPropertyTree; - SerializeLoadNode(rSerializer, propTree); + std::wistringstream ifs((const wchar_t*)strInput, std::ios_base::in); + boost::property_tree::xml_parser::read_xml(ifs, tPropertyTree); - boost::unique_lock lock(m_lock); - m_propTree = propTree; -} + boost::unique_lock lock(GetImpl()->m_lock); -void TConfig::SerializeLoadNode(TReadBinarySerializer& rSerializer, boost::property_tree::wiptree& treeNode) -{ - using namespace Serializers; + ClearNL(); - size_t stCount = 0; - Serialize(rSerializer, stCount); - - while(stCount--) - { - // name of the node - TString strNodeName; - Serialize(rSerializer, strNodeName); - - bool bValue = false; - Serialize(rSerializer, bValue); - - if(bValue) - { - // value - TString strNodeValue; - Serialize(rSerializer, strNodeValue); - - treeNode.add((PCTSTR)strNodeName, (PCTSTR)strNodeValue); - } - else - { - boost::property_tree::wiptree& rSubnodeTree = treeNode.add_child((PCTSTR)strNodeName, boost::property_tree::wiptree()); - SerializeLoadNode(rSerializer, rSubnodeTree); - } - } + GetImpl()->ImportFromPropertyTree(tPropertyTree, lock); } -void TConfig::SerializeStore(TWriteBinarySerializer& rSerializer) +void TConfig::WriteToString(TString& strOutput) { - using namespace Serializers; + // NOTE: locking is done inside ExportToPropertyTree() - boost::property_tree::wiptree propTree; + boost::property_tree::wiptree tPropertyTree; + GetImpl()->ExportToPropertyTree(tPropertyTree); - // make a copy of internal tree (with locking) to avoid locking within serialization operation - { - boost::shared_lock lock(m_lock); - propTree = m_propTree; - } + std::wostringstream ofs(std::ios_base::out); + boost::property_tree::xml_parser::write_xml(ofs, tPropertyTree); - // serialize the copy - SerializeStoreNode(rSerializer, propTree); + strOutput = ofs.str().c_str(); } -void TConfig::SerializeStoreNode(TWriteBinarySerializer& rSerializer, boost::property_tree::wiptree& treeNode) + +void TConfig::Store(const ISerializerContainerPtr& spContainer) const { - using namespace Serializers; - Serialize(rSerializer, treeNode.size()); + if(!spContainer) + THROW_CORE_EXCEPTION(eErr_InvalidPointer); - for(boost::property_tree::wiptree::iterator iterNode = treeNode.begin(); iterNode != treeNode.end(); ++iterNode) - { - // is this node the leaf one? - boost::property_tree::wiptree& rSubNode = (*iterNode).second; + boost::shared_lock lock(GetImpl()->m_lock); - // name of the node - Serialize(rSerializer, (*iterNode).first.c_str()); + spContainer->DeleteRows(m_pImpl->m_setRemovedObjects); - bool bValue = rSubNode.empty(); - Serialize(rSerializer, bValue); + BOOST_FOREACH(const ConfigNode& rNode, m_pImpl->m_mic) + { + ISerializerRowDataPtr spRow; - if(bValue) - { - // value - Serialize(rSerializer, rSubNode.data().c_str()); - } + bool bAdded = rNode.m_setModifications[ConfigNode::eMod_Added]; + if(bAdded) + spRow = spContainer->AddRow(rNode.m_stObjectID); + else if(rNode.m_setModifications.any()) + spRow = spContainer->GetRow(rNode.m_stObjectID); else - { - // store children - SerializeStoreNode(rSerializer, rSubNode); - } - } -} + continue; -void TConfig::ReadFromString(const TString& strInput) -{ - if(strInput.IsEmpty()) - THROW(_T("Invalid argument"), 0, 0, 0); + if(bAdded || rNode.m_setModifications[ConfigNode::eMod_NodeName]) + *spRow % TRowData(_T("name"), rNode.GetNodeName()); + if(bAdded || rNode.m_setModifications[ConfigNode::eMod_Order]) + *spRow % TRowData(_T("node_order"), rNode.GetOrder()); + if(bAdded || rNode.m_setModifications[ConfigNode::eMod_Value]) + *spRow % TRowData(_T("value"), rNode.m_strValue.Get()); - boost::unique_lock lock(m_lock); - - ClearNL(); // also clears m_bModified - - std::wistringstream ifs((const wchar_t*)strInput, std::ios_base::in); - try - { - boost::property_tree::xml_parser::read_xml(ifs, m_propTree); + rNode.m_setModifications.reset(); } - catch(...) - { - m_propTree.clear(); - throw; - } } -void TConfig::WriteToString(TString& strOutput) +void TConfig::Load(const ISerializerContainerPtr& spContainer) const { - boost::shared_lock lock(m_lock); + if(!spContainer) + THROW_CORE_EXCEPTION(eErr_InvalidPointer); - std::wostringstream ofs(std::ios_base::out); + boost::unique_lock lock(GetImpl()->m_lock); + m_pImpl->m_setRemovedObjects.Clear(); + m_pImpl->m_mic.clear(); - boost::property_tree::xml_parser::write_xml(ofs, m_propTree); + ISerializerRowReaderPtr spRowReader = spContainer->GetRowReader(); + IColumnsDefinitionPtr spColumns = spRowReader->GetColumnsDefinitions(); + if(spColumns->IsEmpty()) + *spColumns % _T("name") % _T("node_order") % _T("value"); - strOutput = ofs.str().c_str(); -} - -void TConfig::SetFilePath(PCTSTR pszPath) -{ - boost::unique_lock lock(m_lock); - if(m_strFilePath != pszPath) + while(spRowReader->Next()) { - m_strFilePath = pszPath; - m_bModified = true; // since the path is modified, we can only assume that stuff needs to be written into it regardless of the current modification state - } -} + TString strName; + int iOrder = 0; + TString strValue; -bool TConfig::IsModified() const -{ - boost::shared_lock lock(m_lock); - return m_bModified; -} + spRowReader->GetValue(_T("name"), strName); + spRowReader->GetValue(_T("node_order"), iOrder); + spRowReader->GetValue(_T("value"), strValue); -void TConfig::MarkAsModified() -{ - boost::unique_lock lock(m_lock); - m_bModified = true; + m_pImpl->AddEntry(strName, iOrder, strValue); + } } -void TConfig::MarkAsNotModified() +void TConfig::SetFilePath(PCTSTR pszPath) { - boost::unique_lock lock(m_lock); - m_bModified = false; + boost::unique_lock lock(GetImpl()->m_lock); + GetImpl()->m_strFilePath = pszPath; } void TConfig::Clear() { - boost::unique_lock lock(m_lock); + boost::unique_lock lock(GetImpl()->m_lock); ClearNL(); } void TConfig::ClearNL() { - m_propTree.clear(); - m_bModified = false; - m_setDelayedNotifications.Clear(); - m_bDelayedEnabled = false; - m_strFilePath.Clear(); + GetImpl()->m_mic.clear(); + GetImpl()->m_setDelayedNotifications.Clear(); + GetImpl()->m_bDelayedEnabled = false; + GetImpl()->m_strFilePath.Clear(); } // value setting/retrieval bool TConfig::GetBool(PCTSTR pszPropName, bool bDefault) const { - bool bResult = bDefault; - if(!InternalGetValue(m_propTree, pszPropName, bResult, m_lock)) - bResult = bDefault; - return bResult; + return GetImpl()->GetValue(pszPropName, bDefault); } bool TConfig::GetValue(PCTSTR pszPropName, bool& bValue) const { - return InternalGetValue(m_propTree, pszPropName, bValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, bValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, bool bValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, bValue)) + if(GetImpl()->SetValue(pszPropName, bValue)) SendNotification(pszPropName); return *this; } int TConfig::GetInt(PCTSTR pszPropName, int iDefault) const { - int iResult = 0; - if(!InternalGetValue(m_propTree, pszPropName, iResult, m_lock)) - iResult = iDefault; - return iResult; + return GetImpl()->GetValue(pszPropName, iDefault); } bool TConfig::GetValue(PCTSTR pszPropName, int& iValue) const { - return InternalGetValue(m_propTree, pszPropName, iValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, iValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, int iValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, iValue)) + if(GetImpl()->SetValue(pszPropName, iValue)) SendNotification(pszPropName); return *this; } unsigned int TConfig::GetUInt(PCTSTR pszPropName, unsigned int uiDefault) const { - boost::shared_lock lock(m_lock); - return m_propTree.get(pszPropName, uiDefault); + return GetImpl()->GetValue(pszPropName, uiDefault); } bool TConfig::GetValue(PCTSTR pszPropName, unsigned int& uiValue) const { - return InternalGetValue(m_propTree, pszPropName, uiValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, uiValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, unsigned int uiValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, uiValue)) + if(GetImpl()->SetValue(pszPropName, uiValue)) SendNotification(pszPropName); return *this; } long long TConfig::GetLongLong(PCTSTR pszPropName, long long llDefault) const { - boost::shared_lock lock(m_lock); - return m_propTree.get(pszPropName, llDefault); + return GetImpl()->GetValue(pszPropName, llDefault); } bool TConfig::GetValue(PCTSTR pszPropName, long long& llValue) const { - return InternalGetValue(m_propTree, pszPropName, llValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, llValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, long long llValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, llValue)) + if(GetImpl()->SetValue(pszPropName, llValue)) SendNotification(pszPropName); return *this; } unsigned long long TConfig::GetULongLong(PCTSTR pszPropName, unsigned long long ullDefault) const { - boost::shared_lock lock(m_lock); - return m_propTree.get(pszPropName, ullDefault); + return GetImpl()->GetValue(pszPropName, ullDefault); } bool TConfig::GetValue(PCTSTR pszPropName, unsigned long long& ullValue) const { - return InternalGetValue(m_propTree, pszPropName, ullValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, ullValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, unsigned long long ullValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, ullValue)) + if(GetImpl()->SetValue(pszPropName, ullValue)) SendNotification(pszPropName); return *this; } double TConfig::GetDouble(PCTSTR pszPropName, double dDefault) const { - boost::shared_lock lock(m_lock); - return m_propTree.get(pszPropName, dDefault); + return GetImpl()->GetValue(pszPropName, dDefault); } bool TConfig::GetValue(PCTSTR pszPropName, double& dValue) const { - return InternalGetValue(m_propTree, pszPropName, dValue, m_lock); + return GetImpl()->GetValueNoDefault(pszPropName, dValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, double dValue) { - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, dValue)) + if(GetImpl()->SetValue(pszPropName, dValue)) SendNotification(pszPropName); return *this; } TString TConfig::GetString(PCTSTR pszPropName, const TString& strDefault) const { - boost::shared_lock lock(m_lock); - std::wstring wstrData = m_propTree.get(pszPropName, std::wstring(strDefault)); - return wstrData.c_str(); + return GetImpl()->GetValue(pszPropName, strDefault); } bool TConfig::GetValue(PCTSTR pszPropName, TString& rstrValue) const { - std::wstring wstrData; - bool bResult = InternalGetValue(m_propTree, pszPropName, wstrData, m_lock); - rstrValue = wstrData.c_str(); - - return bResult; + return GetImpl()->GetValueNoDefault(pszPropName, rstrValue); } TConfig& TConfig::SetValue(PCTSTR pszPropName, const TString& strValue) { - std::wstring wstrData = strValue; - if(InternalSetValue(m_propTree, m_bModified, m_lock, pszPropName, wstrData)) + if(GetImpl()->SetValue(pszPropName, strValue)) SendNotification(pszPropName); return *this; } bool TConfig::GetValue(PCTSTR pszPropName, TStringArray& rvValues) const { - boost::shared_lock lock(m_lock); - rvValues.Clear(); - - // get rid of the last part of the property to get to the parent holding the real entries - std::wstring wstrPropertyName = pszPropName; - boost::iterator_range iterFnd = boost::find_last(wstrPropertyName, _T(".")); - if(iterFnd.begin() == wstrPropertyName.end()) - return false; - - std::wstring wstrNewPropName; - wstrNewPropName.insert(wstrNewPropName.end(), wstrPropertyName.begin(), iterFnd.begin()); - - boost::optional children = m_propTree.get_child_optional(wstrNewPropName); - if(children.is_initialized()) - { - BOOST_FOREACH(const boost::property_tree::wiptree::value_type& rEntry, children.get()) - { - rvValues.Add(rEntry.second.data().c_str()); - } - - return true; - } - else - return false; + return GetImpl()->GetArrayValueNoDefault(pszPropName, rvValues); } -void TConfig::SetValue(PCTSTR pszPropName, const TStringArray& rvValues) +TConfig& TConfig::SetValue(PCTSTR pszPropName, const TStringArray& rvValues) { - // get rid of the last part of the property to get to the parent holding the real entries - std::wstring wstrNewPropName; - std::wstring wstrPropertyName = pszPropName; - boost::iterator_range iterFnd = boost::find_last(wstrPropertyName, _T(".")); - if(iterFnd.begin() != wstrPropertyName.end()) - wstrNewPropName.insert(wstrNewPropName.end(), wstrPropertyName.begin(), iterFnd.begin()); - else - wstrNewPropName = pszPropName; + if(GetImpl()->SetArrayValue(pszPropName, rvValues)) + SendNotification(pszPropName); - // separate scope for mutex (to avoid calling notifier inside critical section) - { - boost::unique_lock lock(m_lock); + return *this; +} - boost::optional children = m_propTree.get_child_optional(wstrNewPropName); - if(children.is_initialized()) - children.get().clear(); - for(size_t stIndex = 0; stIndex < rvValues.GetCount(); ++stIndex) - { - m_propTree.add(pszPropName, (const wchar_t*)rvValues.GetAt(stIndex)); - } - - m_bModified = true; - } - - SendNotification(pszPropName); +void TConfig::DeleteNode(PCTSTR pszNodeName) +{ + GetImpl()->DeleteNode(pszNodeName); } // extraction of subtrees bool TConfig::ExtractSubConfig(PCTSTR pszSubTreeName, TConfig& rSubConfig) const { - boost::unique_lock dst_lock(rSubConfig.m_lock); - rSubConfig.ClearNL(); - - boost::shared_lock lock(m_lock); - - boost::optional optChildren = m_propTree.get_child_optional(pszSubTreeName); - if(optChildren.is_initialized()) - { - rSubConfig.m_propTree = optChildren.get(); - return true; - } - else - return false; + return GetImpl()->ExtractNodes(pszSubTreeName, *rSubConfig.m_pImpl); } bool TConfig::ExtractMultiSubConfigs(PCTSTR pszSubTreeName, TConfigArray& rSubConfigs) const { - boost::shared_lock lock(m_lock); + rSubConfigs.Clear(); - std::wstring wstrPropertyName = pszSubTreeName; - boost::iterator_range iterFnd = boost::find_last(wstrPropertyName, _T(".")); - if(iterFnd.begin() == wstrPropertyName.end()) + std::vector vNodeContainers; + if(!GetImpl()->ExtractMultipleNodes(pszSubTreeName, vNodeContainers)) return false; - std::wstring wstrNewPropName; - wstrNewPropName.insert(wstrNewPropName.end(), wstrPropertyName.begin(), iterFnd.begin()); - - boost::optional optChildren = m_propTree.get_child_optional(wstrNewPropName); - if(optChildren.is_initialized()) + BOOST_FOREACH(const ConfigNodeContainer& rNode, vNodeContainers) { - BOOST_FOREACH(const boost::property_tree::wiptree::value_type& rEntry, optChildren.get()) - { - std::wstring strData = rEntry.first.c_str(); + TConfig cfg; + *cfg.m_pImpl = rNode; - TConfig cfg; - cfg.m_propTree = rEntry.second; - rSubConfigs.Add(cfg); - } - - return true; + rSubConfigs.Add(cfg); } - else - return false; + + return true; } void TConfig::PutSubConfig(PCTSTR pszSubTreeName, const TConfig& rSubConfig) { - boost::unique_lock lock(m_lock); - boost::shared_lock src_lock(rSubConfig.m_lock); - - m_propTree.put_child(pszSubTreeName, rSubConfig.m_propTree); - - m_bModified = true; + GetImpl()->ImportNodes(pszSubTreeName, *rSubConfig.m_pImpl); } void TConfig::AddSubConfig(PCTSTR pszSubTreeName, const TConfig& rSubConfig) { - boost::unique_lock lock(m_lock); - boost::shared_lock src_lock(rSubConfig.m_lock); - - m_propTree.add_child(pszSubTreeName, rSubConfig.m_propTree); - - m_bModified = true; + GetImpl()->AddNodes(pszSubTreeName, *rSubConfig.m_pImpl); } -void TConfig::DeleteNode(PCTSTR pszNodeName) -{ - boost::unique_lock lock(m_lock); - m_propTree.erase(pszNodeName); -} - void TConfig::ConnectToNotifier(void (*pfnCallback)(const TStringSet&, void*), void* pParam) { - m_notifier.connect(TConfigNotifier(pfnCallback, pParam)); + boost::unique_lock lock(GetImpl()->m_lock); + GetImpl()->m_notifier.connect(TConfigNotifier(pfnCallback, pParam)); } void TConfig::DisconnectFromNotifier(void (*pfnCallback)(const TStringSet&, void*)) { - m_notifier.disconnect(TConfigNotifier(pfnCallback, NULL)); + boost::unique_lock lock(GetImpl()->m_lock); + GetImpl()->m_notifier.disconnect(TConfigNotifier(pfnCallback, NULL)); } void TConfig::DelayNotifications() { - boost::unique_lock lock(m_lock); - m_bDelayedEnabled = true; + boost::unique_lock lock(GetImpl()->m_lock); + GetImpl()->m_bDelayedEnabled = true; } void TConfig::ResumeNotifications() @@ -614,16 +420,16 @@ // separate scope for shared mutex (to avoid calling notifier inside critical section) { - boost::upgrade_lock lock(m_lock); - if(m_bDelayedEnabled) + boost::upgrade_lock lock(GetImpl()->m_lock); + if(GetImpl()->m_bDelayedEnabled) { - m_bDelayedEnabled = false; - if(!m_setDelayedNotifications.IsEmpty()) + GetImpl()->m_bDelayedEnabled = false; + if(!GetImpl()->m_setDelayedNotifications.IsEmpty()) { - setNotifications = m_setDelayedNotifications; + setNotifications = GetImpl()->m_setDelayedNotifications; boost::upgrade_to_unique_lock upgraded_lock(lock); - m_setDelayedNotifications.Clear(); + GetImpl()->m_setDelayedNotifications.Clear(); } } } @@ -637,38 +443,66 @@ { // separate scope for shared mutex (to avoid calling notifier inside critical section) { - boost::upgrade_lock lock(m_lock); - if(m_bDelayedEnabled) + boost::upgrade_lock lock(GetImpl()->m_lock); + if(GetImpl()->m_bDelayedEnabled) { boost::upgrade_to_unique_lock upgraded_lock(lock); - m_setDelayedNotifications.Insert(rsetInfo); + GetImpl()->m_setDelayedNotifications.Insert(rsetInfo); return; } } // NOTE: we don't lock here - m_notifier(rsetInfo); + GetImpl()->m_notifier(rsetInfo); } void TConfig::SendNotification(PCTSTR pszInfo) { // separate scope for shared mutex (to avoid calling notifier inside critical section) { - boost::upgrade_lock lock(m_lock); - if(m_bDelayedEnabled) + boost::upgrade_lock lock(GetImpl()->m_lock); + if(GetImpl()->m_bDelayedEnabled) { boost::upgrade_to_unique_lock upgraded_lock(lock); - m_setDelayedNotifications.Insert(pszInfo); + GetImpl()->m_setDelayedNotifications.Insert(pszInfo); return; } } // NOTE: we don't lock here TStringSet setData; setData.Insert(pszInfo); - m_notifier(setData); + GetImpl()->m_notifier(setData); } +details::ConfigNodeContainer* TConfig::GetImpl() +{ + return m_pImpl; +} + +const details::ConfigNodeContainer* TConfig::GetImpl() const +{ + return m_pImpl; +} + +TSmartPath TConfig::GetPath(PCTSTR pszPropName, const TSmartPath& pathDefault) const +{ + return PathFromWString(GetString(pszPropName, pathDefault.ToWString())); +} + +bool TConfig::GetValue(PCTSTR pszPropName, TSmartPath& rpathValue) const +{ + TString strPath; + bool bResult = GetValue(pszPropName, strPath); + rpathValue = PathFromWString(strPath); + return bResult; +} + +TConfig& TConfig::SetValue(PCTSTR pszPropName, const TSmartPath& pathValue) +{ + return SetValue(pszPropName, pathValue.ToWString()); +} + END_CHCORE_NAMESPACE Index: src/libchcore/TConfig.h =================================================================== diff -u -r960167a493c3ae7ecbdc7e8c2b91619106d7a685 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TConfig.h (.../TConfig.h) (revision 960167a493c3ae7ecbdc7e8c2b91619106d7a685) +++ src/libchcore/TConfig.h (.../TConfig.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -23,21 +23,19 @@ #ifndef __TCONFIG_H__ #define __TCONFIG_H__ -#pragma warning(push) -#pragma warning(disable: 4100 4702 4512) - #include - #include -#pragma warning(pop) -#include "TStringSet.h" -#include "TStringArray.h" +#include "libchcore.h" +#include "ISerializerContainer.h" BEGIN_CHCORE_NAMESPACE -class TReadBinarySerializer; -class TWriteBinarySerializer; - class TConfigArray; +class TStringSet; +namespace details +{ + struct ConfigNodeContainer; +} + // class for handling configuration settings class LIBCHCORE_API TConfig { @@ -52,21 +50,16 @@ // read/write void Read(PCTSTR pszFile); - void Write(bool bOnlyIfModified = false); + void Write(); - void SerializeLoad(TReadBinarySerializer& rSerializer); - void SerializeStore(TWriteBinarySerializer& rSerializer); + void Store(const ISerializerContainerPtr& spContainer) const; + void Load(const ISerializerContainerPtr& spContainer) const; void ReadFromString(const TString& strInput); void WriteToString(TString& strOutput); void SetFilePath(PCTSTR pszPath); - // Modifications management - bool IsModified() const; - void MarkAsModified(); - void MarkAsNotModified(); - // value setting/retrieval bool GetBool(PCTSTR pszPropName, bool bDefault = false) const; bool GetValue(PCTSTR pszPropName, bool& bValue) const; @@ -96,8 +89,12 @@ bool GetValue(PCTSTR pszPropName, TString& rstrValue) const; TConfig& SetValue(PCTSTR pszPropName, const TString& strValue); + TSmartPath GetPath(PCTSTR pszPropName, const TSmartPath& pathDefault) const; + bool GetValue(PCTSTR pszPropName, TSmartPath& rpathValue) const; + TConfig& SetValue(PCTSTR pszPropName, const TSmartPath& pathValue); + bool GetValue(PCTSTR pszPropName, TStringArray& rvValues) const; - void SetValue(PCTSTR pszPropName, const TStringArray& rvValues); + TConfig& SetValue(PCTSTR pszPropName, const TStringArray& rvValues); void DeleteNode(PCTSTR pszNodeName); @@ -120,22 +117,14 @@ void ClearNL(); - void SerializeStoreNode(TWriteBinarySerializer& rSerializer, boost::property_tree::wiptree& treeNode); - void SerializeLoadNode(TReadBinarySerializer& rSerializer, boost::property_tree::wiptree& treeNode); +private: + details::ConfigNodeContainer* GetImpl(); + const details::ConfigNodeContainer* GetImpl() const; private: #pragma warning(push) #pragma warning(disable: 4251) - boost::property_tree::wiptree m_propTree; - TString m_strFilePath; - - boost::signals2::signal m_notifier; - TStringSet m_setDelayedNotifications; - bool m_bDelayedEnabled; - - bool m_bModified; ///< Modification state - cleared when saving - - mutable boost::shared_mutex m_lock; + details::ConfigNodeContainer* m_pImpl; #pragma warning(pop) }; Index: src/libchcore/TConfigSerializers.h =================================================================== diff -u -rbe30619d750d8663d54cf02e7d4bde2ed2dd8d05 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TConfigSerializers.h (.../TConfigSerializers.h) (revision be30619d750d8663d54cf02e7d4bde2ed2dd8d05) +++ src/libchcore/TConfigSerializers.h (.../TConfigSerializers.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -24,6 +24,7 @@ #define __TCONFIGSERIALIZERS_H__ #include "TConfig.h" +#include "TStringArray.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TDateTime.cpp =================================================================== diff -u -r548382442cbf7bed7f744b279ce3f66b54992724 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TDateTime.cpp (.../TDateTime.cpp) (revision 548382442cbf7bed7f744b279ce3f66b54992724) +++ src/libchcore/TDateTime.cpp (.../TDateTime.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -22,8 +22,8 @@ // ============================================================================ #include "stdafx.h" #include "TDateTime.h" -#include "SerializationHelpers.h" #include "TBinarySerializer.h" +#include "SerializationHelpers.h" #include "TCoreException.h" #include "ErrorCodes.h" Index: src/libchcore/TFileFilter.h =================================================================== diff -u -rba802caea92ee56a154d1da3fe89a4b2f7875f0e -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TFileFilter.h (.../TFileFilter.h) (revision ba802caea92ee56a154d1da3fe89a4b2f7875f0e) +++ src/libchcore/TFileFilter.h (.../TFileFilter.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -22,6 +22,7 @@ #include "libchcore.h" #include #include "TDateTime.h" +#include "TStringArray.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TPath.cpp =================================================================== diff -u -r293e52b38d46653068006262172018a0f0d0a31c -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TPath.cpp (.../TPath.cpp) (revision 293e52b38d46653068006262172018a0f0d0a31c) +++ src/libchcore/TPath.cpp (.../TPath.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -26,11 +26,10 @@ #pragma warning(pop) #include "../libicpf/exception.h" #include -#include "TBinarySerializer.h" -#include "SerializationHelpers.h" #include "TCoreException.h" #include "ErrorCodes.h" #include "TPathContainer.h" +#include "TStringArray.h" BEGIN_CHCORE_NAMESPACE @@ -1072,56 +1071,7 @@ return m_pPath->m_strPath.GetLength(); } -void TSmartPath::Serialize(TReadBinarySerializer& rSerializer) -{ - PrepareToWrite(); - Serializers::Serialize(rSerializer, m_pPath->m_strPath); -} - -void TSmartPath::Serialize(TWriteBinarySerializer& rSerializer) const -{ - if(m_pPath) - Serializers::Serialize(rSerializer, m_pPath->m_strPath); - else - Serializers::Serialize(rSerializer, TString()); -} - // ============================================================================ -/// chcore::TSmartPath::StoreInConfig -/// @date 2011/04/05 -/// -/// @brief Stores the path in configuration file. -/// @param[in] rConfig - configuration object to store information in. -/// @param[in] pszPropName - property name under which to store the path. -// ============================================================================ -void TSmartPath::StoreInConfig(TConfig& rConfig, PCTSTR pszPropName) const -{ - rConfig.SetValue(pszPropName, m_pPath ? m_pPath->m_strPath : TString()); -} - -// ============================================================================ -/// chcore::TSmartPath::ReadFromConfig -/// @date 2011/04/05 -/// -/// @brief Reads a path from configuration file. -/// @param[in] rConfig - configuration object to read path from. -/// @param[in] pszPropName - property name from under which to read the path. -/// @return True if path properly read, false otherwise. -// ============================================================================ -bool TSmartPath::ReadFromConfig(const TConfig& rConfig, PCTSTR pszPropName) -{ - TString wstrPath; - if(rConfig.GetValue(pszPropName, wstrPath)) - { - PrepareToWrite(); - m_pPath->m_strPath = wstrPath; - return true; - } - else - return false; -} - -// ============================================================================ /// TSmartPath::AppendIfNotExists /// @date 2009/11/29 /// Index: src/libchcore/TPath.h =================================================================== diff -u -r293e52b38d46653068006262172018a0f0d0a31c -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TPath.h (.../TPath.h) (revision 293e52b38d46653068006262172018a0f0d0a31c) +++ src/libchcore/TPath.h (.../TPath.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -20,7 +20,7 @@ #define __TPATH_H__ #include "libchcore.h" -#include "TConfig.h" +#include "TString.h" BEGIN_CHCORE_NAMESPACE @@ -142,13 +142,6 @@ bool IsEmpty() const; size_t GetLength() const; - // Serialization - void Serialize(TReadBinarySerializer& rSerializer); - void Serialize(TWriteBinarySerializer& rSerializer) const; - - void StoreInConfig(TConfig& rConfig, PCTSTR pszPropName) const; - bool ReadFromConfig(const TConfig& rConfig, PCTSTR pszPropName); - protected: void PrepareToWrite(); @@ -163,6 +156,4 @@ END_CHCORE_NAMESPACE -CONFIG_MEMBER_SERIALIZATION(TSmartPath) - #endif Index: src/libchcore/TPathContainer.cpp =================================================================== diff -u -r73583f2ca01fa1b2eae49bbc63bce46b9ecff5db -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TPathContainer.cpp (.../TPathContainer.cpp) (revision 73583f2ca01fa1b2eae49bbc63bce46b9ecff5db) +++ src/libchcore/TPathContainer.cpp (.../TPathContainer.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -20,6 +20,7 @@ #include "TPathContainer.h" #include "TCoreException.h" #include "ErrorCodes.h" +#include "TStringArray.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TPathContainer.h =================================================================== diff -u -r73583f2ca01fa1b2eae49bbc63bce46b9ecff5db -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TPathContainer.h (.../TPathContainer.h) (revision 73583f2ca01fa1b2eae49bbc63bce46b9ecff5db) +++ src/libchcore/TPathContainer.h (.../TPathContainer.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -21,6 +21,7 @@ #include "libchcore.h" #include "TPath.h" +#include "TConfig.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TSQLiteTaskSchema.cpp =================================================================== diff -u -ra5aa3c3cb78f3767641de2627d1a49a1dc35b429 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TSQLiteTaskSchema.cpp (.../TSQLiteTaskSchema.cpp) (revision a5aa3c3cb78f3767641de2627d1a49a1dc35b429) +++ src/libchcore/TSQLiteTaskSchema.cpp (.../TSQLiteTaskSchema.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -52,6 +52,9 @@ tStatement.Prepare(_T("CREATE TABLE scanned_files(id BIGINT UNIQUE, rel_path varchar(32768) NOT NULL, base_path_id BIGINT NOT NULL, attr INT NOT NULL, size BIGINT NOT NULL, time_created BIGINT NOT NULL, time_last_write BIGINT NOT NULL, time_last_access BIGINT NOT NULL, flags INT NOT NULL)")); tStatement.Step(); + tStatement.Prepare(_T("CREATE TABLE task_config(id BIGINT UNIQUE, name varchar(256) NOT NULL, node_order INT NOT NULL, value varchar(32768) NOT NULL)")); + tStatement.Step(); + // and finally set the database version to current one tVersion.SetVersion(1); } Index: src/libchcore/TString.cpp =================================================================== diff -u -r14d99757fbeaa3e55d43b45b043033d8ba81980b -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TString.cpp (.../TString.cpp) (revision 14d99757fbeaa3e55d43b45b043033d8ba81980b) +++ src/libchcore/TString.cpp (.../TString.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -79,7 +79,7 @@ wchar_t* pszNewBuffer = new wchar_t[stNewLen]; if(m_pszData) - memcpy(pszNewBuffer, m_pszData, m_stStringLength); + _tcsncpy_s(pszNewBuffer, stNewLen, m_pszData, m_stStringLength + 1); else pszNewBuffer[0] = _T('\0'); @@ -204,7 +204,8 @@ { if(this != &rSrc && m_pData != rSrc.m_pData) { - Release(); + if(InterlockedDecrement(&m_pData->m_lRefCount) < 1) + delete m_pData; if(InterlockedCompareExchange(&rSrc.m_pData->m_lRefCount, 0, 0) > 0) { Index: src/libchcore/TSubTaskBase.h =================================================================== diff -u -r2efd22688b8d12be34c87bf2b024d8db6e317d60 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TSubTaskBase.h (.../TSubTaskBase.h) (revision 2efd22688b8d12be34c87bf2b024d8db6e317d60) +++ src/libchcore/TSubTaskBase.h (.../TSubTaskBase.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -27,6 +27,7 @@ #include "TPath.h" #include "ESubTaskTypes.h" #include "TSubTaskStatsInfo.h" +#include "TBinarySerializer.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TTask.cpp =================================================================== diff -u -ra5aa3c3cb78f3767641de2627d1a49a1dc35b429 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TTask.cpp (.../TTask.cpp) (revision a5aa3c3cb78f3767641de2627d1a49a1dc35b429) +++ src/libchcore/TTask.cpp (.../TTask.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -36,6 +36,7 @@ #include "ErrorCodes.h" #include "TRowData.h" #include "ISerializerRowData.h" +#include "TStringSet.h" BEGIN_CHCORE_NAMESPACE @@ -139,6 +140,9 @@ spContainer = m_spSerializer->GetContainer(_T("scanned_files")); m_files.Load(spContainer, m_spSrcPaths); + + spContainer = m_spSerializer->GetContainer(_T("task_config")); + m_tConfiguration.Load(spContainer); } } @@ -158,6 +162,9 @@ spContainer = m_spSerializer->GetContainer(_T("scanned_files")); m_files.Store(spContainer); + + spContainer = m_spSerializer->GetContainer(_T("task_config")); + m_tConfiguration.Store(spContainer); } m_spSerializer->Flush(); Index: src/libchcore/TTaskConfigTracker.cpp =================================================================== diff -u -r548382442cbf7bed7f744b279ce3f66b54992724 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TTaskConfigTracker.cpp (.../TTaskConfigTracker.cpp) (revision 548382442cbf7bed7f744b279ce3f66b54992724) +++ src/libchcore/TTaskConfigTracker.cpp (.../TTaskConfigTracker.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -24,6 +24,7 @@ #include "TTaskConfigTracker.h" #include "TCoreException.h" #include "ErrorCodes.h" +#include "TStringSet.h" BEGIN_CHCORE_NAMESPACE Index: src/libchcore/TTaskDefinition.cpp =================================================================== diff -u -rb1ecc12ba4c1f2a7b4acd6e82fc4193535e55ff0 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TTaskDefinition.cpp (.../TTaskDefinition.cpp) (revision b1ecc12ba4c1f2a7b4acd6e82fc4193535e55ff0) +++ src/libchcore/TTaskDefinition.cpp (.../TTaskDefinition.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -226,35 +226,6 @@ tTaskInfo.ExtractSubConfig(_T("TaskDefinition.TaskSettings"), m_tConfiguration); } -void TTaskDefinition::Store(const TSmartPath& strPath, bool bOnlyIfModified) -{ - if(!bOnlyIfModified || m_bModified || m_tConfiguration.IsModified()) - { - // read everything - TConfig tTaskInfo; - tTaskInfo.SetFilePath(strPath.ToString()); - - // get information from config file - // task unique id - use if provided, generate otherwise - SetConfigValue(tTaskInfo, _T("TaskDefinition.UniqueID"), m_strTaskName); - - // basic information - SetConfigValue(tTaskInfo, _T("TaskDefinition.SourcePaths.Path"), m_vSourcePaths); - SetConfigValue(tTaskInfo, _T("TaskDefinition.DestinationPath"), m_pathDestinationPath); - - int iOperation = m_tOperationPlan.GetOperationType(); - SetConfigValue(tTaskInfo, _T("TaskDefinition.OperationType"), iOperation); - - SetConfigValue(tTaskInfo, _T("TaskDefinition.Version"), m_ullTaskVersion); - - tTaskInfo.PutSubConfig(_T("TaskDefinition.TaskSettings"), m_tConfiguration); - - tTaskInfo.Write(); - - m_bModified = false; - } -} - void TTaskDefinition::StoreInString(TString& strOutput) { // read everything Index: src/libchcore/TTaskDefinition.h =================================================================== diff -u -r73583f2ca01fa1b2eae49bbc63bce46b9ecff5db -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/TTaskDefinition.h (.../TTaskDefinition.h) (revision 73583f2ca01fa1b2eae49bbc63bce46b9ecff5db) +++ src/libchcore/TTaskDefinition.h (.../TTaskDefinition.h) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -73,7 +73,6 @@ // Serialization void Load(const TSmartPath& strPath); - void Store(const TSmartPath& strPath, bool bOnlyIfModified = false); void StoreInString(TString& strInput); void LoadFromString(const TString& strInput); Index: src/libchcore/Tests/TestsTString.cpp =================================================================== diff -u -r14d99757fbeaa3e55d43b45b043033d8ba81980b -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/Tests/TestsTString.cpp (.../TestsTString.cpp) (revision 14d99757fbeaa3e55d43b45b043033d8ba81980b) +++ src/libchcore/Tests/TestsTString.cpp (.../TestsTString.cpp) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -267,6 +267,13 @@ EXPECT_EQ(strValue, _T("Some string... appended.")); } +TEST(TStringTests, AppendString_WithReallocate) +{ + TString strValue(_T("Some string with up to 64 characters - 123456789012345678901")); + strValue.Append(_T("... appended.")); + EXPECT_EQ(strValue, _T("Some string with up to 64 characters - 123456789012345678901... appended.")); +} + // left, right, mid TEST(TStringTests, Left) { Index: src/libchcore/libchcore.vc90.vcproj =================================================================== diff -u -r960167a493c3ae7ecbdc7e8c2b91619106d7a685 -rb1e03eb232a784d6e2d40f67cbbbb33be0972228 --- src/libchcore/libchcore.vc90.vcproj (.../libchcore.vc90.vcproj) (revision 960167a493c3ae7ecbdc7e8c2b91619106d7a685) +++ src/libchcore/libchcore.vc90.vcproj (.../libchcore.vc90.vcproj) (revision b1e03eb232a784d6e2d40f67cbbbb33be0972228) @@ -1302,6 +1302,26 @@ RelativePath=".\TConfigNotifier.h" > + + + + + + + + + +