#include "stdafx.h" #include "BlobSerializer.h" #include "ClassFactory.h" #include "DrawOperator/XySerializerHelper.h" #include "SigmaDoc.h" #include "SigmaStore.h" #include "UndoManager/UndoManager.h" #include "Util.h" #include "../DrawOperator/Util.h" #include "StorageFactory.h" #include "AsyncWorker.h" #include #include #include #include /** * @brief CSigmaStore::Impl 类的实现 */ class CSigmaStore::Impl { public: Impl(std::shared_ptr storage) : m_storage(storage) { } void SetActions(CSigmaDoc* pDoc) { const std::deque& redoStack = pDoc->GetActionManager()->GetRedoStack(); std::vector records = m_asyncWorker.postTask([=]() { return m_storage->RetrieveAllActionRecords(CStringToUtf8String(pDoc->m_FileName)); }).get(); for (const auto& record : records) { CAction* pAction = DeserializeAction(pDoc, record.data); if (pAction != nullptr) { if (CActionItem* pActionItem = dynamic_cast(pAction)) { const_cast&>(redoStack).push_front(pAction); pDoc->Redo(); } } } } /** * @brief 保存操作记录 * @param actions 操作记录向量 * @param path 文件路径 */ void SaveActions(std::vector& actions, const CString& path) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } if (actions.empty()) { return; } std::string filePath = CStringToUtf8String(path); CacheActions(path, actions); m_asyncWorker.postTask([this, storage = m_storage, filePath, movedActions = std::move(actions)]() { for (auto& record : movedActions) { if (!storage->ExistsActionRecord(filePath, record.uuid)) { storage->AddActionRecord(filePath, record); } } }); } /** * @brief 根据需要保存文件数据 * @param pDoc 文档指针 * @param path 文件路径 */ void SaveFileDataIfNeed(CSigmaDoc* pDoc, const CString& path) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } std::string filePath = CStringToUtf8String(path); if (!m_storage->ExistsFileData(filePath)) { auto rawData = CXySerializerHelper::SerializeCXy(pDoc->m_pXy); auto pFileData = std::make_shared( -1, filePath, reinterpret_cast&>(rawData), FormatTime(std::chrono::system_clock::now()) ); m_asyncWorker.postTask([this, filePath, pFileData]() { m_storage->InsertFileData(filePath, *pFileData); }); } } /** * @brief 获取文档的操作记录 * @param pDoc 文档指针 * @return 操作记录向量 */ std::vector GetActionRecords(const CSigmaDoc* pDoc) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } std::vector result; auto actionManager = const_cast(pDoc)->GetActionManager(); if (actionManager) { std::vector actions; const auto& undoStack = actionManager->GetUndoStack(); for (auto* action : undoStack) { actions.push_back(action); } const auto& redoStack = actionManager->GetRedoStack(); for (auto it = redoStack.rbegin(); it != redoStack.rend(); ++it) { actions.push_back(*it); } CSplitPath sp(""); CString path = sp.GetAbsolutePath(pDoc->m_FileName); for (auto* action : actions) { if (InCache(path, action->GetUUID())) { continue; } result.push_back({ -1, action->GetUUID(), action->GetActionName(), "", SerializeAction(action), FormatTime(action->GetTimePoint()), FormatTime(std::chrono::system_clock::now()) }); } } return result; } /** * @brief 获取操作记录数量 * @param path 文件路径 * @return 操作记录数量 */ int ActionRecordCount(const CString& path) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } return m_asyncWorker.postTask([=]() { std::string u8path = CStringToUtf8String(path); return m_storage->ActionRecordCount(u8path); }).get(); } /** * @brief 检索文件数据 * @param filePath 文件路径 * @return 文件数据(可选) */ std::optional RetrieveFileData(const CString& filePath) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } return m_asyncWorker.postTask([=]() { std::string u8path = CStringToUtf8String(filePath); return m_storage->RetrieveFileData(u8path); }).get(); } /** * @brief 清除指定路径的备份 * @param path 文件路径 */ void Clear(const CString& path) { if (!m_storage) { throw std::runtime_error("m_storage is nullptr"); } m_asyncWorker.postTask([=]() { std::string u8path = CStringToUtf8String(path); m_storage->ClearBackup(u8path); }).wait(); ClearActionCache(path); } private: void ClearActionCache(const CString& path) { auto it = m_actionCache.find(path); if (it != m_actionCache.end()) { m_actionCache.erase(it); } } void CacheActions(const CString& path, const std::vector& actions) { std::set set; for (const ActionRecord& action : actions) { set.insert(action.uuid); } m_actionCache[path] = std::move(set); } bool InCache(const CString& path, const std::string& uuid) const { auto it = m_actionCache.find(path); if (it == m_actionCache.end()) { return false; } return it->second.find(uuid) != it->second.end(); } std::shared_ptr m_storage; AsyncWorker m_asyncWorker; std::unordered_map, CStringHash> m_actionCache; }; CSigmaStore::CSigmaStore(std::shared_ptr storage) : m_impl(std::make_unique(storage)) { } CSigmaStore::~CSigmaStore() { } bool CSigmaStore::Exist(const CString& path) { try { return m_impl->ActionRecordCount(path) > 0; } catch (std::runtime_error& e) { TRACE("Exist %s failed: %s\n", path, e.what()); } return false; } bool CSigmaStore::Load(CSigmaDoc* pDoc, const CString& path) { try { auto fileDataOpt = m_impl->RetrieveFileData(path); if (!fileDataOpt.has_value()) { return false; } auto& fileData = fileDataOpt.value(); pDoc->m_pXy = CXySerializerHelper::DeserializeCXy(reinterpret_cast&>(fileData.data)).release(); pDoc->m_FileName = Utf8StringToCString(fileData.filepath); m_impl->SetActions(pDoc); return pDoc; } catch (const std::runtime_error &e) { TRACE("Load %s failed: %s\n", path, e.what()); return false; } catch (CException* e) { TCHAR errorMessage[512]{ 0 }; UINT nHelpContext = 0; e->GetErrorMessage(errorMessage, sizeof(errorMessage), &nHelpContext); TRACE("Load %s failed: %s\n", path, errorMessage); } catch (...) { TRACE("Load %s failed: Unkown"); return false; } return true; } bool CSigmaStore::Save(CSigmaDoc* pDoc) { CSplitPath sp(""); CString path = sp.GetAbsolutePath(pDoc->m_FileName); try { m_impl->SaveFileDataIfNeed(pDoc, path); m_impl->SaveActions(m_impl->GetActionRecords(pDoc), path); return true; } catch (const std::runtime_error &e) { TRACE("Save %s failed: %s\n", pDoc->m_FileName, e.what()); return false; } catch (CException* e) { TCHAR errorMessage[512]{ 0 }; UINT nHelpContext = 0; e->GetErrorMessage(errorMessage, sizeof(errorMessage), &nHelpContext); TRACE("Save %s failed: %s\n", path, errorMessage); } catch (...) { TRACE("Save %s failed: Unkown"); return false; } } void CSigmaStore::Clear(const CString& path) { try { m_impl->Clear(path); } catch (const std::runtime_error& e) { TRACE("Clear %s failed: %s\n", path, e.what()); } }