2014-01-29 19:31:34 +00:00
/*
2016-05-13 17:40:36 +00:00
Copyright 2006 - 2016 The QElectroTech Team
2014-01-29 19:31:34 +00:00
This file is part of QElectroTech .
QElectroTech is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 2 of the License , or
( at your option ) any later version .
QElectroTech is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with QElectroTech . If not , see < http : //www.gnu.org/licenses/>.
*/
2011-09-13 21:46:10 +00:00
# include "elementscollectioncache.h"
# include "elementscollection.h"
# include "elementscategory.h"
# include "elementdefinition.h"
2013-12-20 20:30:55 +00:00
# include "factory/elementfactory.h"
2014-07-21 20:44:32 +00:00
# include "element.h"
2015-10-03 15:41:59 +00:00
# include "qet.h"
2011-09-13 21:46:10 +00:00
2016-01-14 16:09:28 +00:00
# include <QImageWriter>
# include <QSqlQuery>
# include <QSqlError>
2011-09-13 21:46:10 +00:00
/**
Construct a cache for elements collections .
@ param database_path Path of the SQLite database to open .
@ param parent Parent QObject
*/
ElementsCollectionCache : : ElementsCollectionCache ( const QString & database_path , QObject * parent ) :
QObject ( parent ) ,
locale_ ( " en " ) ,
pixmap_storage_format_ ( " PNG " )
{
// initialize the cache SQLite database
static int cache_instances = 0 ;
QString connection_name = QString ( " ElementsCollectionCache-%1 " ) . arg ( cache_instances + + ) ;
2011-10-08 21:51:38 +00:00
cache_db_ = QSqlDatabase : : addDatabase ( " QSQLITE " , connection_name ) ;
2011-09-13 21:46:10 +00:00
cache_db_ . setDatabaseName ( database_path ) ;
2015-10-03 15:41:59 +00:00
if ( ! cache_db_ . open ( ) )
2011-09-13 21:46:10 +00:00
qDebug ( ) < < " Unable to open the SQLite database " < < database_path < < " as " < < connection_name < < " : " < < cache_db_ . lastError ( ) ;
2015-10-03 15:41:59 +00:00
else
{
2014-02-07 07:04:27 +00:00
cache_db_ . exec ( " PRAGMA temp_store = MEMORY " ) ;
2011-09-13 21:46:10 +00:00
cache_db_ . exec ( " PRAGMA journal_mode = MEMORY " ) ;
2014-02-07 07:04:27 +00:00
cache_db_ . exec ( " PRAGMA page_size = 4096 " ) ;
cache_db_ . exec ( " PRAGMA cache_size = 16384 " ) ;
cache_db_ . exec ( " PRAGMA locking_mode = EXCLUSIVE " ) ;
cache_db_ . exec ( " PRAGMA synchronous = OFF " ) ;
2015-10-03 15:41:59 +00:00
//TODO This code remove old table with mtime for create table with uuid, created at version 0,5
//see to remove this code at version 0,6 or 0,7 when all users will table with uuid.
QSqlQuery table_name ( cache_db_ ) ;
if ( table_name . exec ( " PRAGMA table_info(names) " ) )
{
if ( table_name . seek ( 2 ) )
{
QString str = table_name . value ( 1 ) . toString ( ) ;
table_name . finish ( ) ;
if ( str = = " mtime " )
{
QSqlQuery error ;
error = cache_db_ . exec ( " DROP TABLE names " ) ;
error = cache_db_ . exec ( " DROP TABLE pixmaps " ) ;
}
}
else
table_name . finish ( ) ;
}
//@TODO the tables could already exist, handle that case.
cache_db_ . exec ( " CREATE TABLE names "
" ( "
" path VARCHAR(512) NOT NULL, "
" locale VARCHAR(2) NOT NULL, "
" uuid VARCHAR(512) NOT NULL, "
" name VARCHAR(128), "
" PRIMARY KEY(path, locale) "
" ); " ) ;
cache_db_ . exec ( " CREATE TABLE pixmaps "
" ( "
" path VARCHAR(512) NOT NULL UNIQUE, "
" uuid VARCHAR(512) NOT NULL, "
" pixmap BLOB, PRIMARY KEY(path), "
" FOREIGN KEY(path) REFERENCES names (path) ON DELETE CASCADE); " ) ;
// prepare queries
2011-09-13 21:46:10 +00:00
select_name_ = new QSqlQuery ( cache_db_ ) ;
select_pixmap_ = new QSqlQuery ( cache_db_ ) ;
insert_name_ = new QSqlQuery ( cache_db_ ) ;
insert_pixmap_ = new QSqlQuery ( cache_db_ ) ;
2015-10-03 15:41:59 +00:00
select_name_ - > prepare ( " SELECT name FROM names WHERE path = :path AND locale = :locale AND uuid = :uuid " ) ;
select_pixmap_ - > prepare ( " SELECT pixmap FROM pixmaps WHERE path = :path AND uuid = :uuid " ) ;
insert_name_ - > prepare ( " REPLACE INTO names (path, locale, uuid, name) VALUES (:path, :locale, :uuid, :name) " ) ;
insert_pixmap_ - > prepare ( " REPLACE INTO pixmaps (path, uuid, pixmap) VALUES (:path, :uuid, :pixmap) " ) ;
2011-09-13 21:46:10 +00:00
}
}
/**
Destructor
*/
ElementsCollectionCache : : ~ ElementsCollectionCache ( ) {
2014-04-19 12:56:30 +00:00
delete select_name_ ;
delete select_pixmap_ ;
delete insert_name_ ;
delete insert_pixmap_ ;
2011-09-13 21:46:10 +00:00
cache_db_ . close ( ) ;
}
/**
Define the locale to be used when dealing with names .
@ param locale New locale to be used .
*/
void ElementsCollectionCache : : setLocale ( const QString & locale ) {
locale_ = locale ;
}
/**
@ return The locale to be used when dealing with names .
*/
QString ElementsCollectionCache : : locale ( ) const {
return ( locale_ ) ;
}
/**
Define the storage format for the pixmaps within the SQLite database . See
Qt ' s QPixmap documentation for more information .
@ param format The new pixmap storage format .
@ return True if the format change was accepted , false otherwise .
*/
bool ElementsCollectionCache : : setPixmapStorageFormat ( const QString & format ) {
2015-03-02 20:14:56 +00:00
if ( QImageWriter : : supportedImageFormats ( ) . contains ( format . toLatin1 ( ) ) ) {
2011-09-13 21:46:10 +00:00
pixmap_storage_format_ = format ;
return ( true ) ;
}
return ( false ) ;
}
/**
@ return the pixmap storage format . Default is " PNG "
@ see setPixmapStorageFormat ( )
*/
QString ElementsCollectionCache : : pixmapStorageFormat ( ) const {
return ( pixmap_storage_format_ ) ;
}
/**
Indicate the cache a new collection is about to be browsed . This is mainly
used to delimit database transactions .
@ param collection The elements collection about to be browsed .
*/
void ElementsCollectionCache : : beginCollection ( ElementsCollection * collection ) {
bool use_cache = cache_db_ . isOpen ( ) & & collection - > isCacheable ( ) ;
if ( use_cache ) {
bool transaction_started = cache_db_ . transaction ( ) ;
qDebug ( ) < < ( transaction_started ? " transaction began for " : " transaction not started for " ) < < collection - > protocol ( ) ;
}
}
/**
Indicate the cache the currently browsed collection end has been reached . This
is mainly used to delimit database transactions .
@ param collection The elements collection being browsed .
*/
void ElementsCollectionCache : : endCollection ( ElementsCollection * collection ) {
bool use_cache = cache_db_ . isOpen ( ) & & collection - > isCacheable ( ) ;
if ( use_cache ) {
bool transaction_commited = cache_db_ . commit ( ) ;
2011-10-08 21:51:38 +00:00
if ( transaction_commited ) {
qDebug ( ) < < " transaction commited for " < < collection - > protocol ( ) ;
} else {
qDebug ( ) < < " transaction not commited for " < < collection - > protocol ( ) < < " : " < < cache_db_ . lastError ( ) ;
}
2011-09-13 21:46:10 +00:00
}
}
/**
Retrieve the data for a given element , using the cache if available ,
filling it otherwise . Data are then available through pixmap ( ) and name ( )
methods .
@ param element The definition of an element .
@ see pixmap ( )
@ see name ( )
@ return True if the retrieval succeeded , false otherwise .
*/
2015-10-03 15:41:59 +00:00
bool ElementsCollectionCache : : fetchElement ( ElementDefinition * element )
{
// can we use the cache with this element?
2011-09-13 21:46:10 +00:00
bool use_cache = cache_db_ . isOpen ( ) & & element - > parentCollection ( ) - > isCacheable ( ) ;
2015-10-03 15:41:59 +00:00
// attempt to fetch the element name from the cache database
if ( ! use_cache )
{
2011-09-13 21:46:10 +00:00
return ( fetchData ( element - > location ( ) ) ) ;
2015-10-03 15:41:59 +00:00
}
else
{
2011-09-13 21:46:10 +00:00
QString element_path = element - > location ( ) . toString ( ) ;
2015-10-03 15:41:59 +00:00
bool got_name = fetchNameFromCache ( element_path , element - > uuid ( ) ) ;
bool got_pixmap = fetchPixmapFromCache ( element_path , element - > uuid ( ) ) ;
if ( got_name & & got_pixmap )
{
2011-09-13 21:46:10 +00:00
return ( true ) ;
}
2015-10-03 15:41:59 +00:00
if ( fetchData ( element - > location ( ) ) )
{
cacheName ( element_path , element - > uuid ( ) ) ;
cachePixmap ( element_path , element - > uuid ( ) ) ;
2011-09-13 21:46:10 +00:00
}
return ( true ) ;
}
}
2016-01-14 16:09:28 +00:00
/**
* @ brief ElementsCollectionCache : : fetchElement
* Retrieve the data for a given element , using the cache if available ,
2016-03-15 15:23:11 +00:00
* filling it otherwise . Data are then available through pixmap ( ) and name ( ) methods .
* @ param location The definition of an element .
2016-01-14 16:09:28 +00:00
* @ see pixmap ( )
* @ see name ( )
2016-03-15 15:23:11 +00:00
* @ return True if the retrieval succeeded , false otherwise .
2016-01-14 16:09:28 +00:00
*/
2016-03-15 15:23:11 +00:00
bool ElementsCollectionCache : : fetchElement ( ElementsLocation & location )
2015-12-08 16:52:10 +00:00
{
2016-03-15 15:23:11 +00:00
// can we use the cache with this element?
bool use_cache = cache_db_ . isOpen ( ) & & ! location . isProject ( ) ;
// attempt to fetch the element name from the cache database
if ( ! use_cache ) {
return ( fetchData ( location ) ) ;
}
else
2015-12-08 16:52:10 +00:00
{
2016-03-15 15:23:11 +00:00
QString element_path = location . toString ( ) ;
bool got_name = fetchNameFromCache ( element_path , location . uuid ( ) ) ;
bool got_pixmap = fetchPixmapFromCache ( element_path , location . uuid ( ) ) ;
if ( got_name & & got_pixmap ) {
return ( true ) ;
}
if ( fetchData ( location ) )
{
cacheName ( element_path , location . uuid ( ) ) ;
cachePixmap ( element_path , location . uuid ( ) ) ;
}
return ( true ) ;
2015-12-08 16:52:10 +00:00
}
}
2011-09-13 21:46:10 +00:00
/**
@ return The last name fetched through fetchElement ( ) .
*/
QString ElementsCollectionCache : : name ( ) const {
return ( current_name_ ) ;
}
/**
@ return The last pixmap fetched through fetchElement ( ) .
*/
QPixmap ElementsCollectionCache : : pixmap ( ) const {
return ( current_pixmap_ ) ;
}
/**
Retrieve the data by building the full CustomElement object matching the
given location , without using the cache . Data are then available through
pixmap ( ) and name ( ) methods .
@ param Location Location of a given Element .
@ return True if the retrieval succeeded , false otherwise .
*/
bool ElementsCollectionCache : : fetchData ( const ElementsLocation & location ) {
int state ;
2014-12-14 13:06:21 +00:00
Element * custom_elmt = ElementFactory : : Instance ( ) - > createElement ( location , 0 , & state ) ;
2011-09-13 21:46:10 +00:00
if ( state ) {
qDebug ( ) < < " ElementsCollectionCache::fetchData() : Le chargement du composant " < < qPrintable ( location . toString ( ) ) < < " a echoue avec le code d'erreur " < < state ;
} else {
current_name_ = custom_elmt - > name ( ) ;
current_pixmap_ = custom_elmt - > pixmap ( ) ;
}
delete custom_elmt ;
return ( ! state ) ;
}
/**
2015-10-03 15:41:59 +00:00
* @ brief ElementsCollectionCache : : fetchNameFromCache
* Retrieve the name for an element , given its path and uuid
* The value is then available through the name ( ) method .
* @ param path : Element path ( as obtained using ElementsLocation : : toString ( ) )
* @ param uuid : Element uuid
* @ return True if the retrieval succeeded , false otherwise .
*/
bool ElementsCollectionCache : : fetchNameFromCache ( const QString & path , const QUuid & uuid )
{
2011-09-13 21:46:10 +00:00
select_name_ - > bindValue ( " :path " , path ) ;
select_name_ - > bindValue ( " :locale " , locale_ ) ;
2015-10-03 15:41:59 +00:00
select_name_ - > bindValue ( " :uuid " , uuid . toString ( ) ) ;
if ( select_name_ - > exec ( ) )
{
if ( select_name_ - > first ( ) )
{
2011-09-13 21:46:10 +00:00
current_name_ = select_name_ - > value ( 0 ) . toString ( ) ;
2011-10-08 21:51:38 +00:00
select_name_ - > finish ( ) ;
2011-09-13 21:46:10 +00:00
return ( true ) ;
}
}
2015-10-03 15:41:59 +00:00
else
qDebug ( ) < < " select_name_->exec() failed " ;
2011-09-13 21:46:10 +00:00
return ( false ) ;
}
/**
2015-10-03 15:41:59 +00:00
* @ brief ElementsCollectionCache : : fetchPixmapFromCache
* Retrieve the pixmap for an element , given its path and uuid .
* It is then available through the pixmap ( ) method .
* @ param path : Element path ( as obtained using ElementsLocation : : toString ( ) )
* @ param uuid : Element uuid
* @ return True if the retrieval succeeded , false otherwise .
*/
bool ElementsCollectionCache : : fetchPixmapFromCache ( const QString & path , const QUuid & uuid )
{
2011-09-13 21:46:10 +00:00
select_pixmap_ - > bindValue ( " :path " , path ) ;
2015-10-03 15:41:59 +00:00
select_pixmap_ - > bindValue ( " :uuid " , uuid . toString ( ) ) ;
if ( select_pixmap_ - > exec ( ) )
{
if ( select_pixmap_ - > first ( ) )
{
2011-09-13 21:46:10 +00:00
QByteArray ba = select_pixmap_ - > value ( 0 ) . toByteArray ( ) ;
// avoid returning always the same pixmap (i.e. same cacheKey())
current_pixmap_ . detach ( ) ;
current_pixmap_ . loadFromData ( ba , qPrintable ( pixmap_storage_format_ ) ) ;
2011-10-08 21:51:38 +00:00
select_pixmap_ - > finish ( ) ;
2011-09-13 21:46:10 +00:00
}
return ( true ) ;
}
2015-10-03 15:41:59 +00:00
else
qDebug ( ) < < " select_pixmap_->exec() failed " ;
2011-09-13 21:46:10 +00:00
return ( false ) ;
}
/**
2015-10-03 15:41:59 +00:00
* @ brief ElementsCollectionCache : : cacheName
* Cache the current ( i . e . last retrieved ) name The cache entry will use the locale set via setLocale ( ) .
* @ param path : Element path ( as obtained using ElementsLocation : : toString ( ) )
* @ param uuid : Element uuid
* @ return True if the caching succeeded , false otherwise .
* @ see name ( )
*/
bool ElementsCollectionCache : : cacheName ( const QString & path , const QUuid & uuid )
{
2011-09-13 21:46:10 +00:00
insert_name_ - > bindValue ( " :path " , path ) ;
insert_name_ - > bindValue ( " :locale " , locale_ ) ;
2015-10-03 15:41:59 +00:00
insert_name_ - > bindValue ( " :uuid " , uuid . toString ( ) ) ;
2011-09-13 21:46:10 +00:00
insert_name_ - > bindValue ( " :name " , current_name_ ) ;
2015-10-03 15:41:59 +00:00
if ( ! insert_name_ - > exec ( ) )
{
2011-09-13 21:46:10 +00:00
qDebug ( ) < < cache_db_ . lastError ( ) ;
return ( false ) ;
}
return ( true ) ;
}
/**
2015-10-03 15:41:59 +00:00
* @ brief ElementsCollectionCache : : cachePixmap
* Cache the current ( i . e . last retrieved ) pixmap
* @ param path : Element path ( as obtained using ElementsLocation : : toString ( ) )
* @ param uuid : Element uuid
* @ return True if the caching succeeded , false otherwise .
* @ see pixmap ( )
*/
bool ElementsCollectionCache : : cachePixmap ( const QString & path , const QUuid & uuid )
{
2011-09-13 21:46:10 +00:00
QByteArray ba ;
QBuffer buffer ( & ba ) ;
buffer . open ( QIODevice : : WriteOnly ) ;
current_pixmap_ . save ( & buffer , qPrintable ( pixmap_storage_format_ ) ) ;
insert_pixmap_ - > bindValue ( " :path " , path ) ;
2015-10-03 15:41:59 +00:00
insert_pixmap_ - > bindValue ( " :uuid " , uuid . toString ( ) ) ;
2011-09-13 21:46:10 +00:00
insert_pixmap_ - > bindValue ( " :pixmap " , QVariant ( ba ) ) ;
2015-10-03 15:41:59 +00:00
if ( ! insert_pixmap_ - > exec ( ) )
{
2011-09-13 21:46:10 +00:00
qDebug ( ) < < cache_db_ . lastError ( ) ;
return ( false ) ;
}
return ( true ) ;
}