QElectroTech  0.70
elementscollectioncache.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2006-2019 The QElectroTech Team
3  This file is part of QElectroTech.
4 
5  QElectroTech is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 2 of the License, or
8  (at your option) any later version.
9 
10  QElectroTech is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
17 */
19 #include "factory/elementfactory.h"
20 #include "element.h"
21 #include "qet.h"
22 
23 #include <QImageWriter>
24 #include <QSqlQuery>
25 #include <QSqlError>
26 
32 ElementsCollectionCache::ElementsCollectionCache(const QString &database_path, QObject *parent) :
33  QObject(parent),
34  locale_("en"),
35  pixmap_storage_format_("PNG")
36 {
37  // initialize the cache SQLite database
38  static int cache_instances = 0;
39  QString connection_name = QString("ElementsCollectionCache-%1").arg(cache_instances++);
40  cache_db_ = QSqlDatabase::addDatabase("QSQLITE", connection_name);
41  cache_db_.setDatabaseName(database_path);
42 
43  if (!cache_db_.open())
44  qDebug() << "Unable to open the SQLite database " << database_path << " as " << connection_name << ": " << cache_db_.lastError();
45  else
46  {
47  cache_db_.exec("PRAGMA temp_store = MEMORY");
48  cache_db_.exec("PRAGMA journal_mode = MEMORY");
49  cache_db_.exec("PRAGMA page_size = 4096");
50  cache_db_.exec("PRAGMA cache_size = 16384");
51  cache_db_.exec("PRAGMA locking_mode = EXCLUSIVE");
52  cache_db_.exec("PRAGMA synchronous = OFF");
53 
54  //TODO This code remove old table with mtime for create table with uuid, created at version 0,5
55  //see to remove this code at version 0,6 or 0,7 when all users will table with uuid.
56  QSqlQuery table_name(cache_db_);
57  if (table_name.exec("PRAGMA table_info(names)"))
58  {
59  if (table_name.seek(2))
60  {
61  QString str = table_name.value(1).toString();
62  table_name.finish();
63  if (str == "mtime")
64  {
65  QSqlQuery error;
66  error = cache_db_.exec("DROP TABLE names");
67  error = cache_db_.exec("DROP TABLE pixmaps");
68  }
69  }
70  else
71  table_name.finish();
72  }
73 
74  //@TODO the tables could already exist, handle that case.
75  cache_db_.exec("CREATE TABLE names"
76  "("
77  "path VARCHAR(512) NOT NULL,"
78  "locale VARCHAR(2) NOT NULL,"
79  "uuid VARCHAR(512) NOT NULL,"
80  "name VARCHAR(128),"
81  "PRIMARY KEY(path, locale)"
82  ");");
83 
84  cache_db_.exec("CREATE TABLE pixmaps"
85  "("
86  "path VARCHAR(512) NOT NULL UNIQUE,"
87  "uuid VARCHAR(512) NOT NULL,"
88  "pixmap BLOB, PRIMARY KEY(path),"
89  "FOREIGN KEY(path) REFERENCES names (path) ON DELETE CASCADE);");
90 
91  // prepare queries
92  select_name_ = new QSqlQuery(cache_db_);
93  select_pixmap_ = new QSqlQuery(cache_db_);
94  insert_name_ = new QSqlQuery(cache_db_);
95  insert_pixmap_ = new QSqlQuery(cache_db_);
96  select_name_ -> prepare("SELECT name FROM names WHERE path = :path AND locale = :locale AND uuid = :uuid");
97  select_pixmap_ -> prepare("SELECT pixmap FROM pixmaps WHERE path = :path AND uuid = :uuid");
98  insert_name_ -> prepare("REPLACE INTO names (path, locale, uuid, name) VALUES (:path, :locale, :uuid, :name)");
99  insert_pixmap_ -> prepare("REPLACE INTO pixmaps (path, uuid, pixmap) VALUES (:path, :uuid, :pixmap)");
100  }
101 }
102 
107  delete select_name_;
108  delete select_pixmap_;
109  delete insert_name_;
110  delete insert_pixmap_;
111  cache_db_.close();
112 }
113 
118 void ElementsCollectionCache::setLocale(const QString &locale) {
119  locale_ = locale;
120 }
121 
126  return(locale_);
127 }
128 
136  if (QImageWriter::supportedImageFormats().contains(format.toLatin1())) {
137  pixmap_storage_format_= format;
138  return(true);
139  }
140  return(false);
141 }
142 
148  return(pixmap_storage_format_);
149 }
150 
161 {
162  // can we use the cache with this element?
163  bool use_cache = cache_db_.isOpen() && !location.isProject();
164 
165  // attempt to fetch the element name from the cache database
166  if (!use_cache) {
167  return(fetchData(location));
168  }
169  else
170  {
171  QString element_path = location.toString();
172  bool got_name = fetchNameFromCache(element_path, location.uuid());
173  bool got_pixmap = fetchPixmapFromCache(element_path, location.uuid());
174 
175  if (got_name && got_pixmap) {
176  return(true);
177  }
178 
179  if (fetchData(location))
180  {
181  cacheName(element_path, location.uuid());
182  cachePixmap(element_path, location.uuid());
183  }
184  return(true);
185  }
186 }
187 
192  return(current_name_);
193 }
194 
199  return(current_pixmap_);
200 }
201 
210  int state;
211  Element *custom_elmt = ElementFactory::Instance() -> createElement(location, nullptr, &state);
212  if (state) {
213  qDebug() << "ElementsCollectionCache::fetchData() : Le chargement du composant" << qPrintable(location.toString()) << "a echoue avec le code d'erreur" << state;
214  } else {
215  current_name_ = custom_elmt -> name();
216  current_pixmap_ = custom_elmt -> pixmap();
217  }
218  delete custom_elmt;
219  return(!state);
220 }
221 
230 bool ElementsCollectionCache::fetchNameFromCache(const QString &path, const QUuid &uuid)
231 {
232  select_name_ -> bindValue(":path", path);
233  select_name_ -> bindValue(":locale", locale_);
234  select_name_ -> bindValue(":uuid", uuid.toString());
235  if (select_name_ -> exec())
236  {
237  if (select_name_ -> first())
238  {
239  current_name_ = select_name_ -> value(0).toString();
240  select_name_ -> finish();
241  return(true);
242  }
243  }
244  else
245  qDebug() << "select_name_->exec() failed";
246 
247  return(false);
248 }
249 
258 bool ElementsCollectionCache::fetchPixmapFromCache(const QString &path, const QUuid &uuid)
259 {
260  select_pixmap_ -> bindValue(":path", path);
261  select_pixmap_ -> bindValue(":uuid", uuid.toString());
262  if (select_pixmap_ -> exec())
263  {
264  if (select_pixmap_ -> first())
265  {
266  QByteArray ba = select_pixmap_ -> value(0).toByteArray();
267  // avoid returning always the same pixmap (i.e. same cacheKey())
268  current_pixmap_.detach();
269  current_pixmap_.loadFromData(ba, qPrintable(pixmap_storage_format_));
270  select_pixmap_ -> finish();
271  }
272  return(true);
273  }
274  else
275  qDebug() << "select_pixmap_->exec() failed";
276 
277  return(false);
278 }
279 
288 bool ElementsCollectionCache::cacheName(const QString &path, const QUuid &uuid)
289 {
290  insert_name_ -> bindValue(":path", path);
291  insert_name_ -> bindValue(":locale", locale_);
292  insert_name_ -> bindValue(":uuid", uuid.toString());
293  insert_name_ -> bindValue(":name", current_name_);
294  if (!insert_name_ -> exec())
295  {
296  qDebug() << cache_db_.lastError();
297  return(false);
298  }
299  return(true);
300 }
301 
310 bool ElementsCollectionCache::cachePixmap(const QString &path, const QUuid &uuid)
311 {
312  QByteArray ba;
313  QBuffer buffer(&ba);
314  buffer.open(QIODevice::WriteOnly);
315  current_pixmap_.save(&buffer, qPrintable(pixmap_storage_format_));
316  insert_pixmap_ -> bindValue(":path", path);
317  insert_pixmap_ -> bindValue(":uuid", uuid.toString());
318  insert_pixmap_ -> bindValue(":pixmap", QVariant(ba));
319  if (!insert_pixmap_->exec())
320  {
321  qDebug() << cache_db_.lastError();
322  return(false);
323  }
324  return(true);
325 }
bool isProject() const
ElementsLocation::isProject.
QString current_name_
Last name fetched.
QString toString() const
bool cachePixmap(const QString &path, const QUuid &uuid=QUuid::createUuid())
ElementsCollectionCache::cachePixmap Cache the current (i.e. last retrieved) pixmap.
QPixmap current_pixmap_
Last pixmap fetched.
QSqlQuery * select_pixmap_
Prepared statement to fetch pixmaps from the cache.
QString pixmap_storage_format_
Storage format for cached pixmaps.
bool cacheName(const QString &path, const QUuid &uuid=QUuid::createUuid())
ElementsCollectionCache::cacheName Cache the current (i.e. last retrieved) name The cache entry will ...
ElementsCollectionCache(const QString &database_path, QObject *=nullptr)
QSqlQuery * insert_pixmap_
Prepared statement to insert pixmaps into the cache.
bool fetchNameFromCache(const QString &path, const QUuid &uuid)
ElementsCollectionCache::fetchNameFromCache Retrieve the name for an element, given its path and uuid...
QUuid uuid() const
ElementsLocation::uuid.
bool fetchData(const ElementsLocation &)
bool fetchPixmapFromCache(const QString &path, const QUuid &uuid)
ElementsCollectionCache::fetchPixmapFromCache Retrieve the pixmap for an element, given its path and ...
bool fetchElement(ElementsLocation &location)
ElementsCollectionCache::fetchElement Retrieve the data for a given element, using the cache if avail...
QSqlDatabase cache_db_
Object providing access to the SQLite database this cache relies on.
QSqlQuery * insert_name_
Prepared statement to insert names into the cache.
QSqlQuery * select_name_
Prepared statement to fetch names from the cache.
bool setPixmapStorageFormat(const QString &)
static ElementFactory * Instance()
QString locale_
Locale to be used when dealing with names.