+/**
+ * @file PswGen/Storage/module.cpp
+ * @brief Implementation of the iStorage 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.
+ */
+
+#include "module.h"
+#include "version.h"
+
+#include <Common/Globals>
+#include <Common/iLogger>
+#include <Common/iRegistry>
+#include <Common/iEnv>
+
+#include <QtCore>
+#include <QtSql/QtSql>
+
+VER_EXPORT_VERSION_INFO()
+Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::PswGen::Storage::Module)
+
+using namespace eVaf;
+using namespace eVaf::PswGen;
+using namespace eVaf::PswGen::Storage;
+
+//-------------------------------------------------------------------
+
+Module::Module()
+ : Plugins::iPlugin()
+ , mReady(false)
+{
+ setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__));
+
+ mStorage = new Internal::StorageImpl;
+
+ EVAF_INFO("%s created", qPrintable(objectName()));
+}
+
+Module::~Module()
+{
+ delete mStorage;
+
+ EVAF_INFO("%s destroyed", qPrintable(objectName()));
+}
+
+bool Module::init(QString const & args)
+{
+ Q_UNUSED(args);
+
+ if (!mStorage->init())
+ return false;
+
+ mReady = true;
+
+ EVAF_INFO("%s initialized", qPrintable(objectName()));
+
+ return true;
+}
+
+void Module::done()
+{
+ mReady = false;
+
+ mStorage->done();
+
+ EVAF_INFO("%s finalized", qPrintable(objectName()));
+}
+
+
+//-------------------------------------------------------------------
+
+using namespace eVaf::PswGen::Storage::Internal;
+
+char const * const StorageImpl::DbConnectionName = "PswGenDB";
+
+char const * const StorageImpl::DbName = "PswGen.sqlite";
+
+StorageImpl::StorageImpl()
+ : QAbstractListModel()
+{
+ setObjectName(QString("%1.iGenerator").arg(VER_MODULE_NAME_STR));
+
+ EVAF_INFO("%s created", qPrintable(objectName()));
+}
+
+StorageImpl::~StorageImpl()
+{
+ EVAF_INFO("%s destroyed", qPrintable(objectName()));
+}
+
+bool StorageImpl::init()
+{
+ EVAF_INFO("%s initialized", qPrintable(objectName()));
+
+ // Open the database
+ if (!QSqlDatabase::contains(DbConnectionName)) {
+ // No database connection yet
+ mDb = QSqlDatabase::addDatabase("QSQLITE", DbConnectionName);
+ mDb.setDatabaseName(Common::iEnv::instance()->dataRootDir() + DbName);
+ if (!mDb.open()) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to open database : %s", qPrintable(err.text()));
+ return false;
+ }
+ }
+ else {
+ // Database connection already exists
+ mDb = QSqlDatabase::database(DbConnectionName);
+ }
+
+ // Create tables if necessary
+ if (!createTables())
+ return false;
+
+ // Load data
+ if (!loadData())
+ return false;
+
+ /// Register our interface
+ Common::iRegistry::instance()->registerInterface("iStorage", this);
+
+ return true;
+}
+
+void StorageImpl::done()
+{
+ mData.clear();
+ EVAF_INFO("%s finalized", qPrintable(objectName()));
+}
+
+bool StorageImpl::save(QString const & name, QExplicitlySharedDataPointer<Storage::Data> data)
+{
+ EVAF_TEST_X(data, "Data cannot be null");
+ EVAF_TEST_X(!name.isEmpty(), "Name cannot be empty");
+
+ // Is it an update or a new data record?
+ if (mData.constFind(name) != mData.constEnd()) {
+ // This is an update
+ if (data->modified()) {
+ QSqlQuery q(mDb);
+ if (!q.exec(QString("UPDATE data SET length = \'%1\', flags = \'%2\' WHERE name = \'%3\';").arg(data->length()).arg(data->flags()).arg(name))) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to update \'%s\' : %s", qPrintable(name), qPrintable(err.text()));
+ return false;
+ }
+ }
+ }
+ else {
+ // Store to the database
+ QSqlQuery q(mDb);
+ if (!q.exec(QString("INSERT INTO data (name, length, flags) VALUES ('\%1\', %2, %3);").arg(name).arg(data->length()).arg(int(data->flags())))) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to insert \'%s\' : %s", qPrintable(name), qPrintable(err.text()));
+ return false;
+ }
+
+ // Store also into the local hash
+ mData.insert(name, data);
+
+ // Reset the model
+ reset();
+ }
+
+ data->reset();
+
+ return true;
+}
+
+QExplicitlySharedDataPointer<Storage::Data> StorageImpl::query(QString const & name) const
+{
+ QMap<QString, QExplicitlySharedDataPointer<Storage::Data> >::const_iterator it = mData.constFind(name);
+ if (it != mData.constEnd())
+ return it.value();
+ else
+ return QExplicitlySharedDataPointer<Storage::Data>();
+}
+
+QVariant StorageImpl::data(QModelIndex const & index, int role) const
+{
+ if (!index.isValid() || index.row() < 0 || index.row() >= mData.size() || index.column() != 0)
+ return QVariant();
+
+ if (role == Qt::EditRole || role == Qt::DisplayRole)
+ return mData.keys().at(index.row());
+
+ return QVariant();
+}
+
+bool StorageImpl::createTables()
+{
+ QSqlQuery q(mDb);
+ if (!q.exec("SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'data\';")) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to query database : %s", qPrintable(err.text()));
+ return false;
+ }
+
+ if (q.isActive() && q.isSelect() && q.first())
+ return true; // We already have a table called 'data'
+
+ // Create the 'data' table
+ if (!q.exec("CREATE TABLE data (name text primary key not null, length integer, flags integer);")) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to create table \'data\' : %s", qPrintable(err.text()));
+ return false;
+ }
+
+ return true;
+}
+
+bool StorageImpl::loadData()
+{
+ QSqlQuery q(mDb);
+ if (!q.exec("SELECT name, length, flags FROM data;")) {
+ QSqlError err = mDb.lastError();
+ EVAF_ERROR("Failed to query database : %s", qPrintable(err.text()));
+ return false;
+ }
+
+ while (q.next()) {
+ QString name = q.value(0).toString();
+ QExplicitlySharedDataPointer<Storage::Data> data(new Storage::Data(q.value(1).toInt(), uint(q.value(2).toInt())));
+ mData.insert(name, data);
+ }
+
+ reset();
+
+ return true;
+}