diff --git a/Core/Code/DataManagement/mitkIPersistenceService.h b/Core/Code/DataManagement/mitkIPersistenceService.h index 59923f6eca..6281f28226 100644 --- a/Core/Code/DataManagement/mitkIPersistenceService.h +++ b/Core/Code/DataManagement/mitkIPersistenceService.h @@ -1,273 +1,275 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPersistenceService_h #define mitkIPersistenceService_h // mitk #include "mitkPropertyListReplacedObserver.h" #include "usServiceReference.h" #include "usModuleContext.h" #include "usGetModuleContext.h" //for microservices #include #include "mitkDataStorage.h" namespace mitk { /// /// The central service for the persistence module /// Basic idea is to create PropertyLists with a unique id using AddPropertyList(). A consumer /// of this interface can write arbitrary information into this propertylist /// Calling Save() and Load() will cause the Service to save and load the current set of propertlists from /// a file in the user directory. /// Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application /// start/stop. /// Moreover, depending on the backend type, the service is connected to the SceneSerialization module, i.e. /// the user will be asked whether to save/load the propertlists in/from the current ".mitk" file that is selected /// by the user. /// class MITK_CORE_EXPORT IPersistenceService { public: /// /// Set a default directory for storage /// static std::string CreateDefaultFileName(); /// /// Get the default directory for storage /// static void SetDefaultPersistenceFile(const std::string& defaultPersistenceFile); /// /// Set a default directory for storage /// static std::string GetDefaultPersistenceFile(); /// /// If PropertyList with the given id exists, returns it. Otherwise creates a new one and returns it. /// If id is empty a UUID will be created and set on the variable /// If existed was passed, it is true if the PropertyList with that id existed, false otherwise /// \return a valid PropertyList with a StringProperty "Id" containing the passed id /// virtual mitk::PropertyList::Pointer GetPropertyList( std::string& id, bool* existed=0 ) = 0; /// /// \return The name of the Bool Property that specifies whether a DataNode is a Node carrying Persistence PropertyLists /// virtual std::string GetPersistenceNodePropertyName() const = 0; /// /// Creates a vector of DataNodes that contain all PropertyLists. Additionally, the DataNodes /// will have the property name set to the PropertyList's id and a BoolProperty equal to GetPersistenceNodePropertyName() set to true /// \return vector of DataNodes with the described attributes /// virtual DataStorage::SetOfObjects::Pointer GetDataNodes() const = 0; /// /// Searches storage for persistent DataNodes, extracts and inserts the appended property lists to this service /// \return true if at least one node was found from which a PropertyList could be restored /// virtual bool RestorePropertyListsFromPersistentDataNodes(DataStorage* storage) = 0; /// /// Save the current PropertyLists to fileName. If fileName is empty, a special file in the users home directory will be used. /// if appendchanges is true, the file will not replaced but first loaded, then overwritten and then replaced /// \return false if an error occured (cannot write to file), true otherwise /// virtual bool Save(const std::string& fileName="", bool appendChanges=false) = 0; /// /// Load PropertyLists from fileName. If fileName is empty, a special file in the users home directory will be used. + /// If enforeReload is false, the service will take care of modified time flags, i.e. it will not load a file + /// that was loaded before and did not change in the meantime or that was modified by the service itself /// *ATTENTION*: If there are PropertyLists with the same id contained in the file, existing PropertyLists will be overwritten! /// \see AddPropertyListReplacedObserver() /// \return false if an error occured (cannot load from file), true otherwise /// - virtual bool Load(const std::string& fileName="") = 0; + virtual bool Load(const std::string& fileName="", bool enforeReload=true) = 0; /// /// Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application /// start/stop. /// virtual void SetAutoLoadAndSave(bool autoLoadAndSave) = 0; /// /// adds a observer which is informed if a propertyList gets replaced during a Load() procedure /// virtual void AddPropertyListReplacedObserver( PropertyListReplacedObserver* observer ) = 0; /// /// removes a specific observer /// virtual void RemovePropertyListReplacedObserver( PropertyListReplacedObserver* observer ) = 0; /// /// nothing to do here /// virtual ~IPersistenceService(); private: static std::string m_DefaultPersistenceFile; }; } /// MACROS FOR AUTOMATIC SAVE FUNCTION #define PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION\ us::GetModuleContext() #define PERSISTENCE_GET_SERVICE_MACRO\ mitk::IPersistenceService* persistenceService = 0;\ us::ModuleContext* context = PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION;\ if( context )\ {\ us::ServiceReference persistenceServiceRef = context->GetServiceReference();\ if( persistenceServiceRef )\ {\ persistenceService\ = dynamic_cast ( context->GetService(persistenceServiceRef) );\ }\ } #define PERSISTENCE_GET_SERVICE_METHOD_MACRO\ mitk::IPersistenceService* GetPeristenceService() const\ {\ static mitk::IPersistenceService* staticPersistenceService = 0;\ if( staticPersistenceService == 0 )\ {\ PERSISTENCE_GET_SERVICE_MACRO\ staticPersistenceService = persistenceService;\ }\ return staticPersistenceService;\ } #define PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ public:\ bool Save(const std::string& fileName="")\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ bool noError = persistenceService != 0;\ if( noError )\ this->ToPropertyList();\ if(noError)\ noError = persistenceService->Save(fileName);\ return noError;\ }\ bool Load(const std::string& fileName="")\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ bool noError = persistenceService != 0 && persistenceService->Load(fileName);\ if( noError )\ {\ this->FromPropertyList();\ }\ return noError;\ }\ void ToPropertyList()\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ this->InitializePropertyListReplacedObserver(persistenceService);\ if( !persistenceService )\ return;\ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME);\ propList->Set(#ID_MEMBER_NAME, ID_MEMBER_NAME); #define PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ }\ void FromPropertyList()\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ this->InitializePropertyListReplacedObserver(persistenceService);\ if( !persistenceService )\ return;\ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME);\ propList->Get(#ID_MEMBER_NAME, ID_MEMBER_NAME); #define PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME)\ }\ std::string GetId() const\ {\ return ID_MEMBER_NAME;\ }\ private:\ PERSISTENCE_GET_SERVICE_METHOD_MACRO\ struct MyPropertyListReplacedObserver: public mitk::PropertyListReplacedObserver\ {\ MyPropertyListReplacedObserver()\ : m_##THE_CLASS_NAME(0), m_PersistenceService(0)\ {\ }\ ~MyPropertyListReplacedObserver()\ {\ if( m_PersistenceService )\ m_PersistenceService->RemovePropertyListReplacedObserver(this);\ }\ void AfterPropertyListReplaced( const std::string& id, mitk::PropertyList* propertyList )\ {\ if( m_##THE_CLASS_NAME && m_##THE_CLASS_NAME->GetId() == id )\ m_##THE_CLASS_NAME->FromPropertyList();\ }\ void Initialize( THE_CLASS_NAME##* _##THE_CLASS_NAME, mitk::IPersistenceService* persistenceService )\ {\ m_##THE_CLASS_NAME = _##THE_CLASS_NAME;\ m_PersistenceService = persistenceService;\ if( m_PersistenceService )\ m_PersistenceService->AddPropertyListReplacedObserver(this);\ }\ private:\ THE_CLASS_NAME##* m_##THE_CLASS_NAME;\ mitk::IPersistenceService* m_PersistenceService;\ };\ MyPropertyListReplacedObserver m_MyPropertyListReplacedObserver;\ void InitializePropertyListReplacedObserver(mitk::IPersistenceService* persistenceService)\ {\ static bool observerInitialized = false;\ if( observerInitialized == false && persistenceService )\ {\ m_MyPropertyListReplacedObserver.Initialize( this, persistenceService );\ observerInitialized = true;\ }\ } #define PERSISTENCE_CREATE(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE2(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE3(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE4(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) US_DECLARE_SERVICE_INTERFACE(mitk::IPersistenceService, "org.mitk.services.IPersistenceService") #endif diff --git a/Modules/Persistence/mitkPersistenceService.cpp b/Modules/Persistence/mitkPersistenceService.cpp index f2021ffe8c..711aaeb863 100644 --- a/Modules/Persistence/mitkPersistenceService.cpp +++ b/Modules/Persistence/mitkPersistenceService.cpp @@ -1,247 +1,274 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPersistenceService.h" #include "mitkStandaloneDataStorage.h" #include "mitkUIDGenerator.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "usModuleContext.h" #include "usGetModuleContext.h" #include const std::string mitk::PersistenceService::PERSISTENCE_PROPERTY_NAME("PersistenceNode"); const std::string mitk::PersistenceService::PERSISTENCE_PROPERTYLIST_NAME("PersistenceService"); const std::string mitk::PersistenceService::ID_PROPERTY_NAME("Id"); mitk::PersistenceService::PersistenceService() : m_AutoLoadAndSave( true ), m_SceneIO( SceneIO::New() ) { } void mitk::PersistenceService::Clear() { m_PropertyLists.clear(); + m_FileNamesToModifiedTimes.clear(); } mitk::PersistenceService::~PersistenceService() { MITK_DEBUG("mitk::PersistenceService") << "destructing PersistenceService"; } mitk::PropertyList::Pointer mitk::PersistenceService::GetPropertyList( std::string& id, bool* existed ) { mitk::PropertyList::Pointer propList; if( id.empty() ) { UIDGenerator uidGen; id = uidGen.GetUID(); } std::map::iterator it = m_PropertyLists.find( id ); if( it == m_PropertyLists.end() ) { propList = PropertyList::New(); m_PropertyLists[id] = propList; if( existed ) *existed = false; } else { propList = (*it).second; if( existed ) *existed = true; } return propList; } void mitk::PersistenceService::ClonePropertyList( mitk::PropertyList* from, mitk::PropertyList* to ) const { to->Clear(); const std::map< std::string, BaseProperty::Pointer>* propMap = from->GetMap(); std::map< std::string, BaseProperty::Pointer>::const_iterator propMapIt = propMap->begin(); while( propMapIt != propMap->end() ) { mitk::BaseProperty::Pointer clonedProp = (*propMapIt).second->Clone(); to->SetProperty( (*propMapIt).first, clonedProp ); ++propMapIt; } } bool mitk::PersistenceService::Save(const std::string& fileName, bool appendChanges) { bool save = false; std::string theFile = fileName; if(theFile.empty()) theFile = IPersistenceService::GetDefaultPersistenceFile(); mitk::DataStorage::Pointer tempDs; if(appendChanges) { if( !itksys::SystemTools::FileExists(theFile.c_str()) ) return false; DataStorage::Pointer ds = m_SceneIO->LoadScene( theFile ); bool load = (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedNodes()->size() == 0) && (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedProperties()->IsEmpty()); if( !load ) return false; tempDs = ds; } else tempDs = mitk::StandaloneDataStorage::New(); DataStorage::SetOfObjects::Pointer rs = DataStorage::SetOfObjects::New(); - return m_SceneIO->SaveScene( rs.GetPointer(), tempDs, theFile ); + save = m_SceneIO->SaveScene( rs.GetPointer(), tempDs, theFile ); + if( save ) + { + long int currentModifiedTime = itksys::SystemTools::ModifiedTime( theFile.c_str() ); + m_FileNamesToModifiedTimes[theFile] = currentModifiedTime; + } + return save; } -bool mitk::PersistenceService::Load(const std::string& fileName) +bool mitk::PersistenceService::Load(const std::string& fileName, bool enforceReload) { bool load = false; std::string theFile = fileName; if(theFile.empty()) theFile = IPersistenceService::GetDefaultPersistenceFile(); if( !itksys::SystemTools::FileExists(theFile.c_str()) ) return false; + if( enforceReload == false ) + { + bool loadTheFile = true; + std::map::iterator it = m_FileNamesToModifiedTimes.find( theFile ); + + long int currentModifiedTime = itksys::SystemTools::ModifiedTime( theFile.c_str() ); + if( it != m_FileNamesToModifiedTimes.end() ) + { + long int knownModifiedTime = (*it).second; + if( knownModifiedTime >= currentModifiedTime ) + { + loadTheFile = false; + } + } + if( loadTheFile ) + m_FileNamesToModifiedTimes[theFile] = currentModifiedTime; + else + return true; + } + DataStorage::Pointer ds = m_SceneIO->LoadScene( theFile ); load = (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedNodes()->size() == 0) && (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedProperties()->IsEmpty()); if( !load ) { MITK_DEBUG("mitk::PersistenceService") << "loading of scene files failed"; return load; } return load; } void mitk::PersistenceService::SetAutoLoadAndSave(bool autoLoadAndSave) { m_AutoLoadAndSave = autoLoadAndSave; std::string id = PERSISTENCE_PROPERTYLIST_NAME; mitk::PropertyList::Pointer propList = this->GetPropertyList( id ); propList->Set("m_AutoLoadAndSave", m_AutoLoadAndSave); this->Save(); } void mitk::PersistenceService::AddPropertyListReplacedObserver(PropertyListReplacedObserver* observer) { m_PropertyListReplacedObserver.insert( observer ); } void mitk::PersistenceService::RemovePropertyListReplacedObserver(PropertyListReplacedObserver* observer) { m_PropertyListReplacedObserver.erase( observer ); } us::ModuleContext* mitk::PersistenceService::GetModuleContext() { return us::GetModuleContext(); } std::string mitk::PersistenceService::GetPersistenceNodePropertyName() const { return PERSISTENCE_PROPERTY_NAME; } mitk::DataStorage::SetOfObjects::Pointer mitk::PersistenceService::GetDataNodes() const { DataStorage::SetOfObjects::Pointer set = DataStorage::SetOfObjects::New(); std::map::const_iterator it = m_PropertyLists.begin(); while( it != m_PropertyLists.end() ) { mitk::DataNode::Pointer node = mitk::DataNode::New(); const std::string& name = (*it).first; this->ClonePropertyList( (*it).second, node->GetPropertyList() ); node->SetBoolProperty( PERSISTENCE_PROPERTY_NAME.c_str(), true ); node->SetName( name ); node->SetStringProperty(ID_PROPERTY_NAME.c_str(), name.c_str() ); set->push_back( node ); ++it; } return set; } bool mitk::PersistenceService::RestorePropertyListsFromPersistentDataNodes( DataStorage* storage ) { bool oneFound = false; DataStorage::SetOfObjects::ConstPointer allNodes = storage->GetAll(); for ( mitk::DataStorage::SetOfObjects::const_iterator sourceIter = allNodes->begin(); sourceIter != allNodes->end(); ++sourceIter ) { mitk::DataNode* node = *sourceIter; bool isPersistenceNode = false; node->GetBoolProperty( PERSISTENCE_PROPERTY_NAME.c_str(), isPersistenceNode ); if( isPersistenceNode ) { oneFound = true; MITK_DEBUG("mitk::PersistenceService") << "isPersistenceNode was true"; std::string name = node->GetName(); bool existed = false; mitk::PropertyList::Pointer propList = this->GetPropertyList( name, &existed ); if( existed ) { MITK_DEBUG("mitk::PersistenceService") << "calling replace observer before replacing values"; std::set::iterator it = m_PropertyListReplacedObserver.begin(); while( it != m_PropertyListReplacedObserver.end() ) { (*it)->BeforePropertyListReplaced( name, propList ); ++it; } } // if( existed ) MITK_DEBUG("mitk::PersistenceService") << "replacing values"; this->ClonePropertyList( node->GetPropertyList(), propList ); propList->SetStringProperty(ID_PROPERTY_NAME.c_str(), name.c_str()); if( existed ) { MITK_DEBUG("mitk::PersistenceService") << "calling replace observer before replacing values"; std::set::iterator it = m_PropertyListReplacedObserver.begin(); while( it != m_PropertyListReplacedObserver.end() ) { (*it)->AfterPropertyListReplaced( name, propList ); ++it; } } // if( existed ) } // if( isPersistenceNode ) } // for ( mitk::DataStorage::SetOfObjects::const_iterator sourceIter = allNodes->begin(); ... return oneFound; } bool mitk::PersistenceService::GetAutoLoadAndSave() const { return m_AutoLoadAndSave; } diff --git a/Modules/Persistence/mitkPersistenceService.h b/Modules/Persistence/mitkPersistenceService.h index a251ebe35e..3d2f8f7295 100644 --- a/Modules/Persistence/mitkPersistenceService.h +++ b/Modules/Persistence/mitkPersistenceService.h @@ -1,69 +1,70 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPersistenceService_h #define mitkPersistenceService_h #include "mitkIPersistenceService.h" #include #include "mitkSceneIO.h" namespace mitk { /// /// implementation of the IPersistenceService /// \see IPersistenceService class PersistenceService: public itk::LightObject, public mitk::IPersistenceService { public: static const std::string PERSISTENCE_PROPERTY_NAME; static const std::string PERSISTENCE_PROPERTYLIST_NAME; static const std::string ID_PROPERTY_NAME; static us::ModuleContext* GetModuleContext(); PersistenceService(); ~PersistenceService(); mitk::PropertyList::Pointer GetPropertyList( std::string& id, bool* existed=0 ); std::string GetPersistenceNodePropertyName() const; DataStorage::SetOfObjects::Pointer GetDataNodes() const; bool Save(const std::string& fileName="", bool appendChanges=false); - bool Load(const std::string& fileName=""); + bool Load(const std::string& fileName="", bool enforeReload=true); void SetAutoLoadAndSave(bool autoLoadAndSave); bool GetAutoLoadAndSave() const; void AddPropertyListReplacedObserver( PropertyListReplacedObserver* observer ); void RemovePropertyListReplacedObserver( PropertyListReplacedObserver* observer ); bool RestorePropertyListsFromPersistentDataNodes(DataStorage* storage); void Clear(); private: void ClonePropertyList( mitk::PropertyList* from, mitk::PropertyList* to ) const; std::map m_PropertyLists; bool m_AutoLoadAndSave; std::set m_PropertyListReplacedObserver; SceneIO::Pointer m_SceneIO; + std::map m_FileNamesToModifiedTimes; }; } #endif