You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
7.7 KiB
C++

#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 <chrono>
#include <filesystem>
#include <optional>
#include <mutex>
/**
* @brief CSigmaStore::Impl 类的实现
*/
class CSigmaStore::Impl
{
public:
Impl(std::shared_ptr<IActionStorage> storage)
: m_storage(storage)
{
}
void SetActions(CSigmaDoc* pDoc)
{
const std::deque<CAction*>& redoStack = pDoc->GetActionManager()->GetRedoStack();
std::vector<ActionRecord> 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<CActionItem*>(pAction))
{
const_cast<std::deque<CAction*>&>(redoStack).push_front(pAction);
pDoc->Redo();
}
}
}
}
/**
* @brief 保存操作记录
* @param actions 操作记录向量
* @param path 文件路径
*/
void SaveActions(std::vector<ActionRecord>& 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<FileData>(
-1, filePath, reinterpret_cast<std::vector<char>&>(rawData), FormatTime(std::chrono::system_clock::now())
);
m_asyncWorker.postTask([this, filePath, pFileData]()
{
m_storage->InsertFileData(filePath, *pFileData);
});
}
}
/**
* @brief 获取文档的操作记录
* @param pDoc 文档指针
* @return 操作记录向量
*/
std::vector<ActionRecord> GetActionRecords(const CSigmaDoc* pDoc)
{
if (!m_storage)
{
throw std::runtime_error("m_storage is nullptr");
}
std::vector<ActionRecord> result;
auto actionManager = const_cast<CSigmaDoc*>(pDoc)->GetActionManager();
if (actionManager)
{
std::vector<CAction*> 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<FileData> 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<ActionRecord>& actions)
{
std::set<std::string> 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<IActionStorage> m_storage;
AsyncWorker m_asyncWorker;
std::unordered_map<CString, std::set<std::string>, CStringHash> m_actionCache;
};
CSigmaStore::CSigmaStore(std::shared_ptr<IActionStorage> storage)
: m_impl(std::make_unique<CSigmaStore::Impl>(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<std::vector<BYTE>&>(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 (CArchiveException *e)
{
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());
}
}