]> vaikene.ee Git - evaf/commitdiff
Added the iConfig interface and default implementation in the common library.
authorEnar Väikene <enar@vaikene.net>
Thu, 6 Oct 2011 07:46:53 +0000 (10:46 +0300)
committerEnar Väikene <enar@vaikene.net>
Thu, 6 Oct 2011 07:46:53 +0000 (10:46 +0300)
src/libs/Common/CMakeLists.txt
src/libs/Common/config.cpp [new file with mode: 0644]
src/libs/Common/config.h [new file with mode: 0644]
src/libs/Common/globals.cpp
src/libs/Common/iConfig [new file with mode: 0644]
src/libs/Common/iconfig.h [new file with mode: 0644]

index bb87e9f8cfe9b827f9a39a36d083b4d6bbafbc49..f308c06927e54e900219f8eba7797c8066cebcbd 100644 (file)
@@ -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 (file)
index 0000000..610dd74
--- /dev/null
@@ -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 <QtCore>
+
+//-------------------------------------------------------------------
+
+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>("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<QString, IniFile *>::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<QString, IniFile *>::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<QString, IniFile *>::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 (file)
index 0000000..3c19aaf
--- /dev/null
@@ -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 <QObject>
+#include <QString>
+#include <QVariant>
+#include <QHash>
+#include <QQueue>
+
+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<QString, IniFile *> mIniFiles;
+
+    /// Commit queue
+    QQueue<NameValuePair> 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
index 63d4bce2bd6c3a988c333459d3e42220fb56957d..b981fff43ceee7f65537fe162d19c58e11475aa4 100644 (file)
@@ -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::Internal::Config *>(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 (file)
index 0000000..19308fe
--- /dev/null
@@ -0,0 +1 @@
+#include "iconfig.h"
diff --git a/src/libs/Common/iconfig.h b/src/libs/Common/iconfig.h
new file mode 100644 (file)
index 0000000..3157eb6
--- /dev/null
@@ -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 <QObject>
+#include <QString>
+#include <QVariant>
+
+namespace eVaf {
+namespace Common {
+
+/**
+ * eVaf configuration interface.
+ * @code#include <iConfig>@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:
+ * [\<backend:\>][\<file\>]/\<section\>/\<key\>
+ *
+ * Where:
+ * @li \<backend:\> 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 \<file\> 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 \<section\> is the name of the section. Section names can contain extra '/' characters to build a structured
+ * tree of configuration sections.
+ * @li \<key\> 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