28 #include <QJsonDocument> 54 QObject(parent), m_s(nullptr), m_watching(false)
67 QMetaObject::Connection connection = connect(
m_watcher,
71 zCritical() <<
"Settings object watcher connection failed.";
85 qmlRegisterType<ZSettings>(
"org.zuble.qml", 1, 0,
"ZSettings");
90 zDebug() <<
"SETTINGS FILE MODIFIED SIGNAL FOR THREAD:" <<
94 if(path ==
m_s->fileName())
96 zDebug() <<
"EMITTING SETTINGS FILE MODIFIED! PATH=" <<
m_s->fileName();
102 zDebug() <<
"NOT MODIFIED! ACTUAL PATH=" <<
m_s->fileName();
105 zDebug() <<
"DUMPING FILE WATCHER PATHS: ";
127 map.insert(
"UserScope", QSettings::UserScope);
128 map.insert(
"SystemScope", QSettings::SystemScope);
130 map.insert(
"NoError", QSettings::NoError);
131 map.insert(
"AccessError", QSettings::AccessError);
132 map.insert(
"FormatError", QSettings::FormatError);
134 m_tags = QVariant::fromValue(map);
151 zWarning() <<
"ZSettings::getFileName - WARNING: settings object not opened.";
155 ret =
m_s->fileName();
158 return m_s->fileName();
178 if(bytes.isNull() || bytes.isEmpty())
180 zWarning() <<
"ZSettings::readFunc - can't read from settings device.";
187 QJsonParseError error;
188 QJsonDocument doc(QJsonDocument::fromJson(bytes, &error));
190 if(doc.isNull() || error.error != QJsonParseError::NoError)
192 zCritical() <<
"ZSettings::readFunc - ERROR parsing JSON file: " 193 << error.errorString();
201 QVariant vMap(doc.toVariant());
214 zDebug() <<
"ZSettings::writeFunc enetered";
224 QJsonDocument doc(QJsonDocument::fromVariant(vMap));
228 QByteArray bytes(doc.toJson());
232 qint64
status = device.write(bytes);
236 zCritical() <<
"ZSettings::writeFunc - an error occurred writing to " 237 "the settings device: " << device.errorString();
253 zDebug() <<
"ZSettings begin dumping map...";
255 for(
int i=0; i<keys.count(); i++)
257 QString key(keys.at(i));
258 QVariant
value(map.value(key));
262 if(
value.type() == QVariant::Map)
269 zDebug() <<
"key=" << key <<
", value=" <<
value.toString();
273 zDebug() <<
"ZSettings end dumping map.";
279 zDebug() <<
"begin group " + groupKey;
281 QVariantMap map = group.toMap();
283 QList<QString> keys = map.keys();
285 for(
int i=0; i<map.count(); i++)
287 QString key(keys.at(i));
288 QVariant
value(map.value(key));
290 if(
value.type() == QVariant::Map)
297 zDebug() <<
"key=" << key <<
", value=" <<
value.toString();
300 zDebug() <<
"end group " + groupKey;
305 const QString& application,
306 const QString& organization)
308 QSettings::Scope scp = QSettings::Scope(scope);
312 zWarning() <<
"Programming error: object is already opened.";
317 if(scp != QSettings::UserScope && scp != QSettings::SystemScope)
319 zWarning() <<
"Invalid scope: " << scope
320 <<
" opening settings in User scope...";
322 scp = QSettings::UserScope;
329 if(organization.isEmpty())
330 org = QCoreApplication::organizationName();
336 if(application.isEmpty())
337 app = QCoreApplication::applicationName();
344 m_s =
new QSettings(
m_format, scp, org, app,
this);
346 QString filePath =
m_s->fileName();
360 if(QFile::exists(filePath))
376 zCritical() <<
"Failed to open organization: " 377 << org <<
", application: " << app;
408 if(QFile::exists(filePath))
410 zDebug() <<
"Settings file exists: " << filePath;
416 QFile file(filePath);
418 bool status = file.open(QIODevice::WriteOnly | QIODevice::Text);
422 zWarning() <<
"Can't open empty settings file: " << filePath
423 <<
" -- Error message: " << file.errorString();
427 QString emptyJSONObject(
"{}");
429 qint64 bytesOut = file.write(emptyJSONObject.toUtf8());
433 zWarning() <<
"Can't write to empty settings file: " << filePath
434 <<
" -- Error message: " << file.errorString();
443 if(QFile::exists(filePath))
445 zDebug() <<
"Created empty settings file: " << filePath;
451 zDebug() <<
"FAILED to create empty settings file: " << filePath;
461 zWarning() <<
"Programming error: object is already opened.";
472 if(realPath.isEmpty())
473 throw ZblException(
"Can't open empty settings file path.");
479 zCritical() <<
"Failed to open settings file: " 501 QString filePath =
m_s->fileName();
503 QSettings::Status
status =
m_s->status();
505 if(status != QSettings::NoError)
508 if(status == QSettings::AccessError)
509 zCritical() <<
"ERROR: Can't access settings file: " << filePath;
510 else if(status == QSettings::FormatError)
511 zCritical() <<
"ERROR: Settings file has invalid format: " << filePath;
513 zCritical() <<
"ERROR: Unknown failure type from QSettings: " << filePath;
532 const QString& metaPath,
533 QSettings::Scope
scope,
534 const QString organization,
535 const QString application)
540 QString org = organization;
545 QString app = application;
559 zCritical() <<
"ZSettings::insertBundle can't load settings bundle id: " 567 emit
zApp.settingsBundleAvailable(
id);
576 const QString& dataPath,
584 zDebug() <<
"Replacing bundle id: " << id;
605 zCritical() <<
"ZSettings::insertBundle can't load settings bundle id: " 613 emit
zApp.settingsBundleAvailable(
id);
640 ret = QVariant::fromValue(vm);
654 zWarning() <<
"Invalid bundle ID: " << id;
667 if(QDir::isRelativePath(dataPath))
670 dataPath = QDir::cleanPath(dataPath);
672 if(!zSet->
open(dataPath))
674 "Can't open settings data for path: %1").arg(dataPath));
680 QSettings::Scope
scope;
683 org = QCoreApplication::organizationName();
688 if(bundle.
m_scope == QSettings::SystemScope)
689 scope = QSettings::SystemScope;
691 scope = QSettings::UserScope;
693 if(!zSet->
open(scope, app, org))
696 "Can't open settings data for orgainzation/app: %1/%2").arg(org).arg(app));
716 throw ZblException(QString(
"Invalid bundle ID: %1").arg(
id));
724 throw ZblException(QString(
"Invalid bundle ID: %1").arg(
id));
737 const int keyCount = keys.count();
739 for(
int i=0; i<keyCount; i++)
741 const QString nextKey = keys.at(i);
745 return QVariant::fromValue(ret);
777 zWarning() <<
"FAILED - invalid configuration file path: NULL";
785 QFile cfg(configFilePath);
790 "ABORTING - Nonexistent configuration file: %1").arg(configFilePath);
798 QUrl url(QUrl::fromLocalFile(configFilePath));
809 static const int x_operation = 0;
810 static const int x_id = 1;
811 static const int x_metaPath = 2;
812 static const int x_dataPath = 3;
813 static const int x_scope = 3;
814 static const int x_org = 4;
815 static const int x_app = 5;
817 QString query =
"declare default element namespace 'http://zuble.org/schema/zuble/zblconfig'; \n" 819 "declare function local:if-empty( \n" 820 "$node as node()?, $value as xs:anyAtomicType) as xs:string* \n" 821 "{ if(string($node) != '') then string($node) else $value };" 822 "declare function local:appSettings($s as element()?) as xs:string* \n" 824 " local:if-empty($s/@xml:id,' '), \n" 825 " local:if-empty($s/meta_path,' '), \n" 826 " local:if-empty($s/setting-data/@scope,' '), \n" 827 " local:if-empty($s/setting-data/organization,' '), \n" 828 " local:if-empty($s/setting-data/application,' ')) }; \n" 829 "declare function local:pathSettings($s as element()?) as xs:string* \n" 831 " local:if-empty($s/@xml:id,' '), \n" 832 " local:if-empty($s/meta_path,' '), \n" 833 " local:if-empty($s/data_path,' ')) }; \n" 835 "for $bundle in /zblconfig/settings/setting_bundle \n" 836 " let $app := $bundle/setting-data/application \n" 837 " let $path := $bundle/data_path \n" 838 " return (if($path) then local:pathSettings($bundle) \n" 839 " else if($app) then local:appSettings($bundle) else '=invalid')";
841 bool status = xq.setFocus(url);
846 QString(
"QXmlQuery can't load configuration file: %1").arg(
847 url.path().toUtf8().constData());
858 zWarning() << QString(
"Programming ERROR: invalid XQuery: %1").arg(query.toUtf8().constData());
865 QStringList xmlResults;
867 xq.evaluateTo(&xmlResults);
869 const int count = xmlResults.count();
881 for(
int j=0; j<count;j++)
883 const QString op = xmlResults[j+x_operation];
891 xmlResults[j+x_metaPath],
894 xmlResults[j+x_app]);
898 else if(op ==
"=path")
904 xmlResults[j+x_metaPath],
909 else if(op ==
"=invalid")
936 if(QDir::isAbsolutePath(ret))
939 QDir home(QDir::home());
941 return home.filePath(ret);
947 static const int keyIx = 0;
948 static const int valueIx = 1;
952 QString settingsMetafilePath;
957 settingsMetafilePath =
"Query text: ";
958 settingsMetafilePath += settingsBundle.
m_metaData.left(30);
959 settingsMetafilePath +=
"...";
969 settingsMetafilePath = settingsBundle.
m_metaPath;
979 zCritical() <<
"XQuery can't load application settings metadata file: " 980 << settingsMetafilePath;
985 static const QString query =
986 "declare default element namespace " 987 "'http://zuble.org/schema/zuble/settings'; \n" 988 " for $setting in /appsettings/settings/setting \n" 989 " let $key := concat(xs:string($setting/@group), " 990 " \"/\", xs:string($setting/@xml:id)) \n" 991 " let $value := xs:string($setting/default) \n" 992 " return (($key,$value))";
999 zCritical() <<
"Zuble internal programming ERROR: settings query is invalid.";
1007 zCritical() <<
"ERROR: Can't process application settings metadata file: " 1008 << settingsMetafilePath
1009 <<
", Error message = " 1014 const int count = data.size();
1018 zCritical() <<
"WARNING: Invalid data in application settings metadata file: " 1019 << settingsMetafilePath;
1025 if(!zSet.
open(settingsBundle))
1027 zCritical() <<
"WARNING: Can't open application settings data for settings bundle ID: " 1032 bool needSync =
false;
1034 for(
int i=0; i<count; i += 2)
1036 QString key(data.at(i+keyIx));
1040 QString
value(data.at(i+valueIx));
1065 if(scope == QSettings::SystemScope)
1066 return QString(
"system");
1067 else if(scope == QSettings::UserScope)
1068 return QString(
"user");
1075 QString lc = text.toLower();
1078 return QSettings::SystemScope;
1079 else if(lc ==
"user")
1080 return QSettings::UserScope;
1082 return QSettings::Scope(-1);
1113 QString settingName = QString(
"%1/%2").arg(
m_s->group(), key);
1114 QVariant oldValue =
m_s->value(key);
1116 if(oldValue != value)
1118 m_s->setValue(key, value);
1153 m_s->beginGroup(prefix);
1185 m_s->beginWriteArray(prefix, size);
1201 m_s->setArrayIndex(i);
static qint64 m_maxFileSize
Maximum size settings file that Zuble will try to read.
static QVariant getBundle(const QString &id)
QString getFileName() const
Q_INVOKABLE QVariant allBundles() const
static const QString m_ext
File extension for Zuble's custom JSON format files: ".zbl".
This class wraps QFileSystemWatcher and adds path reference counting.
static QString getAppName()
Obtains the name of the Zuble application.
static void zInit()
Initialize static variables.
static bool isInitialized()
true if the ZApplication object has completed initialization, false otherwise
static void registerType()
Registers ZSettings as a QML type.
Q_INVOKABLE void setValue(const QString &key, const QVariant &value)
static ZblApp & zInstance()
Obtains a reference to the ZblApp object for the current thread.
static ZFileSystemWatcher * m_watcher
Monitors settings data files for updates from this and other applications.
Q_INVOKABLE QString group() const
QStringList getChildGroups() const
void setFocusUrl(const QString &url)
sets the XQuery focus url and calls setQuery on the encapsulated QXmlQuery object.
QString getOrganizationName() const
Q_INVOKABLE void remove(const QString &key)
Q_INVOKABLE QVariant bundle(const QString &id) const
QStringList getAllKeys() const
static QSettings::Format m_format
QSettings custom format value assigned to Zuble for this application instance. This is the value retu...
Q_INVOKABLE void endGroup()
#define ZBL_REGISTER_LOGGED_OBJECT
Q_INVOKABLE int status() const
static QString getDataPath()
Obtains the canonical path to the Zuble application's data directory.
Q_INVOKABLE bool removePath(const QString &file)
ZSettings(QObject *parent=nullptr)
static QString getOrganization()
Obtains the organization of the Zuble application.
Q_INVOKABLE bool hasBundle(const QString &id) const
static bool realizeSettings(const QString &id, const SettingsBundle &settingsBundle)
Creates the default settings values for keys in the settings metadata that don't exist in the setting...
static bool mapConfigSettings(const char *configFilePath)
bool m_watching
True if this settings object is currently watching it's source file, false otherwise.
Q_INVOKABLE int beginReadArray(const QString &prefix)
Q_INVOKABLE bool addPath(const QString &file)
Q_INVOKABLE void beginGroup(const QString &prefix)
QMap< QString, SettingsBundle > bundleMap
void setRestoreDeletedFiles(bool restoreFiles)
Q_INVOKABLE bool setFileWatcher(bool enabled)
Enables or disables the file system watcher. This method must be called to enable this object to emit...
static QVariant m_tags
QVariantMap of ZSettings enumerations for use by Javascript programs.
static bool ensureFileExists(const QString &filePath)
static QSettings::Scope textToScope(const QString &text)
static void dumpMap(const QSettings::SettingsMap &map)
void setFocusText(const QString &text)
sets the XQuery focus url and calls setQuery on the encapsulated QXmlQuery object.
static bool writeFunc(QIODevice &device, const QSettings::SettingsMap &map)
Write function for Zuble's custom settings format.
static bool containsBundle(const QString &id)
QSettings * m_s
Pointer to the encapsulated QSettings object.
QStringList getChildKeys() const
static QVariant getAllBundles()
#define ZBL_SLOT_BEGIN_TRY
void validateStatus() const
void setQueryText(const QString &text)
Copies the specified text to the query text buffer and calls setQuery on the encapsulated QXmlQuery o...
#define ZBL_DEFINE_LOGGED_OBJECT(class_name)
Q_INVOKABLE void endArray()
void settingsModified(const QString &path)
Sent whenever the settings file is modified by this or other programs.
static QString scopeToText(QSettings::Scope scope)
#define zThreadErr
where does this show up?
void updateValue(const QString &settingPath, const QString &settingName, QVariant oldValue, QVariant newValue)
Sent whenever ZSettings values are modified by this or other ZSettings objects within this Qt/QML pro...
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
#define ZBL_SLOT_END_VOID(facility, code, error_message)
static bool readFunc(QIODevice &device, QSettings::SettingsMap &map)
Read function for Zuble's custom settings format.
Q_INVOKABLE QStringList files() const
static bundleMap m_bundles
Maps settings bundle ID values to the associated bundle.
Q_INVOKABLE QStringList evaluateToStringList()
Evaluates the current XQuery to a QStringList object and returns that object.
Wraps the QSettings class and implements a JSON-based backend for storing the settings data...
static QString resolvePath(const QString &path, bool includeUrlScheme=true)
Converts relative file paths into canonical file paths. Paths prefixed with prefix are mapped relativ...
static QString getBundleMetadata(const QString &id)
static QString rectifySettingsDataPath(const QString &dataPath)
Q_INVOKABLE void beginWriteArray(const QString &prefix, int size=-1)
QString getApplicationName() const
Q_INVOKABLE QString getCurrentThreadAddress()
Returns the human-readable memory address of the current thread.
Zuble's Qt Exception Object.
void validateWatcher() const
This inner class allows ZSettings to store settings bundles.
static ZSettings * getBundleSettings(const QString &id)
Returns the ZSettings object for the specified settings bundle ID. Caller takes ownership of the ZSet...
This class provides access to the QXMLQuery class from Javascript.
static QString getBundleMetapath(const QString &id)
Q_INVOKABLE void setArrayIndex(int i)
static void dumpGroup(const QString &groupKey, const QVariant &group)
static bool insertBundle(const QString &id, const QString &metaPath, QSettings::Scope scope=QSettings::UserScope, const QString organization=QString(), const QString application=QString())
Adds the specified settings bundle to the settings repository.
Q_INVOKABLE bool open(int scope=static_cast< int >(QSettings::UserScope), const QString &application=QString(), const QString &organization=QString())
Opens the settings object in the platform-specific configuration file location.
Q_INVOKABLE bool contains(const QString &key) const
#define ZBL_SLOT_END_RETURN(return_success, return_failed, facility, code, error_message)
void onFileChanged(const QString &path)
void fileChanged(const QString &path)