ArnLib  4.0.x
Active Registry Network
ArnPersist.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // GNU Lesser General Public License Usage
11 // This file may be used under the terms of the GNU Lesser General Public
12 // License version 2.1 as published by the Free Software Foundation and
13 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
14 // file. In addition, as a special exception, you may use the rights described
15 // in the Nokia Qt LGPL Exception version 1.1, included in the file
16 // LGPL_EXCEPTION.txt in this package.
17 //
18 // GNU General Public License Usage
19 // Alternatively, this file may be used under the terms of the GNU General Public
20 // License version 3.0 as published by the Free Software Foundation and appearing
21 // in the file LICENSE_GPL.txt included in the packaging of this file.
22 //
23 // Other Usage
24 // Alternatively, this file may be used in accordance with the terms and conditions
25 // contained in a signed written agreement between you and Michael Wiklund.
26 //
27 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
28 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
29 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
30 //
31 
32 #include "ArnInc/ArnPersist.hpp"
33 #include "private/ArnPersist_p.hpp"
35 #include "ArnInc/ArnDepend.hpp"
36 #include "ArnInc/XStringMap.hpp"
37 #include "ArnInc/ArnCompat.hpp"
38 #include <QtSql/QSqlDatabase>
39 #include <QtSql/QSqlQuery>
40 #include <QtSql/QSqlError>
41 #include <QDir>
42 #include <QFile>
43 #include <QFileInfo>
44 #include <QDateTime>
45 #include <QStringList>
46 #include <QDebug>
47 
48 #include <QMetaObject>
49 #include <QMetaMethod>
50 
51 using Arn::XStringMap;
52 
53 
54 ArnItemPersist::ArnItemPersist( ArnPersist* arnPersist) :
55  ArnItem( arnPersist)
56 {
57  setUncrossed(); // Only normal value will be used for Load/Save
58  setBlockEcho( true); // Don't save own updates
59  setIgnoreSameValue( true);
60  _arnPersist = arnPersist;
61  _storeId = 0;
62  _isMandatory = false;
63  _storeType = StoreType::DataBase;
64 }
65 
66 
67 ArnItemPersist::~ArnItemPersist()
68 {
69 }
70 
71 
72 void ArnItemPersist::arnImport( const QByteArray& data, int ignoreSame)
73 {
74  ArnLinkHandle handleData;
75  handleData.flags().set( ArnLinkHandle::Flags::FromPersist);
76  ArnBasicItem::arnImport( data, ignoreSame, handleData);
77 }
78 
79 
80 void ArnItemPersist::setValue( const QByteArray& value, int ignoreSame)
81 {
82  ArnLinkHandle handleData;
83  handleData.flags().set( ArnLinkHandle::Flags::FromPersist);
84  ArnBasicItem::setValue( value, ignoreSame, handleData);
85 }
86 
87 
88 ArnVcs::ArnVcs( QObject* parent) :
89  QObject( parent)
90 {
91 }
92 
93 
94 ArnVcs::~ArnVcs()
95 {
96 }
97 
98 
99 void ArnVcs::log( int numLog)
100 {
101  Q_UNUSED(numLog)
102 }
103 
104 
105 void ArnVcs::files( QString ref)
106 {
107  Q_UNUSED(ref)
108 }
109 
110 
111 void ArnVcs::commit( QString commitMsg, QStringList files, QString name, QString email)
112 {
113  Q_UNUSED(commitMsg)
114  Q_UNUSED(files)
115  Q_UNUSED(name)
116  Q_UNUSED(email)
117 }
118 
119 
120 void ArnVcs::tag( QString name, QString ref)
121 {
122  Q_UNUSED(name)
123  Q_UNUSED(ref)
124 }
125 
126 
127 void ArnVcs::diff( QString ref, QStringList files)
128 {
129  Q_UNUSED(ref)
130  Q_UNUSED(files)
131 }
132 
133 
134 void ArnVcs::logRecord( QString ref)
135 {
136  Q_UNUSED(ref)
137 }
138 
139 
140 void ArnVcs::tags()
141 {
142 }
143 
144 
145 void ArnVcs::branches()
146 {
147 }
148 
149 
150 void ArnVcs::status()
151 {
152 }
153 
154 
155 void ArnVcs::userSettings()
156 {
157 }
158 
159 
160 void ArnVcs::setUserSettings( QString userName, QString userEmail)
161 {
162  Q_UNUSED(userName)
163  Q_UNUSED(userEmail)
164 }
165 
166 
167 void ArnVcs::checkout( QString ref, QStringList files)
168 {
169  Q_UNUSED(ref)
170  Q_UNUSED(files)
171 }
172 
173 
174 
175 ArnPersistPrivate::ArnPersistPrivate()
176 {
177  _db = new QSqlDatabase;
178  _archiveDir = new QDir("archive");
179  _persistDir = new QDir("persist");
180  _xsm = new XStringMap;
181  _sapiCommon = new ArnPersistSapi;
182  _vcs = new ArnVcs;
183  _arnMountPoint = arnNullptr;
184  _query = arnNullptr;
185  _depOffer = arnNullptr;
186 }
187 
188 ArnPersistPrivate::~ArnPersistPrivate()
189 {
190  delete _db;
191  delete _archiveDir;
192  delete _persistDir;
193  delete _xsm;
194  delete _sapiCommon;
195  delete _vcs;
196  if (_arnMountPoint) delete _arnMountPoint;
197  if (_query) delete _query;
198  if (_depOffer) delete _depOffer;
199 }
200 
201 
202 void ArnPersist::init()
203 {
204  Q_D(ArnPersist);
205 
206  setupSapi( d->_sapiCommon);
207 }
208 
209 
210 ArnPersist::ArnPersist( QObject* parent)
211  : QObject( parent)
212  , d_ptr( new ArnPersistPrivate)
213 {
214  init();
215 }
216 
217 
218 ArnPersist::ArnPersist( ArnPersistPrivate& dd, QObject* parent)
219  : QObject( parent)
220  , d_ptr( &dd)
221 {
222  init();
223 }
224 
225 
227 {
228  delete d_ptr;
229 }
230 
231 
232 void ArnPersist::setPersistDir(const QString &path)
233 {
234  Q_D(ArnPersist);
235 
236  d->_persistDir->setPath( path);
237 }
238 
239 
240 void ArnPersist::setArchiveDir(const QString &path)
241 {
242  Q_D(ArnPersist);
243 
244  d->_archiveDir->setPath( path);
245 }
246 
247 
248 void ArnPersist::setVcs( ArnVcs* vcs)
249 {
250  Q_D(ArnPersist);
251 
252  if (d->_vcs) {
253  delete d->_vcs;
254  d->_vcs = arnNullptr;
255  }
256  if (!vcs) return; // No use of VCS
257 
258  d->_vcs = vcs;
259  d->_vcs->setParent( this);
260 
261  connect( d->_vcs, SIGNAL(checkoutR()), this, SLOT(vcsCheckoutR()));
262  ArnRpc::Mode mode;
263  d->_sapiCommon->batchConnect( d->_vcs, ARN_RegExp("(.+)"), "rq_vcs\\1");
264  d->_sapiCommon->batchConnect( ARN_RegExp("^pv_vcs(.+)"), d->_vcs, "\\1");
265 }
266 
267 
268 ArnItemPersist* ArnPersist::getPersistItem( const QString& path)
269 {
270  Q_D(ArnPersist);
271 
272  ArnItemPersist* item = new ArnItemPersist( this);
273  item->open( path);
274  uint linkId = item->linkId();
275 
276  if (d->_itemPersistMap.contains( linkId)) { // Already as persist
277  qDebug() << "getPersistItem already persist: path=" << path << " link=" << linkId;
278  delete item;
279  item = d->_itemPersistMap.value( linkId);
280 
281  return item;
282  }
283 
284  item->setIgnoreSameValue( false);
285  item->setDelay( 5000);
286 
287  connect( item->toArnItem(), SIGNAL(changed()), this, SLOT(doArnUpdate()));
288  connect( item->toArnItem(), SIGNAL(arnLinkDestroyed()), this, SLOT(doArnDestroy()));
289  d->_itemPersistMap.insert( linkId, item);
290 
291  return item;
292 }
293 
294 
295 ArnItemPersist* ArnPersist::setupMandatory( const QString& path, bool isMandatory)
296 {
297  Q_D(ArnPersist);
298 
299  ArnItemPersist::StoreType st;
300  ArnItemPersist* item = arnNullptr;
301 
302  if (isMandatory) {
303  item = getPersistItem( path);
304  item->setMandatory(true);
305  if ((item->storeType() == st.DataBase) && !d->_pathPersistMap.contains( path)) {
306  d->_pathPersistMap.insert( path, item->linkId());
307  item->addMode( Arn::ObjectMode::Save);
308  }
309  }
310  else {
311  uint linkId = d->_pathPersistMap.value( path);
312  if (!linkId) return arnNullptr;
313 
314  item = d->_itemPersistMap.value( linkId);
315  Q_ASSERT(item);
316  item->setMandatory(false);
317  if (item->storeType() != st.File) { // Not persist file
318  d->_pathPersistMap.remove( path); // Map only needed for mandatory & file
319  if (!item->getMode().is( Arn::ObjectMode::Save)) {
320  d->_itemPersistMap.remove( linkId);
321  delete item;
322  }
323  }
324  }
325  return item;
326 }
327 
328 
329 void ArnPersist::removeFilePersistItem( const QString& path)
330 {
331  Q_D(ArnPersist);
332 
333  uint linkId = d->_pathPersistMap.value( path);
334  if (!linkId) return; // path not found
335 
336  ArnItemPersist* item = d->_itemPersistMap.value( linkId);
337  Q_ASSERT(item);
338  ArnItemPersist::StoreType st;
339  if (item->storeType() != st.File) return; // Not persist file
340 
341  item->setStoreType( st.DataBase);
342  setupMandatory( path, item->isMandatory());
343 }
344 
345 
346 void ArnPersist::doArnDestroy()
347 {
348  Q_D(ArnPersist);
349 
350  ArnItemPersist* item = ArnItemPersist::fromArnItem( qobject_cast<ArnItem*>( sender()));
351  if (!item) return; // No valid sender
352 
353  // qDebug() << "Persist destroyArn: path=" << item->path();
354  d->_itemPersistMap.remove( item->linkId());
355  d->_pathPersistMap.remove( item->path());
356  item->deleteLater();
357 }
358 
359 
360 void ArnPersist::doArnModeChanged( const QString& path, uint linkId, Arn::ObjectMode mode)
361 {
362  Q_D(ArnPersist);
363 
364  // qDebug() << "Persist modeChanged: path=" << path << " mode=" << mode.f;
365  if (!mode.is( mode.Save)) return; // Not save-mode
366  if (Arn::isProviderPath( path)) return; // Only value path is used (non provider)
367  if (d->_itemPersistMap.contains( linkId)) return; // Already in map
368  // qDebug() << "Persist modeChanged New: path=" << path << " mode=" << mode.f;
369 
370  ArnItemPersist* item = getPersistItem( path);
371  QByteArray data;
372  int storeId;
373  if (getDbValue( path, data, storeId)) { // path exist in db, load data
374  // qDebug() << "Persist modeChanged dbRead: path=" << path;
375  item->arnImport( data, false);
376  item->setStoreId( storeId);
377  }
378  else { // path is new in db, create it with cuurent value in item // null data
379  // qDebug() << "Persist modeChanged dbInsert: path=" << path;
380  //insertDbValue( path, QByteArray());
381  QByteArray data = item->arnExport();
382  insertDbValue( path, data);
383  if (getDbId( path, storeId)) {
384  item->setStoreId( storeId);
385  }
386  item->arnImport( data, false); // Do an update, to signal update done
387  //item->setValue( QByteArray(), false); // Do a null update, to signal update done
388  }
389 }
390 
391 
392 void ArnPersist::doArnUpdate()
393 {
394  Q_D(ArnPersist);
395 
396  ArnItemPersist* item;
397  item = ArnItemPersist::fromArnItem( qobject_cast<ArnItem*>( sender()));
398  if (!item) {
399  ArnM::errorLog( QString(tr("Can't get ArnItemPersist sender for doArnUpdate")),
401  return;
402  }
403 
404  switch (item->storeType()) {
405  case ArnItemPersist::StoreType::DataBase:
406  {
407  // qDebug() << "Persist arnUpdate DB: path=" << item->path();
408  int storeId = item->storeId();
409  if (storeId) {
410  updateDbValue( storeId, item->arnExport());
411  }
412  else {
413  ArnM::errorLog( QString(tr("Can't get persist storeId for doArnUpdate: path=")) +
414  item->path(), ArnError::Undef);
415  }
416  break;
417  }
418  case ArnItemPersist::StoreType::File:
419  {
420  QString relPath = item->path( Arn::NameF::Relative);
421  // qDebug() << "Persist arnUpdate: Save to relPath=" << relPath;
422  QFile file( d->_persistDir->absoluteFilePath( relPath));
423  file.open( QIODevice::WriteOnly);
424  QByteArray data = item->toByteArray();
425  file.write( data);
426  // qDebug() << "Persist arnUpdate: Save to file=" << file.fileName();
427  break;
428  }
429  }
430  // qDebug() << "Persist Update: id=" << storeId << " path=" << item->path() << " value=" << item->toByteArray();
431 }
432 
433 
434 bool ArnPersist::setMountPoint( const QString& path)
435 {
436  Q_D(ArnPersist);
437 
438  if (!d->_db->isOpen()) {
439  ArnM::errorLog( QString(tr("DataBase required before mountPoint")),
441  return false;
442  }
443 
444  if (d->_arnMountPoint) delete d->_arnMountPoint;
445 
446  d->_arnMountPoint = new ArnItem( this);
447  bool isOk = d->_arnMountPoint->openFolder( path);
448  if (isOk) {
449  connect( d->_arnMountPoint, SIGNAL(arnModeChanged(QString,uint,Arn::ObjectMode)),
450  this, SLOT(doArnModeChanged(QString,uint,Arn::ObjectMode)));
451 
452  // Load all persist files & mandatory into arn
453  doLoadFiles();
454  doLoadMandatory();
455 
456  if (d->_depOffer) delete d->_depOffer;
457  // Persist service is available
458  d->_depOffer = new ArnDependOffer( this);
459  d->_depOffer->advertise("$Persist");
460  }
461 
462  return isOk;
463 }
464 
465 
466 bool ArnPersist::setupDataBase( const QString& dbName)
467 {
468  Q_D(ArnPersist);
469 
470  if (d->_query) delete d->_query;
471 
472  *d->_db = QSqlDatabase::addDatabase("QSQLITE", "ArnPersist");
473  //db.setHostName("bigblue");
474  d->_db->setDatabaseName( dbName);
475  //db.setUserName("acarlson");
476  //db.setPassword("1uTbSbAs");
477  if (!d->_db->open()) {
478  ArnM::errorLog( QString(tr("DataBase open: ") + d->_db->lastError().text()),
480  return false;
481  }
482  d->_query = new QSqlQuery( *d->_db);
483 
484  int curArnDbVer = 100; // Default for db with no meta table
485  // qDebug() << "Persist-Db tables:" << d->_db->tables();
486  if (d->_db->tables().contains("meta"))
487  curArnDbVer = metaDbValue("ver", "101").toInt();
488  bool hasStoreTable = d->_db->tables().contains("store");
489 
491  if (curArnDbVer <= 100) {
492  d->_query->exec("CREATE TABLE meta ("
493  "attr TEXT PRIMARY KEY,"
494  "value TEXT)");
495  ArnM::errorLog("Creating Persist meta-table", ArnError::Info);
496  }
497  if (curArnDbVer <= 101)
498  setMetaDbValue("ver", "102");
499 
500  if ((curArnDbVer < 200) && hasStoreTable) {
501  d->_query->exec("ALTER TABLE store RENAME TO store_save");
502  ArnM::errorLog("Saving old Persist data to table 'store_save'", ArnError::Info);
503  }
504  if ((curArnDbVer < 200) || !hasStoreTable) {
505  d->_query->exec("CREATE TABLE store ("
506  "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
507  "meta TEXT,"
508  "path TEXT,"
509  "valueTxt TEXT,"
510  "value BLOB,"
511  "isMandatory INTEGER NOT NULL DEFAULT(0),"
512  "isUsed INTEGER NOT NULL DEFAULT(1))");
513  setMetaDbValue("ver", "200");
514  ArnM::errorLog("Creating new Persist data table", ArnError::Info);
515  }
516  if ((curArnDbVer < 200) && hasStoreTable) {
517  d->_query->exec("INSERT INTO store (path, value, isUsed, isMandatory) "
518  "SELECT path, value, isUsed, isMandatory FROM store_save");
519  d->_query->exec("DROP TABLE store_save");
520  ArnM::errorLog("Converting Persist data to ArnDB 2.0", ArnError::Info);
521  }
522 
523  return true;
524 }
525 
526 
527 bool ArnPersist::flush( const QString& path)
528 {
529  Q_D(ArnPersist);
530 
531  bool isOk = true;
532  QString fullPath = Arn::fullPath( path);
533  foreach (ArnItemPersist* item, d->_itemPersistMap) {
534  if (path.isEmpty() || item->path().startsWith( fullPath)) {
535  // if (item->isDelayPending())
536  // qDebug() << "Persist flush: path=" << item->path();
537  item->bypassDelayPending();
538  }
539  }
540  return isOk;
541 }
542 
543 
544 QString ArnPersist::metaDbValue( const QString& attr, const QString& def)
545 {
546  Q_D(ArnPersist);
547 
548  QString retVal = def;
549 
550  d->_query->prepare("SELECT value FROM meta WHERE attr = :attr");
551  d->_query->bindValue(":attr", attr);
552  d->_query->exec();
553  if (d->_query->next()) {
554  retVal = d->_query->value(0).toString();
555  if (retVal.isNull()) // Successful query must not be null
556  retVal = "";
557  }
558  d->_query->finish();
559 
560  return retVal;
561 }
562 
563 
564 bool ArnPersist::setMetaDbValue( const QString& attr, const QString& value)
565 {
566  Q_D(ArnPersist);
567 
568  QString curVal = metaDbValue( attr);
569  if (value == curVal) return true; // Already set to value
570 
571  if (curVal.isNull()) // Meta attr not present, insert new
572  d->_query->prepare("INSERT INTO meta (attr, value) VALUES (:attr, :value)");
573  else // Meta attr present, update it
574  d->_query->prepare("UPDATE meta SET value = :value WHERE attr = :attr");
575 
576  d->_query->bindValue(":attr", attr);
577  d->_query->bindValue(":value", value);
578  bool retVal = d->_query->exec();
579  d->_query->finish();
580 
581  return retVal;
582 }
583 
584 
585 bool ArnPersist::getDbId( const QString& path, int& storeId)
586 {
587  Q_D(ArnPersist);
588 
589  bool retVal = false;
590  int isUsed = 1;
591 
592  d->_query->prepare("SELECT id, isUsed FROM store WHERE path = :path");
593  d->_query->bindValue(":path", path);
594  d->_query->exec();
595  if (d->_query->next()) {
596  storeId = d->_query->value(0).toInt();
597  isUsed = d->_query->value(1).toInt();
598  retVal = true;
599  }
600  d->_query->finish();
601 
602  if (!isUsed) updateDbUsed( storeId, 1);
603  return retVal;
604 }
605 
606 
610 void ArnPersist::dbSetupReadValue( const QString& meta, const QString& valueTxt,
611  QByteArray& value)
612 {
613  Q_D(ArnPersist);
614 
615  if (!value.isEmpty()) return; // Non textual is used
616 
617  if (!meta.isEmpty()) {
618  d->_xsm->fromXString( meta.toLatin1());
619  QByteArray variantType = d->_xsm->value("V");
620  if (!variantType.isEmpty()) { // Variant is stored
621  value = char( Arn::ExportCode::VariantTxt)
622  + variantType + ":" + valueTxt.toUtf8();
623  return;
624  }
625  }
626  value = valueTxt.toUtf8();
627  if (!value.isEmpty()) {
628  if (value.at(0) < 32) { // Starting char conflicting with Export-code
629  value.insert( 0, char( Arn::ExportCode::String)); // Stuff String-code
630  }
631  }
632 }
633 
634 
638 void ArnPersist::dbSetupWriteValue( QString& meta, QString& valueTxt, QByteArray& value)
639 {
640  Q_D(ArnPersist);
641 
642  valueTxt = "";
643  meta = "";
644  if (value.isEmpty()) return;
645 
646  uchar c = uchar( value.at(0));
647  if (c == Arn::ExportCode::VariantTxt) {
648  int sepPos = value.indexOf(':', 1);
649  Q_ASSERT(sepPos > 0);
650 
651  QByteArray variantType( value.constData() + 1, sepPos - 1);
652  d->_xsm->clear();
653  d->_xsm->add("V", variantType);
654  meta = QString::fromLatin1( d->_xsm->toXString().constData());
655  valueTxt = QString::fromUtf8( value.constData() + sepPos + 1, value.size() - sepPos - 1);
656  value = QByteArray();
657  }
658  else if (c == Arn::ExportCode::String) {
659  valueTxt = QString::fromUtf8( value.constData() + 1, value.size() - 1);
660  value = QByteArray();
661  }
662  else if (c >= 32) { // Normal printable
663  valueTxt = QString::fromUtf8( value.constData(), value.size());
664  value = QByteArray();
665  }
666 }
667 
668 
669 bool ArnPersist::getDbValue(int storeId, QString &path, QByteArray &value)
670 {
671  Q_D(ArnPersist);
672 
673  bool retVal = false;
674  int isUsed = 1;
675  QByteArray meta;
676  QString valueTxt;
677  d->_query->prepare("SELECT meta, path, valueTxt, value, isUsed FROM store WHERE id = :id");
678  d->_query->bindValue(":id", storeId);
679  d->_query->exec();
680  if (d->_query->next()) {
681  meta = d->_query->value(0).toByteArray();
682  path = d->_query->value(1).toString();
683  valueTxt = d->_query->value(2).toString();
684  value = d->_query->value(3).toByteArray();
685  isUsed = d->_query->value(4).toInt();
686  retVal = true;
687  dbSetupReadValue( meta, valueTxt, value);
688  }
689  d->_query->finish();
690 
691  if (!isUsed) updateDbUsed( storeId, 1);
692  return retVal;
693 }
694 
695 
696 bool ArnPersist::getDbValue( const QString& path, QByteArray& value, int& storeId)
697 {
698  Q_D(ArnPersist);
699 
700  bool retVal = false;
701  int isUsed = 1;
702  QByteArray meta;
703  QString valueTxt;
704  d->_query->prepare("SELECT id, meta, valueTxt, value, isUsed FROM store WHERE path = :path");
705  d->_query->bindValue(":path", path);
706  d->_query->exec();
707  if (d->_query->next()) {
708  storeId = d->_query->value(0).toInt();
709  meta = d->_query->value(1).toByteArray();
710  valueTxt = d->_query->value(2).toString();
711  value = d->_query->value(3).toByteArray();
712  isUsed = d->_query->value(4).toInt();
713  retVal = true;
714  dbSetupReadValue( meta, valueTxt, value);
715  }
716  d->_query->finish();
717 
718  if (!isUsed) updateDbUsed( storeId, 1);
719  return retVal;
720 }
721 
722 
723 bool ArnPersist::getDbMandatoryList( QList<int>& storeIdList)
724 {
725  Q_D(ArnPersist);
726 
727  bool retVal = false;
728  storeIdList.clear();
729  d->_query->prepare("SELECT id FROM store WHERE isMandatory = 1");
730  d->_query->exec();
731  while (d->_query->next()) {
732  storeIdList += d->_query->value(0).toInt();
733  retVal = true;
734  }
735  d->_query->finish();
736 
737  return retVal;
738 }
739 
740 
741 bool ArnPersist::getDbList( bool isUsed, QList<int> &storeIdList)
742 {
743  Q_D(ArnPersist);
744 
745  bool retVal = false;
746  storeIdList.clear();
747  d->_query->prepare("SELECT id FROM store WHERE isUsed = :isUsed");
748  d->_query->bindValue(":isUsed", int(isUsed));
749  d->_query->exec();
750  while (d->_query->next()) {
751  storeIdList += d->_query->value(0).toInt();
752  retVal = true;
753  }
754  d->_query->finish();
755 
756  return retVal;
757 }
758 
759 
760 bool ArnPersist::insertDbValue( const QString& path, const QByteArray& value)
761 {
762  Q_D(ArnPersist);
763 
764  bool retVal = false;
765 
766  QString meta;
767  QString valueTxt;
768  QByteArray value_ = value;
769  dbSetupWriteValue( meta, valueTxt, value_);
770 
771  d->_query->prepare("INSERT INTO store (meta, path, valueTxt, value) "
772  "VALUES (:meta, :path, :valueTxt, :value)");
773  d->_query->bindValue(":meta", meta);
774  d->_query->bindValue(":path", path);
775  d->_query->bindValue(":valueTxt", valueTxt);
776  d->_query->bindValue(":value", value_);
777  retVal = d->_query->exec();
778  d->_query->finish();
779 
780  return retVal;
781 }
782 
783 
784 bool ArnPersist::updateDbValue( int storeId, const QByteArray& value)
785 {
786  Q_D(ArnPersist);
787 
788  // qDebug() << "Persist updateDb: id=" << storeId << " value=" << value;
789  bool retVal = false;
790 
791  QString meta;
792  QString valueTxt;
793  QByteArray value_ = value;
794  dbSetupWriteValue( meta, valueTxt, value_);
795 
796  d->_query->prepare("UPDATE store SET meta = :meta, valueTxt = :valueTxt, value = :value "
797  "WHERE id = :id");
798  d->_query->bindValue(":id", storeId);
799  d->_query->bindValue(":meta", meta);
800  d->_query->bindValue(":valueTxt", valueTxt);
801  d->_query->bindValue(":value", value_);
802  retVal = d->_query->exec();
803  d->_query->finish();
804 
805  return retVal;
806 }
807 
808 
809 bool ArnPersist::updateDbUsed( int storeId, int isUsed)
810 {
811  Q_D(ArnPersist);
812 
813  // qDebug() << "UpdateDb: id=" << storeId << " isUsed=" << isUsed;
814  bool retVal = false;
815 
816  d->_query->prepare("UPDATE store SET isUsed = :isUsed WHERE id = :id");
817  d->_query->bindValue(":id", storeId);
818  d->_query->bindValue(":isUsed", isUsed);
819  retVal = d->_query->exec();
820  d->_query->finish();
821 
822  return retVal;
823 }
824 
825 
826 bool ArnPersist::updateDbMandatory( int storeId, int isMandatory)
827 {
828  Q_D(ArnPersist);
829 
830  // qDebug() << "UpdateDb: id=" << storeId << " isMandatory=" << isMandatory;
831  bool retVal = false;
832 
833  d->_query->prepare("UPDATE store SET isMandatory = :isMandatory WHERE id = :id");
834  d->_query->bindValue(":id", storeId);
835  d->_query->bindValue(":isMandatory", isMandatory);
836  retVal = d->_query->exec();
837  d->_query->finish();
838 
839  return retVal;
840 }
841 
842 
843 bool ArnPersist::doArchive( const QString& name)
844 {
845  Q_D(ArnPersist);
846 
847  QString arFileName;
848 
849  if (name.isNull()) {
850  QDateTime dateTime = QDateTime::currentDateTime();
851  QString nowStr = dateTime.toString("yyMMdd-hhmmss");
852  arFileName = d->_archiveDir->absoluteFilePath( QString("persist_%1.db").arg( nowStr));
853  }
854  else {
855  arFileName = d->_archiveDir->absoluteFilePath( name);
856  }
857  QString dbFileName = d->_db->databaseName();
858 
859  // qDebug() << "Persist Archive: src=" << dbFileName << " dst=" << arFileName;
860  return QFile::copy( dbFileName, arFileName);
861 }
862 
863 
864 void ArnPersist::doLoadMandatory()
865 {
866  QList<int> mandatoryList;
867  if (!getDbMandatoryList( mandatoryList)) return; // Nothing to load
868 
869  QString path;
870  QByteArray value;
871  foreach (int storeId, mandatoryList) {
872  if (!getDbValue( storeId, path, value)) continue; // Not ok ...
873 
874  ArnItemPersist::StoreType st;
875  ArnItemPersist* item = setupMandatory( path, true);
876  Q_ASSERT(item);
877  if (item->storeType() != st.DataBase) continue; // Not a database persist
878 
879  QByteArray data;
880  int storeId2;
881  if (getDbValue( path, data, storeId2)) {
882  item->arnImport( data, false);
883  item->setStoreId( storeId2);
884  }
885  else {
886  item->setValue( QByteArray(), false); // Do a null update, to signal update done
887  }
888  }
889 }
890 
891 
892 void ArnPersist::doLoadFiles()
893 {
894  Q_D(ArnPersist);
895 
896  QStringList flist;
897  getFileList( flist, *d->_persistDir);
898  foreach (const QString& relPath, flist) {
899  loadFile( relPath);
900  }
901 }
902 
903 
904 void ArnPersist::loadFile( const QString& relPath)
905 {
906  Q_D(ArnPersist);
907 
908  QString arnPath = Arn::convertPath( relPath, Arn::NameF::EmptyOk);
909  // qDebug() << "Persist loadFile: relPath=" << relPath;
910 
911  ArnItemPersist* item;
912  if (!d->_pathPersistMap.contains( arnPath)) { // First time loaded
913  item = getPersistItem( arnPath);
914  item->setDelay( 500); // MW: ok?
915  d->_pathPersistMap.insert( arnPath, item->linkId());
916  }
917  else {
918  uint linkId = d->_pathPersistMap.value( arnPath);
919  item = d->_itemPersistMap.value( linkId);
920  Q_ASSERT(item);
921  }
922  item->setStoreType( ArnItemPersist::StoreType::File);
923 
924  QFile file( d->_persistDir->absoluteFilePath( relPath));
925  file.open( QIODevice::ReadOnly);
926  QByteArray data = file.readAll();
927  // qDebug() << "Persist loadFile: absPath=" << file.fileName();
928  item->setValue( data, Arn::SameValue::Accept);
929 }
930 
931 
932 void ArnPersist::getFileList(QStringList& flist, const QDir& dir, const QDir* baseDir)
933 {
934  if (!baseDir)
935  baseDir = &dir;
936  QString path = dir.absolutePath();
937  // qDebug() << "fileList: dir=" << path;
938 
939  foreach( QFileInfo finfo, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files)) {
940  if (finfo.isFile()) {
941  flist += baseDir->relativeFilePath( finfo.absoluteFilePath());
942  }
943  else if (finfo.isDir()) {
944  QString fname = finfo.fileName();
945  getFileList( flist, QDir(finfo.absoluteFilePath()), baseDir);
946  }
947  }
948 }
949 
950 
951 void ArnPersist::setupSapi( ArnPersistSapi* sapi)
952 {
953  typedef ArnRpc::Mode Mode;
954  sapi->open( QString(), Mode::Provider);
955  sapi->batchConnect( ARN_RegExp("^pv_(.+)"), this, "sapi\\1", Mode());
956 }
957 
958 
959 void ArnPersist::destroyRpc()
960 {
961  ArnRpc* rpc = qobject_cast<ArnRpc*>( sender());
962  if (!rpc) return; // No valid sender
963 
964  // qDebug() << "Persist destroyRpc: pipePath=" << rpc->pipePath();
965  rpc->deleteLater();
966 }
967 
968 
969 void ArnPersist::convertFileList( QStringList& files, Arn::NameF nameF)
970 {
971  for (int i = 0; i < files.size(); ++i) {
972  files[i] = Arn::convertPath( files.at(i), nameF);
973  }
974 }
975 
976 
977 void ArnPersist::vcsCheckoutR()
978 {
979  doLoadFiles();
980  // emit rps_vcsCheckoutR();
981 }
982 
983 
984 void ArnPersist::sapiFlush( const QString& path)
985 {
986  Q_D(ArnPersist);
987 
988  bool isOk = flush( path);
989  emit d->_sapiCommon->rq_flushR( isOk, path);
990 }
991 
992 
993 void ArnPersist::sapiTest( const QString& str, int i)
994 {
995  ArnPersistSapi* sapiSender = qobject_cast<ArnPersistSapi*>( sender());
996  if (sapiSender) {
997  qDebug() << "sapiTest sender=" << sapiSender->pipePath();
998  sapiSender->sendText("sapiTest: str=" + str + " int=" + QString::number(i));
999  }
1000  qDebug() << "----- sapiTest: str=" << str << " int=" << i;
1001 }
1002 
1003 
1004 void ArnPersist::sapiLoad()
1005 {
1006  doLoadFiles();
1007 }
1008 
1009 
1010 void ArnPersist::sapiLs( const QString& path)
1011 {
1012  Q_D(ArnPersist);
1013 
1014  QStringList flist;
1015  getFileList( flist, *d->_persistDir);
1016  convertFileList( flist, Arn::NameF::EmptyOk);
1017 
1018  if (path.isEmpty())
1019  emit d->_sapiCommon->rq_lsR( flist);
1020  else { // Filter only files beginning with path
1021  QStringList retList;
1022  foreach( QString fstr, flist) {
1023  if (fstr.startsWith( path))
1024  retList += fstr;
1025  }
1026  emit d->_sapiCommon->rq_lsR( retList);
1027  }
1028 }
1029 
1030 
1031 void ArnPersist::sapiRm( const QString& path)
1032 {
1033  Q_D(ArnPersist);
1034 
1035  QString relPath = Arn::convertPath( path, Arn::NameF::Relative);
1036  bool isOk = true;
1037  if (Arn::isFolderPath( relPath)) {
1038  QStringList flist;
1039  getFileList( flist, *d->_persistDir);
1040  foreach (const QString& delPath, flist) {
1041  if (delPath.startsWith( relPath)) {
1042  isOk &= d->_persistDir->remove( delPath);
1043  removeFilePersistItem( Arn::convertPath( delPath, Arn::NameF::EmptyOk));
1044  if (isOk) {
1045  d->_persistDir->rmpath( Arn::parentPath( delPath)); // Remove empty paths if possible
1046  }
1047  }
1048  }
1049  }
1050  else {
1051  isOk = d->_persistDir->remove( relPath);
1052  removeFilePersistItem( path);
1053  if (isOk) {
1054  d->_persistDir->rmpath( Arn::parentPath( relPath)); // Remove empty paths if possible
1055  }
1056  }
1057 
1058  emit d->_sapiCommon->rq_rmR( isOk);
1059 }
1060 
1061 
1062 void ArnPersist::sapiTouch( const QString& path)
1063 {
1064  Q_D(ArnPersist);
1065 
1066  if (path.endsWith('/')) return; // Don't touch a dir
1067 
1068  bool isOk = true;
1069  QString relPath = Arn::convertPath( path, Arn::NameF::Relative);
1070  QString filePath = d->_persistDir->absoluteFilePath( relPath);
1071  // Make any needed directories
1072  isOk &= d->_persistDir->mkpath( QFileInfo( filePath).dir().path());
1073 
1075  QFile file( filePath);
1076  isOk &= file.open( QIODevice::ReadWrite);
1077  file.close();
1078  if (isOk)
1079  loadFile( relPath);
1080 
1081  emit d->_sapiCommon->rq_touchR( isOk);
1082 }
1083 
1084 
1085 void ArnPersist::sapiDbMandatory( const QString& path, bool isMandatory)
1086 {
1087  Q_D(ArnPersist);
1088 
1089  bool isOk = true;
1090 
1091  if (Arn::isFolderPath( path)) {
1092  QList<int> storeIdList;
1093  if (!getDbList( true, storeIdList)) {
1094  emit d->_sapiCommon->rq_dbMandatoryR( isOk);
1095  return;
1096  }
1097 
1098  QString pathDb;
1099  QByteArray value;
1100  foreach (int storeId, storeIdList) {
1101  if (!getDbValue( storeId, pathDb, value)) continue;
1102  if (!pathDb.startsWith( path)) continue;
1103 
1104  if (updateDbMandatory( storeId, isMandatory)) {
1105  setupMandatory( pathDb, isMandatory);
1106  }
1107  else
1108  isOk = false;
1109  }
1110  }
1111  else {
1112  int storeId;
1113  if (getDbId( path, storeId)) {
1114  if (updateDbMandatory( storeId, isMandatory)) {
1115  setupMandatory( path, isMandatory);
1116  }
1117  else
1118  isOk = false;
1119  }
1120  }
1121  emit d->_sapiCommon->rq_dbMandatoryR( isOk);
1122 }
1123 
1124 
1125 void ArnPersist::sapiDbMandatoryLs( const QString& path)
1126 {
1127  Q_D(ArnPersist);
1128 
1129  QStringList retList;
1130  QList<int> storeIdList;
1131  if (!getDbMandatoryList( storeIdList)) {
1132  emit d->_sapiCommon->rq_dbMandatoryLsR( retList);
1133  return;
1134  }
1135 
1136  QString pathDb;
1137  QByteArray value;
1138  foreach (int storeId, storeIdList) {
1139  if (!getDbValue( storeId, pathDb, value)) continue;
1140  if (!pathDb.startsWith( path)) continue;
1141 
1142  retList += pathDb;
1143  }
1144 
1145  emit d->_sapiCommon->rq_dbMandatoryLsR( retList);
1146 }
1147 
1148 
1149 void ArnPersist::sapiDbLs( const QString& path, bool isUsed)
1150 {
1151  Q_D(ArnPersist);
1152 
1153  QStringList retList;
1154  QList<int> storeIdList;
1155  if (!getDbList( isUsed, storeIdList)) {
1156  emit d->_sapiCommon->rq_dbLsR( retList);
1157  return;
1158  }
1159 
1160  QString pathDb;
1161  QByteArray value;
1162  foreach (int storeId, storeIdList) {
1163  if (!getDbValue( storeId, pathDb, value)) continue;
1164  if (!pathDb.startsWith( path)) continue;
1165 
1166  retList += pathDb;
1167  }
1168 
1169  emit d->_sapiCommon->rq_dbLsR( retList);
1170 }
1171 
1172 
1173 void ArnPersist::sapiDbMarkUnused( const QString& path)
1174 {
1175  Q_D(ArnPersist);
1176 
1177  QList<int> storeIdList;
1178  if (!getDbList( true, storeIdList)) {
1179  emit d->_sapiCommon->rq_dbMarkUnusedR(false); // Error
1180  return;
1181  }
1182 
1183  QString pathDb;
1184  QByteArray value;
1185  foreach (int storeId, storeIdList) {
1186  if (!getDbValue( storeId, pathDb, value)) continue;
1187  if (!pathDb.startsWith( path)) continue;
1188 
1189  if (!updateDbUsed( storeId, false)) {
1190  emit d->_sapiCommon->rq_dbMarkUnusedR(false); // Error
1191  return;
1192  }
1193  }
1194  emit d->_sapiCommon->rq_dbMarkUnusedR(true); // Success
1195 }
1196 
1197 
1198 void ArnPersist::sapiInfo()
1199 {
1200  Q_D(ArnPersist);
1201 
1202  emit d->_sapiCommon->rq_infoR("Arn Persist", "1.0");
1203 }
void setValue(const ArnBasicItem &other, int ignoreSame=Arn::SameValue::DefaultAction)
ArnPersist(QObject *parent=arnNullptr)
Definition: ArnPersist.cpp:210
Path: "/@/test" ==> "//test", Item: "@" ==> "".
Definition: Arn.hpp:189
Class for handling persistent Arn Data object.
Definition: ArnPersist.hpp:168
Class for advertising that a service is available.
Definition: ArnDepend.hpp:59
Container class with string representation for serialized data.
Definition: XStringMap.hpp:107
void setIgnoreSameValue(bool isIgnore=true)
Set skipping of equal value.
void setUncrossed(bool isUncrossed=true)
Set a Bidirectional item as Uncrossed.
QString fullPath(const QString &path)
Convert a path to a full absolute path.
Definition: Arn.cpp:82
Assigning same value generates an update of the Arn Data Object
Definition: Arn.hpp:64
#define ARN_RegExp
Definition: ArnCompat.hpp:70
void setArchiveDir(const QString &path)
Set the persistent database backup directory.
Definition: ArnPersist.cpp:240
bool setMountPoint(const QString &path)
Set the persistent enabled tree path.
Definition: ArnPersist.cpp:434
Only on path, no effect on discrete names. "/test/value" ==> "test/value".
Definition: Arn.hpp:191
void setVcs(ArnVcs *vcs)
Set the Version Control System to be used.
Definition: ArnPersist.cpp:248
Remote Procedure Call.
Definition: ArnRpc.hpp:156
void arnImport(const QByteArray &data, int ignoreSame=Arn::SameValue::DefaultAction)
Import data to an Arn Data Object
bool setupDataBase(const QString &dbName="persist.db")
Setup the persistent database.
Definition: ArnPersist.cpp:466
QString convertPath(const QString &path, Arn::NameF nameF)
Convert a path to a specific format.
Definition: Arn.cpp:148
void setPersistDir(const QString &path)
Set the persistent file directory root
Definition: ArnPersist.cpp:232
QString parentPath(const QString &path)
Get the parent to a given path
Definition: Arn.cpp:183
bool doArchive(const QString &name=QString())
Do a persistent database backup.
Definition: ArnPersist.cpp:843
bool flush(const QString &path=QString())
Save any pending values now.
Definition: ArnPersist.cpp:527
static void errorLog(QString errText, ArnError err=ArnError::Undef, void *reference=arnNullptr)
Definition: ArnM.cpp:1025
bool isFolderPath(const QString &path)
Test if path is a folder path
Definition: Arn.cpp:210
Data is persistent and will be saved.
Definition: Arn.hpp:130
Handle for an Arn Data Object.
Definition: ArnItem.hpp:72
bool isProviderPath(const QString &path)
Test if path is a provider path
Definition: Arn.cpp:216