From ec272061c2af46e4cd8730c18682e18ac5b369ff Mon Sep 17 00:00:00 2001 From: =?utf8?q?Enar=20V=C3=A4ikene?= Date: Thu, 6 Oct 2011 10:46:53 +0300 Subject: [PATCH] Added the iConfig interface and default implementation in the common library. --- src/libs/Common/CMakeLists.txt | 3 + src/libs/Common/config.cpp | 205 +++++++++++++++++++++++++++++++++ src/libs/Common/config.h | 119 +++++++++++++++++++ src/libs/Common/globals.cpp | 7 ++ src/libs/Common/iConfig | 1 + src/libs/Common/iconfig.h | 127 ++++++++++++++++++++ 6 files changed, 462 insertions(+) create mode 100644 src/libs/Common/config.cpp create mode 100644 src/libs/Common/config.h create mode 100644 src/libs/Common/iConfig create mode 100644 src/libs/Common/iconfig.h diff --git a/src/libs/Common/CMakeLists.txt b/src/libs/Common/CMakeLists.txt index bb87e9f..f308c06 100644 --- a/src/libs/Common/CMakeLists.txt +++ b/src/libs/Common/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRCS registry.cpp util.cpp inifile.cpp + config.cpp ) # Header files for the meta-object compiler @@ -32,10 +33,12 @@ set(MOC_HDRS ieventqueue.h ilogger.h iregistry.h + iconfig.h app.h eventqueue.h logger.h registry.h + config.h ) # Version info resource file for Windows builds diff --git a/src/libs/Common/config.cpp b/src/libs/Common/config.cpp new file mode 100644 index 0000000..610dd74 --- /dev/null +++ b/src/libs/Common/config.cpp @@ -0,0 +1,205 @@ +/** + * @file Common/config.cpp + * @brief eVaf configuration interface implementation + * @author Enar Vaikene + * + * Copyright (c) 2011 Enar Vaikene + * + * This file is part of the eVaf C++ cross-platform application development framework. + * + * This file can be used under the terms of the GNU General Public License + * version 3.0 as published by the Free Software Foundation and appearing in + * the file LICENSE included in the packaging of this file. Please review the + * the following information to ensure the GNU General Public License version + * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. + * + * Alternatively, this file may be used in accordance with the Commercial License + * Agreement provided with the Software. + */ + +#include "config.h" +#include "inifile.h" +#include "iregistry.h" +#include "ilogger.h" +#include "iapp.h" +#include "version.h" + +#include + +//------------------------------------------------------------------- + +using namespace eVaf::Common; + +iConfig * iConfig::instance() +{ + static Internal::Config singleton; + return singleton.interface(); +} + + +//------------------------------------------------------------------- + +using namespace eVaf::Common::Internal; + +Config::Config() + : iConfig() +{ + setObjectName(QString("%1.iConfig").arg(VER_MODULE_NAME_STR)); + + // Register the iConfig interface + iRegistry::instance()->registerInterface("iConfig", this); +} + +Config::~Config() +{ + done(); +} + +iConfig * Config::interface() const +{ + return evafQueryInterface("iConfig"); +} + +bool Config::init() +{ + // Finalize first in case this is not the first time init() is called + done(); + + return true; +} + +void Config::done() +{ + // Commit any queued parameters + commitValues(); + + // Clear the list of opened INI files + QHash::iterator it = mIniFiles.begin(); + while (it != mIniFiles.end()) { + delete it.value(); + it = mIniFiles.erase(it); + } +} + +QVariant Config::getValue(QString const & paramName, QVariant const & defaultValue) const +{ + // Get the optional file part + int idx = paramName.indexOf('/'); + if (idx < 0) { + EVAF_ERROR("Invalid parameter name '%s'", qPrintable(paramName)); + return defaultValue; + } + QString file = paramName.left(idx); + QString name = paramName.mid(idx + 1); + + // Ignore the optional backend identifier in the file name + idx = file.indexOf(':'); + if (idx >= 0) + file.remove(0, idx + 1); + + // If the file name is empty or '*', use the application's name + if (file.isEmpty() || file == "*") + file = iApp::instance()->name(); + + IniFile * ini = 0; + + // Is this INI file already opened? + QHash::const_iterator it = mIniFiles.constFind(file); + + // The file is opened and we can reuse it + if (it != mIniFiles.constEnd()) { + ini = *it; + } + + // The file is not opened + else { + ini = new IniFile(QString("%1/%2.ini").arg(iApp::instance()->etcDir()).arg(file)); + if (!ini->isValid()) { + EVAF_ERROR("Failed to open '%s' : %s", qPrintable(name), qPrintable(ini->errorString())); + delete ini; + return defaultValue; + } + mIniFiles.insert(file, ini); + } + + // Read the value + return ini->getValue(name, defaultValue); +} + +bool Config::setValue(QString const & paramName, QVariant const & value, bool commit) +{ + if (!commit) { + // Queue the write operation + mCommitQueue.enqueue(NameValuePair(paramName, value)); + } + else { + // Commit any queued parameters + if (!commitValues()) + return false; + + // Write the parameter + if (!writeValue(paramName, value)) + return false; + } + + return true; +} + +bool Config::commitValues() +{ + while (!mCommitQueue.isEmpty()) { + if (!writeValue(mCommitQueue.dequeue())) { + mCommitQueue.clear(); + return false; + } + } + return true; +} + +bool Config::writeValue(QString const & paramName, QVariant const & value) +{ + // Get the optional file part + int idx = paramName.indexOf('/'); + if (idx < 0) { + EVAF_ERROR("Invalid parameter name '%s'", qPrintable(paramName)); + return false; + } + QString file = paramName.left(idx); + QString name = paramName.mid(idx + 1); + + // Ignore the optional backend identifier in the file name + idx = file.indexOf(':'); + if (idx >= 0) + file.remove(0, idx + 1); + + // If the file name is empty or '*', use the application's name + if (file.isEmpty() || file == "*") + file = iApp::instance()->name(); + + IniFile * ini = 0; + + // Is this INI file already opened? + QHash::const_iterator it = mIniFiles.constFind(file); + + // The file is opened and we can reuse it + if (it != mIniFiles.constEnd()) { + ini = *it; + } + + // The file is not opened + else { + ini = new IniFile(QString("%1/%2.ini").arg(iApp::instance()->etcDir()).arg(file)); + if (!ini->isValid()) { + EVAF_ERROR("Failed to open '%s' : %s", qPrintable(name), qPrintable(ini->errorString())); + delete ini; + return false; + } + mIniFiles.insert(file, ini); + } + + // Write the value + if (!ini->setValue(name, value)) + return false; + + return true; +} diff --git a/src/libs/Common/config.h b/src/libs/Common/config.h new file mode 100644 index 0000000..3c19aaf --- /dev/null +++ b/src/libs/Common/config.h @@ -0,0 +1,119 @@ +/** + * @file Common/config.h + * @brief eVaf configuration interface implementation + * @author Enar Vaikene + * + * Copyright (c) 2011 Enar Vaikene + * + * This file is part of the eVaf C++ cross-platform application development framework. + * + * This file can be used under the terms of the GNU General Public License + * version 3.0 as published by the Free Software Foundation and appearing in + * the file LICENSE included in the packaging of this file. Please review the + * the following information to ensure the GNU General Public License version + * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. + * + * Alternatively, this file may be used in accordance with the Commercial License + * Agreement provided with the Software. + */ + +#ifndef __COMMON_CONFIG_H +# define __COMMON_CONFIG_H + +#include "iconfig.h" + +#include +#include +#include +#include +#include + +namespace eVaf { +namespace Common { + class IniFile; +namespace Internal { + +/** + * Structure for queued write operations + */ +struct NameValuePair +{ + NameValuePair(QString const & n, QVariant const & v) + : name(n) + , value(v) + {} + + QString name; + QVariant value; +}; + +/** + * Default implementation of the iConfig interface. + * + * This class implements the iConfig interface using INI files in the eVaf::Common::iApp::instance()->etcDir() directory. + */ +class Config : public iConfig +{ + Q_OBJECT + +public: + + Config(); + + virtual ~Config(); + + /** + * Returns the current implementation of the iConfig interface + */ + iConfig * interface() const; + + /** + * Initializes the iConfig interface implementation. + * @return True if succeeded; false if not + */ + bool init(); + + /** + * Finalizes the iConfig interface implementation. + */ + void done(); + + /* + iConfig interface + */ + + virtual QVariant getValue(QString const & paramName, QVariant const & defaultValue) const; + + virtual bool setValue(QString const & paramName, QVariant const & value, bool commit); + + +private: // Members + + /// List of already opened INI files + mutable QHash mIniFiles; + + /// Commit queue + QQueue mCommitQueue; + + +private: // Methods + + /// Commits queued parameters + bool commitValues(); + + /// Writes a parameter value to the INI file + bool writeValue(QString const & paramName, QVariant const & value); + + /// Writes a name/value pair to the INI file + inline bool writeValue(NameValuePair const & param) + { + return writeValue(param.name, param.value); + } + +}; + +} // namespace eVaf::Common::Internal +} // namespace eVaf::Common +} // namespace eVaf + +#endif // config.h diff --git a/src/libs/Common/globals.cpp b/src/libs/Common/globals.cpp index 63d4bce..b981fff 100644 --- a/src/libs/Common/globals.cpp +++ b/src/libs/Common/globals.cpp @@ -19,6 +19,7 @@ #include "globals.h" #include "app.h" +#include "config.h" #include "logger.h" #include "version.h" #include "ilogger.h" @@ -51,6 +52,12 @@ bool eVaf::Common::init() if (!logger->init()) return false; } + eVaf::Common::Internal::Config * config = + qobject_cast(eVaf::Common::iConfig::instance()); + if (config) { + if (!config->init()) + return false; + } EVAF_INFO("%s-Globals initialized", VER_MODULE_NAME_STR); diff --git a/src/libs/Common/iConfig b/src/libs/Common/iConfig new file mode 100644 index 0000000..19308fe --- /dev/null +++ b/src/libs/Common/iConfig @@ -0,0 +1 @@ +#include "iconfig.h" diff --git a/src/libs/Common/iconfig.h b/src/libs/Common/iconfig.h new file mode 100644 index 0000000..3157eb6 --- /dev/null +++ b/src/libs/Common/iconfig.h @@ -0,0 +1,127 @@ +/** + * @file Common/iconfig.h + * @brief eVaf configuration interface + * @author Enar Vaikene + * + * Copyright (c) 2011 Enar Vaikene + * + * This file is part of the eVaf C++ cross-platform application development framework. + * + * This file can be used under the terms of the GNU General Public License + * version 3.0 as published by the Free Software Foundation and appearing in + * the file LICENSE included in the packaging of this file. Please review the + * the following information to ensure the GNU General Public License version + * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. + * + * Alternatively, this file may be used in accordance with the Commercial License + * Agreement provided with the Software. + */ + +#ifndef __COMMON_ICONFIG_H +# define __COMMON_ICONFIG_H + +#include "libcommon.h" + +#include +#include +#include + +namespace eVaf { +namespace Common { + +/** + * eVaf configuration interface. + * @code#include @endcode + * + * The iConfig interface provides access to configuration parameters. Methods in this interface can be + * used to read and write configuration parameters. + * + * The default implementation of the iConfig interface uses INI files in the eVaf::Common::iApp::instance()->etcDir() + * directory. Additional configuration backends can be provided by re-implementing the interface. + * + * Configuration parameters are identified by a name in the following format: + * [\][\]/\/\ + * + * Where: + * @li \ is an optional identifier for the configuration backend. This part is ignored by the default + * iConfig interface implementation and is reserved for custom modules. For example, a custom module could look + * for the 'db:' backend identifier and store parameters in a database. Any other requests would be forwarded to + * the default implementation. + * @li \ is an optional configuration file name. The default iConfig interface implementation uses the file + * name part to locate the INI file in the eVaf::Common::iApp::instance()->etcDir() directory. If the file name part + * is '*' or empty, uses eVaf::Common::iApp::instance()->name() for the file name. + * @li \ is the name of the section. Section names can contain extra '/' characters to build a structured + * tree of configuration sections. + * @li \ is the name of the key. + * + * For example: + * @li 'db:local/extensions/help/module' - backend 'db:', file or database name 'local', section name 'extensions/help' and + * the key 'module'; + * @li '/extensions/help/module' - default backend, default file name, section name 'extensions/help' and the key 'module'. + */ +class COMMON_EXPORT iConfig : public QObject +{ + Q_OBJECT + +public: + + /// Interface constructor + iConfig() : QObject() {} + + /// Empty virtual destructor + virtual ~iConfig() {} + + /** + * Returns the current iConfig interface instance + * @return The iConfig interface + * + * This method returns the current iConfig interface instance as shown in the following example: + * @code + * QString moduleName = eVaf::Common::iConfig::instance()->getValue("/extensions/help/module", "libHelp.so").toString(); + * @endcode + */ + static iConfig * instance(); + + /** + * Reads a configuration parameter value + * @param paramName Name of the parameter + * @param defaultValue Default value + * @return The parameter value or the default value if the parameter cannot be found + * + * The getValue() method returns a configuration parameter value identified by the parameter name. If the parameter + * cannot be read or is not found, returns the default value. + * + * The default value is used to determine the type of the value. For example, if the default value is an integer, then + * the returned value is also an integer. The default iConfig interface implementation validates parameter values and + * makes sure that the parameter value is of the proper type. If the value cannot be converted to the proper type, + * returns the default value. Other implementations of the iConfig interface are encouraged to do the same. + */ + virtual QVariant getValue(QString const & paramName, QVariant const & defaultValue) const = 0; + + /** + * Writes a configuration parameter value + * @param paramName Name of the parameter + * @param value The parameter value + * @param commit If true, then commits new parameter values + * @return True if succeeded; false if not + * + * The setValue() method writes new configuration parameter values identified by the parameter name. + * + * The commit argument can be used to improve the performance of writing several parameter values at once. Use + * commit = false for all except the last parameter's write operation. + * @code + * iConfig * conf = eVaf::Common::iConfig::instance(); + * conf->setValue("/main/param1", 1, false); + * conf->setValue("/main/param2", 2, false); + * conf->setValue("/main/param3", 3); // Last parameter in the block + * @endcode + */ + virtual bool setValue(QString const & paramName, QVariant const & value, bool commit = true) = 0; + +}; + + +} // namespace eVaf::Common +} // namespace eVaf + +#endif // iconfig.h -- 2.47.0