33 #include "private/ArnPersist_p.hpp" 38 #include <QtSql/QSqlDatabase> 39 #include <QtSql/QSqlQuery> 40 #include <QtSql/QSqlError> 45 #include <QStringList> 48 #include <QMetaObject> 49 #include <QMetaMethod> 54 ArnItemPersist::ArnItemPersist(
ArnPersist* arnPersist) :
60 _arnPersist = arnPersist;
63 _storeType = StoreType::DataBase;
67 ArnItemPersist::~ArnItemPersist()
72 void ArnItemPersist::arnImport(
const QByteArray& data,
int ignoreSame)
74 ArnLinkHandle handleData;
75 handleData.flags().set( ArnLinkHandle::Flags::FromPersist);
80 void ArnItemPersist::setValue(
const QByteArray& value,
int ignoreSame)
82 ArnLinkHandle handleData;
83 handleData.flags().set( ArnLinkHandle::Flags::FromPersist);
88 ArnVcs::ArnVcs( QObject* parent) :
99 void ArnVcs::log(
int numLog)
105 void ArnVcs::files( QString ref)
111 void ArnVcs::commit( QString commitMsg, QStringList files, QString name, QString email)
120 void ArnVcs::tag( QString name, QString ref)
127 void ArnVcs::diff( QString ref, QStringList files)
134 void ArnVcs::logRecord( QString ref)
145 void ArnVcs::branches()
150 void ArnVcs::status()
155 void ArnVcs::userSettings()
160 void ArnVcs::setUserSettings( QString userName, QString userEmail)
167 void ArnVcs::checkout( QString ref, QStringList files)
175 ArnPersistPrivate::ArnPersistPrivate()
177 _db =
new QSqlDatabase;
178 _archiveDir =
new QDir(
"archive");
179 _persistDir =
new QDir(
"persist");
181 _sapiCommon =
new ArnPersistSapi;
183 _arnMountPoint = arnNullptr;
185 _depOffer = arnNullptr;
188 ArnPersistPrivate::~ArnPersistPrivate()
196 if (_arnMountPoint)
delete _arnMountPoint;
197 if (_query)
delete _query;
198 if (_depOffer)
delete _depOffer;
202 void ArnPersist::init()
206 setupSapi( d->_sapiCommon);
212 , d_ptr( new ArnPersistPrivate)
236 d->_persistDir->setPath( path);
244 d->_archiveDir->setPath( path);
254 d->_vcs = arnNullptr;
259 d->_vcs->setParent(
this);
261 connect( d->_vcs, SIGNAL(checkoutR()),
this, SLOT(vcsCheckoutR()));
263 d->_sapiCommon->batchConnect( d->_vcs,
ARN_RegExp(
"(.+)"),
"rq_vcs\\1");
264 d->_sapiCommon->batchConnect(
ARN_RegExp(
"^pv_vcs(.+)"), d->_vcs,
"\\1");
268 ArnItemPersist* ArnPersist::getPersistItem(
const QString& path)
272 ArnItemPersist* item =
new ArnItemPersist(
this);
274 uint linkId = item->linkId();
276 if (d->_itemPersistMap.contains( linkId)) {
277 qDebug() <<
"getPersistItem already persist: path=" << path <<
" link=" << linkId;
279 item = d->_itemPersistMap.value( linkId);
284 item->setIgnoreSameValue(
false);
285 item->setDelay( 5000);
287 connect( item->toArnItem(), SIGNAL(changed()),
this, SLOT(doArnUpdate()));
288 connect( item->toArnItem(), SIGNAL(arnLinkDestroyed()),
this, SLOT(doArnDestroy()));
289 d->_itemPersistMap.insert( linkId, item);
295 ArnItemPersist* ArnPersist::setupMandatory(
const QString& path,
bool isMandatory)
299 ArnItemPersist::StoreType st;
300 ArnItemPersist* item = arnNullptr;
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());
311 uint linkId = d->_pathPersistMap.value( path);
312 if (!linkId)
return arnNullptr;
314 item = d->_itemPersistMap.value( linkId);
316 item->setMandatory(
false);
317 if (item->storeType() != st.File) {
318 d->_pathPersistMap.remove( path);
320 d->_itemPersistMap.remove( linkId);
329 void ArnPersist::removeFilePersistItem(
const QString& path)
333 uint linkId = d->_pathPersistMap.value( path);
336 ArnItemPersist* item = d->_itemPersistMap.value( linkId);
338 ArnItemPersist::StoreType st;
339 if (item->storeType() != st.File)
return;
341 item->setStoreType( st.DataBase);
342 setupMandatory( path, item->isMandatory());
346 void ArnPersist::doArnDestroy()
350 ArnItemPersist* item = ArnItemPersist::fromArnItem( qobject_cast<ArnItem*>( sender()));
354 d->_itemPersistMap.remove( item->linkId());
355 d->_pathPersistMap.remove( item->path());
360 void ArnPersist::doArnModeChanged(
const QString& path, uint linkId,
Arn::ObjectMode mode)
365 if (!mode.is( mode.
Save))
return;
367 if (d->_itemPersistMap.contains( linkId))
return;
370 ArnItemPersist* item = getPersistItem( path);
373 if (getDbValue( path, data, storeId)) {
375 item->arnImport( data,
false);
376 item->setStoreId( storeId);
381 QByteArray data = item->arnExport();
382 insertDbValue( path, data);
383 if (getDbId( path, storeId)) {
384 item->setStoreId( storeId);
386 item->arnImport( data,
false);
392 void ArnPersist::doArnUpdate()
396 ArnItemPersist* item;
397 item = ArnItemPersist::fromArnItem( qobject_cast<ArnItem*>( sender()));
399 ArnM::errorLog( QString(tr(
"Can't get ArnItemPersist sender for doArnUpdate")),
404 switch (item->storeType()) {
405 case ArnItemPersist::StoreType::DataBase:
408 int storeId = item->storeId();
410 updateDbValue( storeId, item->arnExport());
413 ArnM::errorLog( QString(tr(
"Can't get persist storeId for doArnUpdate: path=")) +
418 case ArnItemPersist::StoreType::File:
422 QFile file( d->_persistDir->absoluteFilePath( relPath));
423 file.open( QIODevice::WriteOnly);
424 QByteArray data = item->toByteArray();
438 if (!d->_db->isOpen()) {
439 ArnM::errorLog( QString(tr(
"DataBase required before mountPoint")),
444 if (d->_arnMountPoint)
delete d->_arnMountPoint;
446 d->_arnMountPoint =
new ArnItem(
this);
447 bool isOk = d->_arnMountPoint->openFolder( path);
449 connect( d->_arnMountPoint, SIGNAL(arnModeChanged(QString,uint,
Arn::ObjectMode)),
456 if (d->_depOffer)
delete d->_depOffer;
459 d->_depOffer->advertise(
"$Persist");
470 if (d->_query)
delete d->_query;
472 *d->_db = QSqlDatabase::addDatabase(
"QSQLITE",
"ArnPersist");
474 d->_db->setDatabaseName( dbName);
477 if (!d->_db->open()) {
478 ArnM::errorLog( QString(tr(
"DataBase open: ") + d->_db->lastError().text()),
482 d->_query =
new QSqlQuery( *d->_db);
484 int curArnDbVer = 100;
486 if (d->_db->tables().contains(
"meta"))
487 curArnDbVer = metaDbValue(
"ver",
"101").toInt();
488 bool hasStoreTable = d->_db->tables().contains(
"store");
491 if (curArnDbVer <= 100) {
492 d->_query->exec(
"CREATE TABLE meta (" 493 "attr TEXT PRIMARY KEY," 497 if (curArnDbVer <= 101)
498 setMetaDbValue(
"ver",
"102");
500 if ((curArnDbVer < 200) && hasStoreTable) {
501 d->_query->exec(
"ALTER TABLE store RENAME TO store_save");
504 if ((curArnDbVer < 200) || !hasStoreTable) {
505 d->_query->exec(
"CREATE TABLE store (" 506 "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," 511 "isMandatory INTEGER NOT NULL DEFAULT(0)," 512 "isUsed INTEGER NOT NULL DEFAULT(1))");
513 setMetaDbValue(
"ver",
"200");
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");
533 foreach (ArnItemPersist* item, d->_itemPersistMap) {
534 if (path.isEmpty() || item->path().startsWith(
fullPath)) {
537 item->bypassDelayPending();
544 QString ArnPersist::metaDbValue(
const QString& attr,
const QString& def)
548 QString retVal = def;
550 d->_query->prepare(
"SELECT value FROM meta WHERE attr = :attr");
551 d->_query->bindValue(
":attr", attr);
553 if (d->_query->next()) {
554 retVal = d->_query->value(0).toString();
564 bool ArnPersist::setMetaDbValue(
const QString& attr,
const QString& value)
568 QString curVal = metaDbValue( attr);
569 if (value == curVal)
return true;
572 d->_query->prepare(
"INSERT INTO meta (attr, value) VALUES (:attr, :value)");
574 d->_query->prepare(
"UPDATE meta SET value = :value WHERE attr = :attr");
576 d->_query->bindValue(
":attr", attr);
577 d->_query->bindValue(
":value", value);
578 bool retVal = d->_query->exec();
585 bool ArnPersist::getDbId(
const QString& path,
int& storeId)
592 d->_query->prepare(
"SELECT id, isUsed FROM store WHERE path = :path");
593 d->_query->bindValue(
":path", path);
595 if (d->_query->next()) {
596 storeId = d->_query->value(0).toInt();
597 isUsed = d->_query->value(1).toInt();
602 if (!isUsed) updateDbUsed( storeId, 1);
610 void ArnPersist::dbSetupReadValue(
const QString& meta,
const QString& valueTxt,
615 if (!value.isEmpty())
return;
617 if (!meta.isEmpty()) {
618 d->_xsm->fromXString( meta.toLatin1());
619 QByteArray variantType = d->_xsm->value(
"V");
620 if (!variantType.isEmpty()) {
622 + variantType +
":" + valueTxt.toUtf8();
626 value = valueTxt.toUtf8();
627 if (!value.isEmpty()) {
628 if (value.at(0) < 32) {
638 void ArnPersist::dbSetupWriteValue( QString& meta, QString& valueTxt, QByteArray& value)
644 if (value.isEmpty())
return;
646 uchar c = uchar( value.at(0));
648 int sepPos = value.indexOf(
':', 1);
649 Q_ASSERT(sepPos > 0);
651 QByteArray variantType( value.constData() + 1, sepPos - 1);
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();
659 valueTxt = QString::fromUtf8( value.constData() + 1, value.size() - 1);
660 value = QByteArray();
663 valueTxt = QString::fromUtf8( value.constData(), value.size());
664 value = QByteArray();
669 bool ArnPersist::getDbValue(
int storeId, QString &path, QByteArray &value)
677 d->_query->prepare(
"SELECT meta, path, valueTxt, value, isUsed FROM store WHERE id = :id");
678 d->_query->bindValue(
":id", storeId);
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();
687 dbSetupReadValue( meta, valueTxt, value);
691 if (!isUsed) updateDbUsed( storeId, 1);
696 bool ArnPersist::getDbValue(
const QString& path, QByteArray& value,
int& storeId)
704 d->_query->prepare(
"SELECT id, meta, valueTxt, value, isUsed FROM store WHERE path = :path");
705 d->_query->bindValue(
":path", path);
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();
714 dbSetupReadValue( meta, valueTxt, value);
718 if (!isUsed) updateDbUsed( storeId, 1);
723 bool ArnPersist::getDbMandatoryList( QList<int>& storeIdList)
729 d->_query->prepare(
"SELECT id FROM store WHERE isMandatory = 1");
731 while (d->_query->next()) {
732 storeIdList += d->_query->value(0).toInt();
741 bool ArnPersist::getDbList(
bool isUsed, QList<int> &storeIdList)
747 d->_query->prepare(
"SELECT id FROM store WHERE isUsed = :isUsed");
748 d->_query->bindValue(
":isUsed",
int(isUsed));
750 while (d->_query->next()) {
751 storeIdList += d->_query->value(0).toInt();
760 bool ArnPersist::insertDbValue(
const QString& path,
const QByteArray& value)
768 QByteArray value_ = value;
769 dbSetupWriteValue( meta, valueTxt, value_);
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();
784 bool ArnPersist::updateDbValue(
int storeId,
const QByteArray& value)
793 QByteArray value_ = value;
794 dbSetupWriteValue( meta, valueTxt, value_);
796 d->_query->prepare(
"UPDATE store SET meta = :meta, valueTxt = :valueTxt, value = :value " 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();
809 bool ArnPersist::updateDbUsed(
int storeId,
int isUsed)
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();
826 bool ArnPersist::updateDbMandatory(
int storeId,
int isMandatory)
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();
850 QDateTime dateTime = QDateTime::currentDateTime();
851 QString nowStr = dateTime.toString(
"yyMMdd-hhmmss");
852 arFileName = d->_archiveDir->absoluteFilePath( QString(
"persist_%1.db").arg( nowStr));
855 arFileName = d->_archiveDir->absoluteFilePath( name);
857 QString dbFileName = d->_db->databaseName();
860 return QFile::copy( dbFileName, arFileName);
864 void ArnPersist::doLoadMandatory()
866 QList<int> mandatoryList;
867 if (!getDbMandatoryList( mandatoryList))
return;
871 foreach (
int storeId, mandatoryList) {
872 if (!getDbValue( storeId, path, value))
continue;
874 ArnItemPersist::StoreType st;
875 ArnItemPersist* item = setupMandatory( path,
true);
877 if (item->storeType() != st.DataBase)
continue;
881 if (getDbValue( path, data, storeId2)) {
882 item->arnImport( data,
false);
883 item->setStoreId( storeId2);
886 item->setValue( QByteArray(),
false);
892 void ArnPersist::doLoadFiles()
897 getFileList( flist, *d->_persistDir);
898 foreach (
const QString& relPath, flist) {
904 void ArnPersist::loadFile(
const QString& relPath)
911 ArnItemPersist* item;
912 if (!d->_pathPersistMap.contains( arnPath)) {
913 item = getPersistItem( arnPath);
914 item->setDelay( 500);
915 d->_pathPersistMap.insert( arnPath, item->linkId());
918 uint linkId = d->_pathPersistMap.value( arnPath);
919 item = d->_itemPersistMap.value( linkId);
922 item->setStoreType( ArnItemPersist::StoreType::File);
924 QFile file( d->_persistDir->absoluteFilePath( relPath));
925 file.open( QIODevice::ReadOnly);
926 QByteArray data = file.readAll();
932 void ArnPersist::getFileList(QStringList& flist,
const QDir& dir,
const QDir* baseDir)
936 QString path = dir.absolutePath();
939 foreach( QFileInfo finfo, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files)) {
940 if (finfo.isFile()) {
941 flist += baseDir->relativeFilePath( finfo.absoluteFilePath());
943 else if (finfo.isDir()) {
944 QString fname = finfo.fileName();
945 getFileList( flist, QDir(finfo.absoluteFilePath()), baseDir);
951 void ArnPersist::setupSapi( ArnPersistSapi* sapi)
954 sapi->open( QString(), Mode::Provider);
955 sapi->batchConnect(
ARN_RegExp(
"^pv_(.+)"),
this,
"sapi\\1", Mode());
959 void ArnPersist::destroyRpc()
969 void ArnPersist::convertFileList( QStringList& files,
Arn::NameF nameF)
971 for (
int i = 0; i < files.size(); ++i) {
977 void ArnPersist::vcsCheckoutR()
984 void ArnPersist::sapiFlush(
const QString& path)
988 bool isOk =
flush( path);
989 emit d->_sapiCommon->rq_flushR( isOk, path);
993 void ArnPersist::sapiTest(
const QString& str,
int i)
995 ArnPersistSapi* sapiSender = qobject_cast<ArnPersistSapi*>( sender());
997 qDebug() <<
"sapiTest sender=" << sapiSender->pipePath();
998 sapiSender->sendText(
"sapiTest: str=" + str +
" int=" + QString::number(i));
1000 qDebug() <<
"----- sapiTest: str=" << str <<
" int=" << i;
1004 void ArnPersist::sapiLoad()
1010 void ArnPersist::sapiLs(
const QString& path)
1015 getFileList( flist, *d->_persistDir);
1019 emit d->_sapiCommon->rq_lsR( flist);
1021 QStringList retList;
1022 foreach( QString fstr, flist) {
1023 if (fstr.startsWith( path))
1026 emit d->_sapiCommon->rq_lsR( retList);
1031 void ArnPersist::sapiRm(
const QString& path)
1039 getFileList( flist, *d->_persistDir);
1040 foreach (
const QString& delPath, flist) {
1041 if (delPath.startsWith( relPath)) {
1042 isOk &= d->_persistDir->remove( delPath);
1051 isOk = d->_persistDir->remove( relPath);
1052 removeFilePersistItem( path);
1058 emit d->_sapiCommon->rq_rmR( isOk);
1062 void ArnPersist::sapiTouch(
const QString& path)
1066 if (path.endsWith(
'/'))
return;
1070 QString filePath = d->_persistDir->absoluteFilePath( relPath);
1072 isOk &= d->_persistDir->mkpath( QFileInfo( filePath).dir().path());
1075 QFile file( filePath);
1076 isOk &= file.open( QIODevice::ReadWrite);
1081 emit d->_sapiCommon->rq_touchR( isOk);
1085 void ArnPersist::sapiDbMandatory(
const QString& path,
bool isMandatory)
1092 QList<int> storeIdList;
1093 if (!getDbList(
true, storeIdList)) {
1094 emit d->_sapiCommon->rq_dbMandatoryR( isOk);
1100 foreach (
int storeId, storeIdList) {
1101 if (!getDbValue( storeId, pathDb, value))
continue;
1102 if (!pathDb.startsWith( path))
continue;
1104 if (updateDbMandatory( storeId, isMandatory)) {
1105 setupMandatory( pathDb, isMandatory);
1113 if (getDbId( path, storeId)) {
1114 if (updateDbMandatory( storeId, isMandatory)) {
1115 setupMandatory( path, isMandatory);
1121 emit d->_sapiCommon->rq_dbMandatoryR( isOk);
1125 void ArnPersist::sapiDbMandatoryLs(
const QString& path)
1129 QStringList retList;
1130 QList<int> storeIdList;
1131 if (!getDbMandatoryList( storeIdList)) {
1132 emit d->_sapiCommon->rq_dbMandatoryLsR( retList);
1138 foreach (
int storeId, storeIdList) {
1139 if (!getDbValue( storeId, pathDb, value))
continue;
1140 if (!pathDb.startsWith( path))
continue;
1145 emit d->_sapiCommon->rq_dbMandatoryLsR( retList);
1149 void ArnPersist::sapiDbLs(
const QString& path,
bool isUsed)
1153 QStringList retList;
1154 QList<int> storeIdList;
1155 if (!getDbList( isUsed, storeIdList)) {
1156 emit d->_sapiCommon->rq_dbLsR( retList);
1162 foreach (
int storeId, storeIdList) {
1163 if (!getDbValue( storeId, pathDb, value))
continue;
1164 if (!pathDb.startsWith( path))
continue;
1169 emit d->_sapiCommon->rq_dbLsR( retList);
1173 void ArnPersist::sapiDbMarkUnused(
const QString& path)
1177 QList<int> storeIdList;
1178 if (!getDbList(
true, storeIdList)) {
1179 emit d->_sapiCommon->rq_dbMarkUnusedR(
false);
1185 foreach (
int storeId, storeIdList) {
1186 if (!getDbValue( storeId, pathDb, value))
continue;
1187 if (!pathDb.startsWith( path))
continue;
1189 if (!updateDbUsed( storeId,
false)) {
1190 emit d->_sapiCommon->rq_dbMarkUnusedR(
false);
1194 emit d->_sapiCommon->rq_dbMarkUnusedR(
true);
1198 void ArnPersist::sapiInfo()
1202 emit d->_sapiCommon->rq_infoR(
"Arn Persist",
"1.0");
void setValue(const ArnBasicItem &other, int ignoreSame=Arn::SameValue::DefaultAction)
ArnPersist(QObject *parent=arnNullptr)
Path: "/@/test" ==> "//test", Item: "@" ==> "".
Class for handling persistent Arn Data object.
Class for advertising that a service is available.
Container class with string representation for serialized data.
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.
Assigning same value generates an update of the Arn Data Object
void setArchiveDir(const QString &path)
Set the persistent database backup directory.
bool setMountPoint(const QString &path)
Set the persistent enabled tree path.
Only on path, no effect on discrete names. "/test/value" ==> "test/value".
void setVcs(ArnVcs *vcs)
Set the Version Control System to be used.
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.
QString convertPath(const QString &path, Arn::NameF nameF)
Convert a path to a specific format.
void setPersistDir(const QString &path)
Set the persistent file directory root
QString parentPath(const QString &path)
Get the parent to a given path
bool doArchive(const QString &name=QString())
Do a persistent database backup.
bool flush(const QString &path=QString())
Save any pending values now.
static void errorLog(QString errText, ArnError err=ArnError::Undef, void *reference=arnNullptr)
bool isFolderPath(const QString &path)
Test if path is a folder path
Data is persistent and will be saved.
Handle for an Arn Data Object.
bool isProviderPath(const QString &path)
Test if path is a provider path