mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-20 12:35:43 +08:00
refs #904 Settings JSON files split into arbitrary levels of subfolders, one file per key.
This commit is contained in:
@@ -69,7 +69,7 @@ namespace BlackMisc
|
|||||||
CDataCacheRevision *m_rev = nullptr;
|
CDataCacheRevision *m_rev = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
CDataCache::CDataCache()
|
CDataCache::CDataCache() : CValueCache(1)
|
||||||
{
|
{
|
||||||
if (! QDir::root().mkpath(persistentStore()))
|
if (! QDir::root().mkpath(persistentStore()))
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,7 @@ namespace BlackMisc
|
|||||||
|
|
||||||
QString CDataCache::filenameForKey(const QString &key)
|
QString CDataCache::filenameForKey(const QString &key)
|
||||||
{
|
{
|
||||||
return CFileUtils::appendFilePaths(persistentStore(), CValueCache::filenameForKey(key));
|
return CFileUtils::appendFilePaths(persistentStore(), instance()->CValueCache::filenameForKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CDataCache::enumerateStore() const
|
QStringList CDataCache::enumerateStore() const
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
{
|
{
|
||||||
CSettingsCache::CSettingsCache()
|
CSettingsCache::CSettingsCache() : CValueCache(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CSettingsCache *CSettingsCache::instance()
|
CSettingsCache *CSettingsCache::instance()
|
||||||
@@ -58,7 +58,7 @@ namespace BlackMisc
|
|||||||
|
|
||||||
QString CSettingsCache::filenameForKey(const QString &key)
|
QString CSettingsCache::filenameForKey(const QString &key)
|
||||||
{
|
{
|
||||||
return CFileUtils::appendFilePaths(persistentStore(), CValueCache::filenameForKey(key));
|
return CFileUtils::appendFilePaths(persistentStore(), instance()->CValueCache::filenameForKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString CSettingsCache::relativeFilePath()
|
const QString CSettingsCache::relativeFilePath()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDBusMetaType>
|
#include <QDBusMetaType>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
@@ -162,8 +163,9 @@ namespace BlackMisc
|
|||||||
return cats;
|
return cats;
|
||||||
}
|
}
|
||||||
|
|
||||||
CValueCache::CValueCache(QObject *parent) : QObject(parent)
|
CValueCache::CValueCache(int fileSplitDepth, QObject *parent) : QObject(parent), m_fileSplitDepth(fileSplitDepth)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT_X(fileSplitDepth >= 0, Q_FUNC_INFO, "Negative value not allowed, use 0 for maximum split depth");
|
||||||
Q_ASSERT_X(QThread::currentThread() == qApp->thread(), Q_FUNC_INFO, "Cache constructed in wrong thread");
|
Q_ASSERT_X(QThread::currentThread() == qApp->thread(), Q_FUNC_INFO, "Cache constructed in wrong thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +367,7 @@ namespace BlackMisc
|
|||||||
QMap<QString, CVariantMap> namespaces;
|
QMap<QString, CVariantMap> namespaces;
|
||||||
for (auto it = values.cbegin(); it != values.cend(); ++it)
|
for (auto it = values.cbegin(); it != values.cend(); ++it)
|
||||||
{
|
{
|
||||||
namespaces[it.key().section('/', 0, 0)].insert(it.key(), it.value());
|
namespaces[it.key().section('/', 0, m_fileSplitDepth - 1)].insert(it.key(), it.value());
|
||||||
}
|
}
|
||||||
if (! QDir::root().mkpath(dir))
|
if (! QDir::root().mkpath(dir))
|
||||||
{
|
{
|
||||||
@@ -374,6 +376,10 @@ namespace BlackMisc
|
|||||||
for (auto it = namespaces.cbegin(); it != namespaces.cend(); ++it)
|
for (auto it = namespaces.cbegin(); it != namespaces.cend(); ++it)
|
||||||
{
|
{
|
||||||
CAtomicFile file(dir + "/" + it.key() + ".json");
|
CAtomicFile file(dir + "/" + it.key() + ".json");
|
||||||
|
if (! QDir::root().mkpath(QFileInfo(file).path()))
|
||||||
|
{
|
||||||
|
return CStatusMessage(this).error("Failed to create directory '%1'") << QFileInfo(file).path();
|
||||||
|
}
|
||||||
if (! file.open(QFile::ReadWrite | QFile::Text))
|
if (! file.open(QFile::ReadWrite | QFile::Text))
|
||||||
{
|
{
|
||||||
return CStatusMessage(this).error("Failed to open %1: %2") << file.fileName() << file.errorString();
|
return CStatusMessage(this).error("Failed to open %1: %2") << file.fileName() << file.errorString();
|
||||||
@@ -419,19 +425,20 @@ namespace BlackMisc
|
|||||||
QMap<QString, QStringList> keysInFiles;
|
QMap<QString, QStringList> keysInFiles;
|
||||||
for (const auto &key : keys)
|
for (const auto &key : keys)
|
||||||
{
|
{
|
||||||
keysInFiles[key.section('/', 0, 0)].push_back(key);
|
keysInFiles[key.section('/', 0, m_fileSplitDepth - 1) + ".json"].push_back(key);
|
||||||
}
|
}
|
||||||
if (keys.isEmpty())
|
if (keys.isEmpty())
|
||||||
{
|
{
|
||||||
for (const auto &filename : QDir(dir).entryInfoList({ "*.json" }, QDir::Files))
|
QDirIterator iter(dir, { "*.json" }, QDir::Files, QDirIterator::Subdirectories);
|
||||||
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
keysInFiles.insert(filename.completeBaseName(), {});
|
keysInFiles.insert(QDir(dir).relativeFilePath(iter.next()), {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
for (auto it = keysInFiles.cbegin(); it != keysInFiles.cend(); ++it)
|
for (auto it = keysInFiles.cbegin(); it != keysInFiles.cend(); ++it)
|
||||||
{
|
{
|
||||||
QFile file(dir + "/" + it.key() + ".json");
|
QFile file(QDir(dir).absoluteFilePath(it.key()));
|
||||||
if (! file.exists())
|
if (! file.exists())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -453,7 +460,7 @@ namespace BlackMisc
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString messagePrefix = QStringLiteral("Parsing %1.json").arg(it.key());
|
const QString messagePrefix = QStringLiteral("Parsing %1").arg(it.key());
|
||||||
auto messages = temp.convertFromMemoizedJsonNoThrow(json.object(), it.value(), this, messagePrefix);
|
auto messages = temp.convertFromMemoizedJsonNoThrow(json.object(), it.value(), this, messagePrefix);
|
||||||
if (it.value().isEmpty()) { messages.push_back(temp.convertFromMemoizedJsonNoThrow(json.object(), this, messagePrefix)); }
|
if (it.value().isEmpty()) { messages.push_back(temp.convertFromMemoizedJsonNoThrow(json.object(), this, messagePrefix)); }
|
||||||
if (! messages.isEmpty())
|
if (! messages.isEmpty())
|
||||||
@@ -508,9 +515,9 @@ namespace BlackMisc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CValueCache::filenameForKey(const QString &key)
|
QString CValueCache::filenameForKey(const QString &key) const
|
||||||
{
|
{
|
||||||
return key.section('/', 0, 0) + ".json";
|
return key.section('/', 0, m_fileSplitDepth - 1) + ".json";
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CValueCache::enumerateFiles(const QString &dir) const
|
QStringList CValueCache::enumerateFiles(const QString &dir) const
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ namespace BlackMisc
|
|||||||
static const CLogCategoryList &getLogCategories();
|
static const CLogCategoryList &getLogCategories();
|
||||||
|
|
||||||
//! Constructor.
|
//! Constructor.
|
||||||
explicit CValueCache(QObject *parent = nullptr);
|
explicit CValueCache(int fileSplitDepth, QObject *parent = nullptr);
|
||||||
|
|
||||||
//! Return map containing all values in the cache.
|
//! Return map containing all values in the cache.
|
||||||
//! If prefix is provided then only those values whose keys start with that prefix.
|
//! If prefix is provided then only those values whose keys start with that prefix.
|
||||||
@@ -219,7 +219,7 @@ namespace BlackMisc
|
|||||||
//! Return the (relative) filename that may is (or would be) used to save the value with the given key.
|
//! Return the (relative) filename that may is (or would be) used to save the value with the given key.
|
||||||
//! The file may or may not exist (because it might not have been saved yet).
|
//! The file may or may not exist (because it might not have been saved yet).
|
||||||
//! \threadsafe
|
//! \threadsafe
|
||||||
static QString filenameForKey(const QString &key);
|
QString filenameForKey(const QString &key) const;
|
||||||
|
|
||||||
//! List the Json files which are (or would be) used to save the current values.
|
//! List the Json files which are (or would be) used to save the current values.
|
||||||
//! The files may or may not exist (because they might not have been saved yet).
|
//! The files may or may not exist (because they might not have been saved yet).
|
||||||
@@ -317,6 +317,7 @@ namespace BlackMisc
|
|||||||
|
|
||||||
QMap<QString, ElementPtr> m_elements;
|
QMap<QString, ElementPtr> m_elements;
|
||||||
QMap<QString, QString> m_humanReadable;
|
QMap<QString, QString> m_humanReadable;
|
||||||
|
const int m_fileSplitDepth = 1; //!< How many levels of subdirectories to split JSON files
|
||||||
|
|
||||||
Element &getElement(const QString &key);
|
Element &getElement(const QString &key);
|
||||||
Element &getElement(const QString &key, QMap<QString, ElementPtr>::const_iterator pos);
|
Element &getElement(const QString &key, QMap<QString, ElementPtr>::const_iterator pos);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace BlackMiscTest
|
|||||||
{ "value4", CVariant::from(4) }
|
{ "value4", CVariant::from(4) }
|
||||||
};
|
};
|
||||||
|
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
QVERIFY(cache.getAllValues() == CVariantMap());
|
QVERIFY(cache.getAllValues() == CVariantMap());
|
||||||
cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
|
cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
|
||||||
QVERIFY(cache.getAllValues() == testData);
|
QVERIFY(cache.getAllValues() == testData);
|
||||||
@@ -131,7 +131,7 @@ namespace BlackMiscTest
|
|||||||
|
|
||||||
void CTestValueCache::localOnly()
|
void CTestValueCache::localOnly()
|
||||||
{
|
{
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
||||||
CValueCacheUser user1(&cache);
|
CValueCacheUser user1(&cache);
|
||||||
CValueCacheUser user2(&cache);
|
CValueCacheUser user2(&cache);
|
||||||
@@ -140,7 +140,7 @@ namespace BlackMiscTest
|
|||||||
|
|
||||||
void CTestValueCache::localOnlyWithThreads()
|
void CTestValueCache::localOnlyWithThreads()
|
||||||
{
|
{
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
||||||
CValueCacheUser user1(&cache);
|
CValueCacheUser user1(&cache);
|
||||||
CValueCacheUser user2(&cache);
|
CValueCacheUser user2(&cache);
|
||||||
@@ -158,8 +158,8 @@ namespace BlackMiscTest
|
|||||||
json.insert("processId", otherProcess.getProcessId() + 1);
|
json.insert("processId", otherProcess.getProcessId() + 1);
|
||||||
otherProcess.convertFromJson(json);
|
otherProcess.convertFromJson(json);
|
||||||
|
|
||||||
CValueCache thisCache;
|
CValueCache thisCache(1);
|
||||||
CValueCache otherCache;
|
CValueCache otherCache(1);
|
||||||
connect(&thisCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CValueCachePacket &values)
|
connect(&thisCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CValueCachePacket &values)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, thisProcess));
|
QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, thisProcess));
|
||||||
@@ -191,7 +191,7 @@ namespace BlackMiscTest
|
|||||||
|
|
||||||
void CTestValueCache::batched()
|
void CTestValueCache::batched()
|
||||||
{
|
{
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
for (int i = 0; i < 2; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Empty cache value")); }
|
||||||
CValueCacheUser user1(&cache);
|
CValueCacheUser user1(&cache);
|
||||||
CValueCacheUser user2(&cache);
|
CValueCacheUser user2(&cache);
|
||||||
@@ -225,7 +225,7 @@ namespace BlackMiscTest
|
|||||||
{ "value3", CVariant::from(3) }
|
{ "value3", CVariant::from(3) }
|
||||||
};
|
};
|
||||||
|
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
cache.loadFromJson(testJson);
|
cache.loadFromJson(testJson);
|
||||||
QVERIFY(cache.getAllValues() == testData);
|
QVERIFY(cache.getAllValues() == testData);
|
||||||
QVERIFY(cache.saveToJson() == testJson);
|
QVERIFY(cache.saveToJson() == testJson);
|
||||||
@@ -243,7 +243,7 @@ namespace BlackMiscTest
|
|||||||
{ "namespace2/aircraft", CVariant::from(aircraft) },
|
{ "namespace2/aircraft", CVariant::from(aircraft) },
|
||||||
{ "namespace2/atcstations", CVariant::from(atcStations) }
|
{ "namespace2/atcstations", CVariant::from(atcStations) }
|
||||||
};
|
};
|
||||||
CValueCache cache;
|
CValueCache cache(1);
|
||||||
cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
|
cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
|
||||||
|
|
||||||
QDir dir(QDir::currentPath() + "/testcache");
|
QDir dir(QDir::currentPath() + "/testcache");
|
||||||
@@ -257,7 +257,7 @@ namespace BlackMiscTest
|
|||||||
QCOMPARE(files[0].fileName(), QString("namespace1.json"));
|
QCOMPARE(files[0].fileName(), QString("namespace1.json"));
|
||||||
QCOMPARE(files[1].fileName(), QString("namespace2.json"));
|
QCOMPARE(files[1].fileName(), QString("namespace2.json"));
|
||||||
|
|
||||||
CValueCache cache2;
|
CValueCache cache2(1);
|
||||||
status = cache2.loadFromFiles(dir.absolutePath());
|
status = cache2.loadFromFiles(dir.absolutePath());
|
||||||
QVERIFY(status.isSuccess());
|
QVERIFY(status.isSuccess());
|
||||||
QCOMPARE(cache2.getAllValues(), testData);
|
QCOMPARE(cache2.getAllValues(), testData);
|
||||||
|
|||||||
Reference in New Issue
Block a user