/** * @file PswGen/Storage/module.cpp * @brief Implementation of the iStorage interface * @author Enar Vaikene * * Copyright (c) 2011-2019 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 #include #include #include #include #include #include VER_EXPORT_VERSION_INFO() 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() { // Open the database QSqlDatabase db; if (!QSqlDatabase::contains(DbConnectionName)) { // No database connection yet db = QSqlDatabase::addDatabase("QSQLITE", DbConnectionName); EVAF_INFO("Added database %s", DbConnectionName); db.setDatabaseName(Common::iApp::instance()->dataRootDir() + DbName); if (!db.open()) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to open database : %s", qPrintable(err.text())); return false; } } else { // Database connection already exists db = QSqlDatabase::database(DbConnectionName); } // Create tables if necessary if (!createTables(db)) return false; // Load data if (!loadData(db)) return false; /// Register our interface Common::iRegistry::instance()->registerInterface("iStorage", this); EVAF_INFO("%s initialized", qPrintable(objectName())); return true; } void StorageImpl::done() { mData.clear(); if (QSqlDatabase::contains(DbConnectionName)) { QSqlDatabase::removeDatabase(DbConnectionName); EVAF_INFO("Removed database %s", DbConnectionName); } EVAF_INFO("%s finalized", qPrintable(objectName())); } bool StorageImpl::save(QString const & name, QExplicitlySharedDataPointer data) { EVAF_TEST_X(data, "Data cannot be null") EVAF_TEST_X(!name.isEmpty(), "Name cannot be empty") if (!QSqlDatabase::contains(DbConnectionName)) { EVAF_ERROR("Failed to get DB connection"); return false; } QSqlDatabase db = QSqlDatabase::database(DbConnectionName); // Is it an update or a new data record? if (mData.constFind(name) != mData.constEnd()) { // This is an update if (data->modified()) { QSqlQuery q(db); if (!q.exec(QString("UPDATE data SET suffix = \'%1\', length = \'%2\', flags = \'%3\' WHERE name = \'%4\';") .arg(data->suffix()).arg(data->length()).arg(data->flags()).arg(name))) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to update \'%s\' : %s", qPrintable(name), qPrintable(err.text())); return false; } } } else { // Store to the database QSqlQuery q(db); if (!q.exec(QString("INSERT INTO data (name, suffix, length, flags) VALUES (\'%1\', \'%2\', %3, %4);") .arg(name).arg(data->suffix()).arg(data->length()) .arg(int(data->flags())))) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to insert \'%s\' : %s", qPrintable(name), qPrintable(err.text())); return false; } // Store also into the local hash beginResetModel(); mData.insert(name, data); endResetModel(); } data->reset(); return true; } QExplicitlySharedDataPointer StorageImpl::query(QString const & name) const { QMap >::const_iterator it = mData.constFind(name); if (it != mData.constEnd()) return it.value(); else return QExplicitlySharedDataPointer(); } 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(QSqlDatabase & db) { QSqlQuery q(db); if (!q.exec("SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'data\';")) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); return false; } if (q.isActive() && q.isSelect() && q.first()) { // Check if the table needs to be upgraded return upgradeTables(db); } // Create the 'data' table if (!q.exec("CREATE TABLE data (name text primary key not null, suffix text, length integer, flags integer);")) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to create table \'data\' : %s", qPrintable(err.text())); return false; } return true; } bool StorageImpl::upgradeTables(QSqlDatabase & db) { QSqlQuery q(db); // Check if the 'suffix' column exists if (q.exec("SELECT suffix from data;")) { return true; } // Add the 'suffix' columnt if (!q.exec("ALTER TABLE data ADD COLUMN suffix TEXT;")) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to upgrade table \'data\' : %s", qPrintable(err.text())); return false; } return true; } bool StorageImpl::loadData(QSqlDatabase & db) { QSqlQuery q(db); if (!q.exec("SELECT name, suffix, length, flags FROM data;")) { QSqlError err = db.lastError(); EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); return false; } beginResetModel(); while (q.next()) { QString name = q.value(0).toString(); QExplicitlySharedDataPointer data( new Storage::Data(name, q.value(1).toString(), q.value(2).toInt(), uint(q.value(3).toInt()))); mData.insert(name, data); } endResetModel(); return true; }