Switch to JSON for application settings

This commit is contained in:
Atlante45 2016-09-13 17:28:40 -07:00
parent 16af14d1f6
commit eaca3b672f
17 changed files with 324 additions and 82 deletions

View file

@ -12,7 +12,6 @@
#include <assert.h>
#include <QProcess>
#include <QSettings>
#include <QSharedMemory>
#include <QThread>
#include <QTimer>
@ -50,8 +49,6 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
{
LogUtils::init();
QSettings::setDefaultFormat(QSettings::IniFormat);
DependencyManager::set<AccountManager>();
auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>();

View file

@ -40,9 +40,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
ShutdownEventListener::getInstance();
# endif
setApplicationName(BuildInfo::ASSIGNMENT_CLIENT_NAME);
setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
setOrganizationDomain("highfidelity.io");
setApplicationName("assignment-client");
setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
setApplicationVersion(BuildInfo::VERSION);
// use the verbose message handler in Logging

View file

@ -1,6 +1,6 @@
//
// BuildInfo.h.in
// cmake/macros
// cmake/templates
//
// Created by Stephen Birarda on 1/14/16.
// Copyright 2015 High Fidelity, Inc.
@ -11,10 +11,18 @@
#define USE_STABLE_GLOBAL_SERVICES @USE_STABLE_GLOBAL_SERVICES@
#include <QString>
namespace BuildInfo {
// WARNING: This file has been auto-generated.
// Check cmake/templates/BuildInfo.h.in if you want to modify it.
const QString INTERFACE_NAME = "Interface";
const QString ASSIGNMENT_CLIENT_NAME = "assignment-client";
const QString DOMAIN_SERVER_NAME = "domain-server";
const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@";
const QString ORGANIZATION_DOMAIN = "highfidelity.io";
const QString VERSION = "@BUILD_VERSION@";
const QString BUILD_BRANCH = "@BUILD_BRANCH@";
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
}

View file

@ -79,11 +79,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit);
setApplicationName(BuildInfo::DOMAIN_SERVER_NAME);
setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
setOrganizationDomain("highfidelity.io");
setApplicationName("domain-server");
setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
setApplicationVersion(BuildInfo::VERSION);
QSettings::setDefaultFormat(QSettings::IniFormat);
qDebug() << "Setting up domain-server";

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DomainServerSettingsManager.h"
#include <algorithm>
#include <QtCore/QCoreApplication>
@ -16,20 +18,19 @@
#include <QtCore/QFile>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QSettings>
#include <QtCore/QStandardPaths>
#include <QtCore/QUrl>
#include <QtCore/QUrlQuery>
#include <AccountManager.h>
#include <QTimeZone>
#include <AccountManager.h>
#include <Assignment.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
#include <NLPacketList.h>
#include <NumericalConstants.h>
#include <SettingHandle.h>
#include "DomainServerSettingsManager.h"
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
@ -41,6 +42,8 @@ const QString DESCRIPTION_COLUMNS_KEY = "columns";
const QString SETTINGS_VIEWPOINT_KEY = "viewpoint";
static const Setting::Handle<double> JSON_SETTING_VERSION("json-settings/version", 0.0);
DomainServerSettingsManager::DomainServerSettingsManager() :
_descriptionArray(),
_configMap()
@ -101,9 +104,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
// What settings version were we before and what are we using now?
// Do we need to do any re-mapping?
QSettings appSettings;
const QString JSON_SETTINGS_VERSION_KEY = "json-settings/version";
double oldVersion = appSettings.value(JSON_SETTINGS_VERSION_KEY, 0.0).toDouble();
double oldVersion = JSON_SETTING_VERSION.get();
if (oldVersion != _descriptionVersion) {
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
@ -299,7 +300,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
unpackPermissions();
// write the current description version to our settings
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
JSON_SETTING_VERSION.set(_descriptionVersion);
}
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {

View file

@ -1,4 +0,0 @@
[INFO]
name=Interface
organizationName=High Fidelity
organizationDomain=highfidelity.io

View file

@ -406,11 +406,7 @@ bool setupEssentials(int& argc, char** argv) {
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
// Set build version
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
Setting::preInit();
Setting::init();
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
@ -423,8 +419,6 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
Setting::init();
// Set dependencies
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
DependencyManager::set<ScriptEngines>();

View file

@ -18,18 +18,17 @@
#include <QLabel>
#include <PathUtils.h>
#include <QRadioButton>
#include <QSettings>
#include <QStandardPaths>
#include <QVBoxLayout>
#include <QtCore/QUrl>
#include "Menu.h"
#include <SettingHandle.h>
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
bool CrashHandler::checkForResetSettings(bool suppressPrompt) {
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
Settings settings;
settings.beginGroup("Developer");
QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);
QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings);
@ -109,8 +108,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
return;
}
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
Settings settings;
const QString ADDRESS_MANAGER_GROUP = "AddressManager";
const QString ADDRESS_KEY = "address";
const QString AVATAR_GROUP = "Avatar";

View file

@ -14,7 +14,6 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QSettings>
#include <gpu/Batch.h>

View file

@ -15,7 +15,6 @@
#include <QDir>
#include <QLocalSocket>
#include <QLocalServer>
#include <QSettings>
#include <QSharedMemory>
#include <QTranslator>
@ -31,12 +30,13 @@
#include "MainWindow.h"
#include <QtCore/QProcess>
#ifdef HAS_BUGSPLAT
#include <BuildInfo.h>
#ifdef HAS_BUGSPLAT
#include <BugSplat.h>
#include <CrashReporter.h>
#endif
int main(int argc, const char* argv[]) {
#if HAS_BUGSPLAT
static QString BUG_SPLAT_DATABASE = "interface_alpha";
@ -45,7 +45,13 @@ int main(int argc, const char* argv[]) {
#endif
disableQtBearerPoll(); // Fixes wifi ping spikes
// Set application infos
QCoreApplication::setApplicationName(BuildInfo::INTERFACE_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
bool instanceMightBeRunning = true;
@ -168,7 +174,6 @@ int main(int argc, const char* argv[]) {
int exitCode;
{
QSettings::setDefaultFormat(QSettings::IniFormat);
Application app(argc, const_cast<char**>(argv), startupTime);
// If we failed the OpenGLVersion check, log it.

View file

@ -12,8 +12,6 @@
#ifndef hifi_AutoUpdater_h
#define hifi_AutoUpdater_h
#include <QtCore/QSettings>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QMap>

View file

@ -95,7 +95,7 @@ QUrl AddressManager::currentFacingShareableAddress() const {
void AddressManager::loadSettings(const QString& lookupString) {
if (lookupString.isEmpty()) {
handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings);
handleUrl(currentAddressHandle.get(), LookupTrigger::StartupFromSettings);
} else {
handleUrl(lookupString, LookupTrigger::StartupFromSettings);
}

View file

@ -19,16 +19,12 @@
const QString Settings::firstRun { "firstRun" };
Settings::Settings() :
_manager(DependencyManager::get<Setting::Manager>()),
_locker(&(_manager->getLock()))
Settings::Settings() : _manager(DependencyManager::get<Setting::Manager>())
{
}
Settings::~Settings() {
if (_prefixes.size() != 0) {
qFatal("Unstable Settings Prefixes: You must call endGroup for every beginGroup and endArray for every begin*Array call");
}
QString Settings::fileName() const {
return _manager->fileName();
}
void Settings::remove(const QString& key) {
@ -54,17 +50,14 @@ bool Settings::contains(const QString& key) const {
}
int Settings::beginReadArray(const QString & prefix) {
_prefixes.push(prefix);
return _manager->beginReadArray(prefix);
}
void Settings::beginWriteArray(const QString& prefix, int size) {
_prefixes.push(prefix);
_manager->beginWriteArray(prefix, size);
}
void Settings::endArray() {
_prefixes.pop();
_manager->endArray();
}
@ -73,19 +66,15 @@ void Settings::setArrayIndex(int i) {
}
void Settings::beginGroup(const QString& prefix) {
_prefixes.push(prefix);
_manager->beginGroup(prefix);
}
void Settings::endGroup() {
_prefixes.pop();
_manager->endGroup();
}
void Settings::setValue(const QString& name, const QVariant& value) {
if (_manager->value(name) != value) {
_manager->setValue(name, value);
}
_manager->setValue(name, value);
}
QVariant Settings::value(const QString& name, const QVariant& defaultValue) const {

View file

@ -14,7 +14,6 @@
#include <type_traits>
#include <QtCore/QSettings>
#include <QtCore/QStack>
#include <QtCore/QString>
#include <QtCore/QVariant>
@ -32,7 +31,8 @@ class Settings {
public:
static const QString firstRun;
Settings();
~Settings();
QString fileName() const;
void remove(const QString& key);
QStringList childGroups() const;
@ -61,8 +61,6 @@ public:
private:
QSharedPointer<Setting::Manager> _manager;
QWriteLocker _locker;
QStack<QString> _prefixes;
};
namespace Setting {

View file

@ -20,10 +20,245 @@
#include "SettingManager.h"
#include "SharedLogging.h"
#include <QSettings>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRect>
#include <QSize>
#include <QPoint>
#include <QDataStream>
#include <QStringList>
QStringList splitArgs(const QString& string, int idx) {
int length = string.length();
Q_ASSERT(length > 0);
Q_ASSERT(string.at(idx) == QLatin1Char('('));
Q_ASSERT(string.at(length - 1) == QLatin1Char(')'));
QStringList result;
QString item;
for (++idx; idx < length; ++idx) {
QChar c = string.at(idx);
if (c == QLatin1Char(')')) {
Q_ASSERT(idx == length - 1);
result.append(item);
} else if (c == QLatin1Char(' ')) {
result.append(item);
item.clear();
} else {
item.append(c);
}
}
return result;
}
QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) {
qDebug() << Q_FUNC_INFO << map.size() << "in map";
QJsonObject object;
for (auto it = map.cbegin(); it != map.cend(); ++it) {
auto& key = it.key();
auto& variant = it.value();
auto variantType = variant.type();
// Switch some types so they are readable/modifiable in the json file
if (variantType == QVariant(1.0f).type()) { // float
variantType = QVariant::Double;
}
if (variantType == QVariant((quint16)0).type()) { // uint16
variantType = QVariant::UInt;
}
switch (variantType) {
case QVariant::Map:
case QVariant::List:
case QVariant::Hash: {
qCritical() << "Unsupported variant type" << variant.typeName();
Q_ASSERT(false);
break;
}
case QVariant::Invalid:
object.insert(key, QJsonValue());
break;
case QVariant::LongLong:
case QVariant::ULongLong:
case QVariant::Int:
case QVariant::UInt:
case QVariant::Bool:
case QVariant::Double:
object.insert(key, QJsonValue::fromVariant(variant));
break;
case QVariant::String: {
QString result = variant.toString();
if (result.startsWith(QLatin1Char('@'))) {
result.prepend(QLatin1Char('@'));
}
object.insert(key, result);
break;
}
case QVariant::ByteArray: {
QByteArray a = variant.toByteArray();
QString result = QLatin1String("@ByteArray(");
result += QString::fromLatin1(a.constData(), a.size());
result += QLatin1Char(')');
object.insert(key, result);
break;
}
case QVariant::Rect: {
QRect r = qvariant_cast<QRect>(variant);
QString result = QLatin1String("@Rect(");
result += QString::number(r.x());
result += QLatin1Char(' ');
result += QString::number(r.y());
result += QLatin1Char(' ');
result += QString::number(r.width());
result += QLatin1Char(' ');
result += QString::number(r.height());
result += QLatin1Char(')');
object.insert(key, result);
break;
}
case QVariant::Size: {
QSize s = qvariant_cast<QSize>(variant);
QString result = QLatin1String("@Size(");
result += QString::number(s.width());
result += QLatin1Char(' ');
result += QString::number(s.height());
result += QLatin1Char(')');
object.insert(key, result);
break;
}
case QVariant::Point: {
QPoint p = qvariant_cast<QPoint>(variant);
QString result = QLatin1String("@Point(");
result += QString::number(p.x());
result += QLatin1Char(' ');
result += QString::number(p.y());
result += QLatin1Char(')');
object.insert(key, result);
break;
}
default: {
QByteArray array;
{
QDataStream stream(&array, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_4_0);
stream << variant;
}
QString result = QLatin1String("@Variant(");
result += QString::fromLatin1(array.constData(), array.size());
result += QLatin1Char(')');
object.insert(key, result);
break;
}
}
}
qDebug() << Q_FUNC_INFO << object.size() << "in json";
return QJsonDocument(object);
}
QSettings::SettingsMap jsonDocumentToVariantMap(const QJsonDocument& document) {
if (!document.isObject()) {
qWarning() << "Settings file does not contain a JSON object";
return QSettings::SettingsMap();
}
auto object = document.object();
qDebug() << Q_FUNC_INFO << object.size() << "in json";
QSettings::SettingsMap map;
for (auto it = object.begin(); it != object.end(); ++it) {
QVariant result;
if (!it->isString()) {
result = it->toVariant();
} else {
auto string = it->toString();
if (string.startsWith(QLatin1String("@@"))) {
result = QVariant(string.mid(1));
} else if (string.startsWith(QLatin1Char('@'))) {
if (string.endsWith(QLatin1Char(')'))) {
if (string.startsWith(QLatin1String("@ByteArray("))) {
result = QVariant(string.toLatin1().mid(11, string.size() - 12));
} else if (string.startsWith(QLatin1String("@Variant("))) {
QByteArray a(string.toLatin1().mid(9));
QDataStream stream(&a, QIODevice::ReadOnly);
stream.setVersion(QDataStream::Qt_4_0);
stream >> result;
} else if (string.startsWith(QLatin1String("@Rect("))) {
QStringList args = splitArgs(string, 5);
if (args.size() == 4) {
result = QRect(args[0].toInt(), args[1].toInt(),
args[2].toInt(), args[3].toInt());
}
} else if (string.startsWith(QLatin1String("@Size("))) {
QStringList args = splitArgs(string, 5);
if (args.size() == 2) {
result = QSize(args[0].toInt(), args[1].toInt());
}
} else if (string.startsWith(QLatin1String("@Point("))) {
QStringList args = splitArgs(string, 6);
if (args.size() == 2) {
result = QPoint(args[0].toInt(), args[1].toInt());
}
}
}
}
}
map.insert(it.key(), result);
}
qDebug() << Q_FUNC_INFO << map.size() << "in map";
return map;
}
bool readJSONFile(QIODevice& device, QSettings::SettingsMap& map) {
QJsonParseError jsonParseError;
auto bytesRead = device.readAll();
auto document = QJsonDocument::fromJson(bytesRead, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qDebug() << "Error parsing QSettings file:" << jsonParseError.errorString();
return false;
}
map = jsonDocumentToVariantMap(document);
return true;
}
bool writeJSONFile(QIODevice& device, const QSettings::SettingsMap& map) {
auto document = variantMapToJsonDocument(map);
auto jsonByteArray = document.toJson(QJsonDocument::Indented);
auto bytesWritten = device.write(jsonByteArray);
return bytesWritten == jsonByteArray.size();
}
const auto jsonFormat = QSettings::registerFormat("json", readJSONFile, writeJSONFile);
namespace Setting {
static QSharedPointer<Manager> globalManager;
const QString Interface::FIRST_RUN { "firstRun" };
// cleans up the settings private instance. Should only be run once at closing down.
void cleanupPrivateInstance() {
@ -40,30 +275,58 @@ namespace Setting {
settingsManagerThread->quit();
settingsManagerThread->wait();
}
// Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand,
void init() {
// Set settings format
QSettings::setDefaultFormat(jsonFormat);
QSettings settings;
// Set up application settings. Should only be run once at startup.
void preInit() {
// read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings applicationInfo(PathUtils::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
// set the associated application properties
applicationInfo.beginGroup("INFO");
QCoreApplication::setApplicationName(applicationInfo.value("name").toString());
QCoreApplication::setOrganizationName(applicationInfo.value("organizationName").toString());
QCoreApplication::setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
if (settings.allKeys().size() == 0) {
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings iniSettings;
if (iniSettings.allKeys().size()) {
qDebug() << "No data in json settings file, trying to load old ini settings file.";
for (auto key : iniSettings.allKeys()) {
auto variant = iniSettings.value(key);
if (variant.type() == QVariant::String) {
auto string = variant.toString();
if (string == "true") {
variant = true;
} else if (string == "false") {
variant = false;
} else {
bool ok;
double value = string.toDouble(&ok);
if (ok) {
variant = value;
}
}
}
settings.setValue(key, variant);
}
qDebug() << "Loaded" << settings.allKeys().size() << "keys from ini settings file.";
}
QSettings::setDefaultFormat(jsonFormat);
}
qDebug() << "First run:" << settings.contains("firstRun") << settings.value("firstRun", true);
// Delete Interface.ini.lock file if it exists, otherwise Interface freezes.
QSettings settings;
QString settingsLockFilename = settings.fileName() + ".lock";
QFile settingsLockFile(settingsLockFilename);
if (settingsLockFile.exists()) {
bool deleted = settingsLockFile.remove();
qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename;
}
}
// Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand,
void init() {
// Let's set up the settings Private instance on its own thread
QThread* thread = new QThread();
Q_CHECK_PTR(thread);

View file

@ -20,14 +20,11 @@
namespace Setting {
class Manager;
void preInit();
void init();
void cleanupSettings();
class Interface {
public:
static const QString FIRST_RUN;
const QString& getKey() const { return _key; }
bool isSet() const { return _isSet; }

View file

@ -9,12 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SettingManager.h"
#include <QtCore/QThread>
#include <QtCore/QDebug>
#include <QtCore/QUuid>
#include "SettingInterface.h"
#include "SettingManager.h"
namespace Setting {
@ -32,7 +33,6 @@ namespace Setting {
// Custom deleter does nothing, because we need to shutdown later than the dependency manager
void Manager::customDeleter() { }
void Manager::registerHandle(Interface* handle) {
const QString& key = handle->getKey();
withWriteLock([&] {