ArnLib  4.0.x
Active Registry Network
ArnM.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/ArnM.hpp"
33 #include "ArnInc/ArnLib.hpp"
34 #include "ArnInc/ArnEvent.hpp"
35 #include "ArnLink.hpp"
36 #include <QMutex>
37 #include <QWaitCondition>
38 #include <QThreadStorage>
39 #include <QThread>
40 #include <QCoreApplication>
41 #include <QMetaType>
42 #include <QMetaObject>
43 #include <QMetaEnum>
44 #include <QFile>
45 #include <QDir>
46 #include <iostream>
47 #include <QTimer>
48 #include <QDateTime>
49 #include <QStringList>
50 #include <QVector>
51 #include <QDebug>
52 
53 
55 
57 class ArnThreadComStorage : public QThreadStorage<ArnThreadCom*> {};
59 
60 
61 ArnThreadComStorage* ArnThreadCom::getThreadComStorage()
62 {
63  static ArnThreadComStorage threadComStorage;
64 
65  return &threadComStorage;
66 }
67 
68 
69 ArnThreadCom* ArnThreadCom::getThreadCom()
70 {
71  ArnThreadComStorage* threadComStorage = getThreadComStorage();
72  if (!threadComStorage->hasLocalData()) {
73  threadComStorage->setLocalData( new ArnThreadCom);
74  }
75  return threadComStorage->localData();
76 }
77 
78 
79 ArnThreadComCaller::ArnThreadComCaller()
80 {
81  _p = ArnThreadCom::getThreadCom();
82  if (Arn::debugThreading) qDebug() << "ThreadComCaller start p=" << _p;
83  _p->_mutex.lock();
84 }
85 
86 
87 ArnThreadComCaller::~ArnThreadComCaller()
88 {
89  _p->_mutex.unlock();
90  if (Arn::debugThreading) qDebug() << "ThreadComCaller end p=" << _p;
91 }
92 
93 
94 void ArnThreadComCaller::waitCommandEnd()
95 {
96  _p->_commandEnd.wait( &_p->_mutex); // Wait main-thread Proxy is finished
97 }
98 
99 
100 ArnThreadCom* ArnThreadComCaller::p()
101 {
102  return _p;
103 }
104 
105 
106 ArnThreadComProxyLock::ArnThreadComProxyLock( ArnThreadCom* threadCom) :
107  _p( threadCom)
108 {
109  if (Arn::debugThreading) qDebug() << "ThreadComProxy start p=" << _p;
110  _p->_mutex.lock(); // Sync caller waiting
111  _p->_mutex.unlock();
112 }
113 
114 
115 ArnThreadComProxyLock::~ArnThreadComProxyLock()
116 {
117  _p->_commandEnd.wakeOne(); // wake up caller
118  if (Arn::debugThreading) qDebug() << "ThreadComProxy end p=" << _p;
119 }
120 
121 
123 
124 
125 int ArnM::_countFolder(0);
126 int ArnM::_countLeaf(0);
127 QAtomicInt ArnM::_countRef(0);
128 
129 
131 ArnM::ArnM()
132 {
133  ArnLink::arnM( this);
134 
135  _countFolder = 0;
136  _countLeaf = 0;
137  _countRef = 0;
138  _countFolderLink = arnNullptr;
139  _countLeafLink = arnNullptr;
140  _countRefLink = arnNullptr;
141  _timerMetrics = new QTimer( this);
142 
143  _defaultIgnoreSameValue = false;
144  _skipLocalSysLoading = false;
145  _isThreadedApp = false;
146  _mainThread = QThread::currentThread();
147  _root = new ArnLink( arnNullptr, "", Arn::LinkFlags::Folder);
148 
149 #if QT_VERSION >= 0x050a00
150 #else
151  qsrand( uint(QDateTime::currentMSecsSinceEpoch()));
152 #endif
153 
154  qRegisterMetaType<ArnThreadCom*>();
155  qRegisterMetaType<ArnLinkHandle>("ArnLinkHandle");
156  qRegisterMetaType<QVariant>("QVariant");
157 
159  _consoleError = true;
160  _errorLogger = arnNullptr;
161 
162  _errTextTab.resize( ArnError::Err_N);
163  _errTextTab[ ArnError::Ok] = QString(tr("Ok"));
164  _errTextTab[ ArnError::Warning] = QString(tr("Warning"));
165  _errTextTab[ ArnError::CreateError] = QString(tr("Can't create"));
166  _errTextTab[ ArnError::NotFound] = QString(tr("Not found"));
167  _errTextTab[ ArnError::NotOpen] = QString(tr("Not open"));
168  _errTextTab[ ArnError::AlreadyExist] = QString(tr("Already exist"));
169  _errTextTab[ ArnError::AlreadyOpen] = QString(tr("Already open"));
170  _errTextTab[ ArnError::FolderNotOpen] = QString(tr("Folder is not open"));
171  _errTextTab[ ArnError::ItemNotOpen] = QString(tr("Item is not open"));
172  _errTextTab[ ArnError::ItemNotSet] = QString(tr("Item is not set"));
173  _errTextTab[ ArnError::Retired] = QString(tr("Access to retired"));
174  _errTextTab[ ArnError::NotMainThread] = QString(tr("Not main thread"));
175  _errTextTab[ ArnError::ConnectionError] = QString(tr("Connection error"));
176  _errTextTab[ ArnError::RecUnknown] = QString(tr("Unknown record type"));
177  _errTextTab[ ArnError::ScriptError] = QString(tr("Script"));
178  _errTextTab[ ArnError::RpcInvokeError] = QString(tr("Rpc Invoke error"));
179  _errTextTab[ ArnError::RpcReceiveError] = QString(tr("Rpc Receive error"));
180  _errTextTab[ ArnError::LoginBad] = QString(tr("Login error"));
181  _errTextTab[ ArnError::RecNotExpected] = QString(tr("Not expected record type here"));
182  _errTextTab[ ArnError::OpNotAllowed] = QString(tr("Operation denied, no privilege"));
183 
184  if (Arn::debugSizes) {
185  qDebug() << "====== Arn Sizes ======";
186  qDebug() << "QObject: " << sizeof(QObject);
187  qDebug() << " QScopedPtr: " << sizeof(QScopedPointer<QObjectData>);
188  qDebug() << " QObjectData: " << sizeof(QObjectData);
189  qDebug() << " QObjectList: " << sizeof(QObjectList);
190  qDebug() << "QString: " << sizeof(QString);
191  qDebug() << "QVariant: " << sizeof(QVariant);
192  qDebug() << "ArnLink: " << sizeof(ArnLink);
193  qDebug() << " AnrLinkList: " << sizeof(ArnLinkList);
194  qDebug() << "ArnItemB: " << sizeof(ArnItemB);
195  qDebug() << "ArnItem: " << sizeof(ArnItem);
196  qDebug() << "DataType: " << sizeof(Arn::DataType);
197  qDebug() << "DataType::E: " << sizeof(Arn::DataType::E);
198  qDebug() << "=======================";
199  }
200 
201  QTimer::singleShot( 0, this, SLOT(postSetup()));
202 }
203 
204 
205 void ArnM::postSetup()
206 {
207  QString legalPath = Arn::pathLocalSys + "Legal/";
208 
209  int lgplStat = 0;
210 #if defined(ARNLIB_COMPILE)
211  lgplStat = 1;
212 #endif
213  setValue( legalPath + "ArnLib_LGPL/value", lgplStat);
214  setValue( legalPath + "ArnLib_LGPL/set", "0=Seemes_Ok 1=Not_Ok,_statically_linked_to_application");
215 
216  QString metricPath = Arn::pathLocalSys + "Metric/";
217  _countFolderLink = ArnM::link( metricPath + "ObjectFolders/value", Arn::LinkFlags::CreateAllowed);
218  _countLeafLink = ArnM::link( metricPath + "ObjectLeaves/value", Arn::LinkFlags::CreateAllowed);
219  _countRefLink = ArnM::link( metricPath + "ObjectRef/value", Arn::LinkFlags::CreateAllowed);
220  _timerMetrics->start( 5000);
221  connect( _timerMetrics, SIGNAL(timeout()), this, SLOT(onTimerMetrics()));
222  onTimerMetrics();
223 
224  if (_skipLocalSysLoading) return;
225 
227  QString licensesPath = legalPath + "Licenses/";
228  QDir dirArnRoot( Arn::resourceArnRoot);
229  loadFromDirRoot( licensesPath + "LICENSE_ARNLIB.txt", dirArnRoot, Arn::Coding::Text);
230  loadFromDirRoot( licensesPath + "LICENSE_LGPL.txt", dirArnRoot, Arn::Coding::Text);
231  loadFromDirRoot( licensesPath + "LGPL_EXCEPTION.txt", dirArnRoot, Arn::Coding::Text);
232  loadFromDirRoot( licensesPath + "LICENSE_GPL3.txt", dirArnRoot, Arn::Coding::Text);
233  loadFromDirRoot( licensesPath + "LICENSE_MDNS.txt", dirArnRoot, Arn::Coding::Text);
234  loadFromDirRoot( licensesPath + "LICENSE_APACHE2.txt", dirArnRoot, Arn::Coding::Text);
235 }
236 
237 
238 ArnM::~ArnM()
239 {
240  // Should never be used;
241 }
242 
243 
244 int ArnM::valueInt( const QString& path)
245 {
246  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
247  if (!link) return 0;
248 
249  int retVal = link->toInt();
250  link->deref();
251  return retVal;
252 }
253 
254 
255 double ArnM::valueDouble( const QString& path)
256 {
257  return valueReal( path);
258 }
259 
260 
261 ARNREAL ArnM::valueReal(const QString& path)
262 {
263  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
264  if (!link) return 0;
265 
266  ARNREAL retVal = link->toReal();
267  link->deref();
268  return retVal;
269 }
270 
271 
272 QString ArnM::valueString( const QString& path)
273 {
274  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
275  if (!link) return QString();
276 
277  QString retVal = link->toString();
278  link->deref();
279  return retVal;
280 }
281 
282 
283 QByteArray ArnM::valueByteArray( const QString& path)
284 {
285  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
286  if (!link) return QByteArray();
287 
288  QByteArray retVal = link->toByteArray();
289  link->deref();
290  return retVal;
291 }
292 
293 
294 QVariant ArnM::valueVariant( const QString& path)
295 {
296  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
297  if (!link) return QVariant();
298 
299  QVariant retVal = link->toVariant();
300  link->deref();
301  return retVal;
302 }
303 
304 
305 void ArnM::itemsProxy( ArnThreadCom* threadCom, const QString& path)
306 {
307  ArnThreadComProxyLock proxyLock( threadCom);
308 
309  if (Arn::debugThreading) qDebug() << "itemsProxy: path=" << path;
310  threadCom->_retStringList = itemsMain( path);
311  if (Arn::debugThreading) qDebug() << "itemsProxy: waking thread";
312 }
313 
314 
315 QStringList ArnM::items( const QString& path)
316 {
317  if (isMainThread()) {
318  return itemsMain( path);
319  }
320  else { // Threaded - must be threadsafe
321  ArnThreadComCaller threadCom;
322 
323  threadCom.p()->_retStringList.clear(); // Just in case ...
324  if (Arn::debugThreading) qDebug() << "items-thread: start path=" << path;
325  QMetaObject::invokeMethod( &instance(),
326  "itemsProxy",
327  Qt::QueuedConnection,
328  Q_ARG( ArnThreadCom*, threadCom.p()),
329  Q_ARG( QString, path));
330  threadCom.waitCommandEnd(); // Wait main-thread gives retStringList
331  QStringList retStringList = threadCom.p()->_retStringList;
332  threadCom.p()->_retStringList.clear(); // Manually release memory
333  if (Arn::debugThreading) qDebug() << "items-thread: end stringList=" << threadCom.p()->_retStringList;
334 
335  return retStringList;
336  }
337 }
338 
339 
340 QStringList ArnM::itemsMain( const ArnLink *parent)
341 {
342  if (!parent) {
343  qWarning() << "items: empty path";
344  return QStringList();
345  }
346 
347  const ArnLinkList& children = parent->children();
348  QStringList childnames;
349 
350  for (int i = 0; i < children.size(); i++) {
351  ArnLink* childLink = children.at(i);
352 
353  if (childLink != arnNullptr) {
354  if (childLink->isFolder()) {
355  childnames << childLink->objectName() + "/";
356  }
357  else {
358  childnames << childLink->objectName();
359  }
360  }
361  }
362 
363  return childnames;
364 }
365 
366 
367 QStringList ArnM::itemsMain( const QString& path)
368 {
369  ArnLink* link = ArnM::link( path, Arn::LinkFlags::Folder);
370  QStringList retVal = itemsMain( link);
371  if (link) {
372  link->deref();
373  }
374 
375  return retVal;
376 }
377 
378 
380 {
381  QThread* mainThread = instance()._mainThread;
382  if (Arn::debugThreading) {
383  qDebug() << "isMainThread: appThr=" << QCoreApplication::instance()->thread()
384  << " mainThr=" << mainThread
385  << " curThr=" << QThread::currentThread()
386  << " ArnMThr=" << instance().thread();
387  }
388  if (QThread::currentThread() != mainThread) {
389  if (!instance()._isThreadedApp) instance()._isThreadedApp = true;
390  return false;
391  }
392  return true;
393 }
394 
395 
397 {
398  return instance()._isThreadedApp;
399 }
400 
401 
402 bool ArnM::exist( const QString &path)
403 {
404  Arn::LinkFlags flags;
405  ArnLink* link = ArnM::link( path, flags.SilentError);
406 
407  if (!link) return false;
408  link->deref();
409  return true;
410 }
411 
412 
413 bool ArnM::isFolder( const QString& path)
414 {
415  Arn::LinkFlags flags;
416  ArnLink* link = ArnM::link( path, flags.Folder | flags.SilentError);
417 
418  if (!link) return false;
419  link->deref();
420  return true;
421 }
422 
423 
424 bool ArnM::isLeaf( const QString& path)
425 {
426  if (Arn::isFolderPath( path)) return false;
427 
428  ArnLink* link = ArnM::link( path, Arn::LinkFlags::SilentError);
429 
430  if (!link) return false;
431  link->deref();
432  return true;
433 }
434 
435 
436 void ArnM::setAtomicOpProvider( const QString& path)
437 {
438  if (Arn::isFolderPath( path)) return;
439 
440  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
441 
442  if (link) {
443  link->setAtomicOpProvider( true);
444  link->deref();
445  }
446 }
447 
448 
449 bool ArnM::isAtomicOpProvider( const QString& path)
450 {
451  if (Arn::isFolderPath( path)) return false;
452 
453  ArnLink* link = ArnM::link( path, Arn::LinkFlags::SilentError);
454 
455  if (!link) return false;
456  bool retVal = link->isAtomicOpProvider();
457  link->deref();
458  return retVal;
459 }
460 
461 
462 void ArnM::setValue( const QString& path, const QString& value)
463 {
464  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
465 
466  if (link) {
467  link->setValue( value);
468  link->deref();
469  }
470 }
471 
472 
473 void ArnM::setValue( const QString& path, int value)
474 {
475  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
476 
477  if (link) {
478  link->setValue( value);
479  link->deref();
480  }
481 }
482 
483 
484 void ArnM::setValue( const QString& path, ARNREAL value)
485 {
486  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
487 
488  if (link) {
489  link->setValue( value);
490  link->deref();
491  }
492 }
493 
494 
495 void ArnM::setValue( const QString& path, const QByteArray& value)
496 {
497  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
498 
499  if (link) {
500  link->setValue( value);
501  link->deref();
502  }
503 }
504 
505 
506 void ArnM::setValue( const QString& path, const QVariant& value, const char* typeName)
507 {
508  ArnLink* link = ArnM::link( path, Arn::LinkFlags::CreateAllowed);
509 
510  if (link) {
511  int valueType = 0;
512  if (typeName && *typeName)
513  valueType = QMetaType::type( typeName);
514 
515  if (!valueType)
516  link->setValue( value);
517  else {
518  QVariant val = value;
519  if (val.convert( QVariant::Type( valueType))) {
520  link->setValue( val);
521  }
522  else {
523  errorLog( QString(tr("Can't convert variant', Path:")) + path + " type=" + typeName,
525  }
526  }
527  link->deref();
528  }
529 }
530 
531 
532 void ArnM::setValue( const QString& path, const char* value)
533 {
534  setValue( path, QString::fromUtf8( value));
535 }
536 
537 
538 bool ArnM::loadFromFile( const QString& path, const QString& fileName, Arn::Coding coding)
539 {
540  bool isText = coding.is( coding.Text);
541 
542  QFile file( fileName);
543  if (!file.open( QIODevice::ReadOnly)) return false;
544  file.setTextModeEnabled( isText);
545  QByteArray data = file.readAll();
546 
547  if (isText)
548  ArnM::setValue( path, QString::fromUtf8( data.constData(), data.size()));
549  else
550  ArnM::setValue( path, data);
551 
552  return true;
553 }
554 
555 
556 bool ArnM::loadFromDirRoot( const QString& path, const QDir& dirRoot, Arn::Coding coding)
557 {
558  QString arnFullPath = Arn::fullPath( path);
559  QString fileAbsPath = dirRoot.absoluteFilePath( Arn::convertPath( arnFullPath, Arn::NameF::Relative));
560 
561  return loadFromFile( arnFullPath, fileAbsPath, coding);
562 }
563 
564 
565 bool ArnM::saveToFile( const QString& path, const QString& fileName, Arn::Coding coding)
566 {
567  bool isText = coding.is( coding.Text);
568 
569  QFile file( fileName);
570  if (!file.open( QIODevice::WriteOnly)) return false;
571  file.setTextModeEnabled( isText);
572 
573  QByteArray data = ArnM::valueByteArray( path);
574  return (file.write( data) >= 0);
575 }
576 
577 
578 #ifndef DOXYGEN_SKIP
579 // Must onlty be called fronm main thread (application)
580 ArnLink* ArnM::root()
581 {
582  return instance()._root;
583 }
584 
585 
586 void ArnM::linkProxy( ArnThreadCom* threadCom, const QString& path, int flagValue, int syncMode)
587 {
588  ArnThreadComProxyLock proxyLock( threadCom);
589 
590  if (Arn::debugThreading) qDebug() << "linkProxy: path=" << path;
591  threadCom->_retObj = linkMain( path, Arn::LinkFlags::fromInt( flagValue),
592  Arn::ObjectSyncMode::fromInt( syncMode));
593  if (Arn::debugThreading) qDebug() << "linkProxy: waking thread";
594 }
595 
596 
597 ArnLink* ArnM::link( const QString& path, Arn::LinkFlags flags, Arn::ObjectSyncMode syncMode)
598 {
599  if (isMainThread()) return linkMain( path, flags, syncMode);
600  else return linkThread( path, flags, syncMode);
601 }
602 
603 
605 ArnLink* ArnM::linkThread( const QString& path, Arn::LinkFlags flags, Arn::ObjectSyncMode syncMode)
606 {
607  flags.set( flags.Threaded);
608 
609  ArnThreadComCaller threadCom;
610 
611  threadCom.p()->_retObj = arnNullptr; // Just in case ...
612  if (Arn::debugThreading) qDebug() << "link-thread: start path=" << path;
613  QMetaObject::invokeMethod( &instance(),
614  "linkProxy",
615  Qt::QueuedConnection,
616  Q_ARG( ArnThreadCom*, threadCom.p()),
617  Q_ARG( QString, path), Q_ARG( int, flags.toInt()),
618  Q_ARG( int, syncMode.toInt()));
619  if (Arn::debugThreading) qDebug() << "link-thread: invoked path=" << path;
620  threadCom.waitCommandEnd(); // Wait main-thread gives retLink
621  ArnLink* retLink = static_cast<ArnLink*>( threadCom.p()->_retObj);
622  if (retLink) if (Arn::debugThreading) qDebug() << "link-thread: end path=" << retLink->linkPath();
623 
624  return retLink;
625 }
626 
627 
628 ArnLink* ArnM::linkMain( const QString& path, Arn::LinkFlags flags, Arn::ObjectSyncMode syncMode)
629 {
630  // qDebug() << "### link-main: path=" << path;
631  QString pathNorm = Arn::fullPath( path);
632  if (pathNorm.endsWith("/")) {
633  flags.set( flags.Folder);
634  pathNorm.resize( pathNorm.size() - 1); // Remove '/' at end (Also root become "")
635  }
636 
637  ArnLink* currentLink = root();
638  QStringList pathlist = pathNorm.split("/");
639  QString growPath = "/";
640  int pathListSize = pathlist.size();
641 
642  for (int i = 1; i < pathListSize; ++i) {
643  Arn::LinkFlags subFlags;
644  subFlags.f = flags.f | flags.flagIf( i < pathListSize - 1, flags.Folder);
645  subFlags.set( flags.LastLink, i == pathListSize - 1);
646  QString subPath = pathlist.at(i);
647 
648  growPath += subPath;
649  if (subFlags.is( subFlags.Folder))
650  growPath += "/";
651 
652  currentLink = ArnM::linkMain( growPath, currentLink, subPath, subFlags, syncMode);
653  if (currentLink == arnNullptr) {
654  return arnNullptr;
655  }
656  }
657 
658  currentLink->ref();
659  return currentLink;
660 }
661 
662 
663 ArnLink* ArnM::linkMain( const QString& path, ArnLink *parent, const QString& name, Arn::LinkFlags flags,
664  Arn::ObjectSyncMode syncMode)
665 {
666  if (!parent) { // No parent (folder) error
667  if (!flags.is( flags.SilentError)) {
668  errorLog( QString(tr("Can't handle SubItem:")) + name,
670  }
671  return arnNullptr;
672  }
673 
674  QString nameNorm = name;
675  if (nameNorm.endsWith("/")) {
676  flags.set( flags.Folder);
677  nameNorm.resize( nameNorm.size() - 1); // Remove '/' at end
678  }
679 
680  ArnLink* child;
681  child = getRawLink( parent, nameNorm, flags);
682  if (!child) { // Error getting link
683  return arnNullptr;
684  }
685 
686  if (!flags.is( flags.Folder)
687  && nameNorm.endsWith('!')
688  && flags.is( flags.CreateAllowed)) {
689  // Make sure a provider link has a twin, ie a value link
690  addTwinMain( path, child, syncMode, flags);
691  }
692 
693  child->setupEnd( path, syncMode, flags);
694  return child;
695 }
696 
697 
698 ArnLink* ArnM::addTwin( const QString& path, ArnLink* link,
699  Arn::ObjectSyncMode syncMode, Arn::LinkFlags flags)
700 {
701  if (!link) return arnNullptr;
702 
703  if (isMainThread()) {
704  ArnLink* retLink = addTwinMain( path, link, syncMode, flags);
705  if (retLink) retLink->ref();
706  return retLink;
707  }
708 
710  if (!link->twinLink()) { // This link has no twin, create one
711  ArnLink* parent = link->parent();
712  QString twinPath = parent->linkPath() + link->twinName();
713  return linkThread( twinPath, flags.f | flags.CreateAllowed, syncMode);
715  }
716 
717  return link->twinLink();
718 }
719 
720 
721 ArnLink* ArnM::addTwinMain( const QString& path, ArnLink* link,
722  Arn::ObjectSyncMode syncMode, Arn::LinkFlags flags)
723 {
724  if (!link) {
725  return arnNullptr;
726  }
727 
728  if (!link->twinLink()) { // This link has no twin, create one
729  QString twinName = link->twinName();
730  ArnLink* parent = link->parent();
731  ArnLink* twinLink;
732  twinLink = getRawLink( parent, twinName, flags.f | flags.CreateAllowed);
733  // qDebug() << "addTwin: parent=" << parent->linkPath() << " twinName=" << twinName;
734  if (twinLink) { // if twin ok, setup cross links betwen value & provider
735  bool isThreaded = false;
736  if (link->isThreaded() // If anything indicates threaded, set twins as threded
737  || twinLink->isThreaded()
738  || flags.is( flags.Threaded)) {
739  isThreaded = true;
740  link->setThreaded();
741  twinLink->setThreaded();
742  link->lock();
743  twinLink->lock();
744  }
745  twinLink->_twin = link;
746  link->_twin = twinLink;
747  if (isThreaded) {
748  link->unlock();
749  twinLink->unlock();
750  }
751  flags.set( flags.LastLink); // A twin must be last link (also a leaf)
752  twinLink->setupEnd( Arn::twinPath( path), syncMode, flags);
753  link->doModeChanged(); // This is now Bidirectional mode
754  }
755  }
756 
757  return link->twinLink();
758 }
759 #endif
760 
761 
762 ArnLink* ArnM::getRawLink( ArnLink *parent, const QString& name, Arn::LinkFlags flags)
763 {
764  bool showErrors = !flags.is( flags.SilentError);
765  if (!parent) { // No parent (folder) error
766  if (showErrors) {
767  errorLog( QString(tr("Can't handle SubItem:")) + name,
769  }
770  return arnNullptr;
771  }
772  if (parent->isRetired()) {
773  if (showErrors) {
774  errorLog( QString(tr("parent:")) + parent->linkPath(),
776  }
777  return arnNullptr;
778  }
779 
780  ArnLink *child = parent->findLink( name);
781 
782  if (child == arnNullptr) { // link not existing, create it ?
783  if (!flags.is( flags.CreateAllowed)) {
784  // Creating new items are not allowed
785  if (showErrors) {
786  errorLog( QString(tr("Path:")) + parent->linkPath() +
787  QString(tr(" Item:")) + name,
789  }
790  return arnNullptr;
791  }
792  if (name.isEmpty() && !flags.is( flags.Folder)) {
793  // Empty names only allowed for folders
794  if (showErrors) {
795  errorLog( QString(tr("Empty leaf name, Path:")) + parent->linkPath(),
797  }
798  return arnNullptr;
799  }
800  if (name.endsWith("!!")) {
801  // Invalid, must not have double '!' at end
802  if (showErrors) {
803  errorLog( QString(tr("Invalid name, Path:")) + parent->linkPath(),
805  }
806  return arnNullptr;
807  }
808  // Create folders or items when needed
809  child = new ArnLink( parent, name, flags);
810  if (flags.is( flags.Folder))
811  ++_countFolder;
812  else
813  ++_countLeaf;
814  }
815  else {
816  if (child->isRetired()) {
817  if (showErrors) {
818  errorLog( QString(tr("child:")) + child->linkPath(),
820  }
821  return arnNullptr;
822  }
823  if (child->isFolder() != flags.is( flags.Folder)) {
824  // There is already a link with this name, but it is of
825  // the wrong type. This is an error.
826  if (showErrors) {
827  if (flags.is( flags.Folder)) {
828  errorLog( QString(tr("Is not folder, Path:")) + child->linkPath(),
830  }
831  else {
832  errorLog( QString(tr("Is folder, Path:")) + child->linkPath(),
834  }
835  }
836  return arnNullptr;
837  }
838  }
840  if (flags.is( flags.Threaded)) {
841  if (child) {
842  child->setThreaded();
843  if (child->_twin) {
844  child->_twin->setThreaded();
845  }
846  }
847  }
848 
849  return child;
850 }
851 
852 
853 void ArnM::destroyLink( ArnLink* link, bool isGlobal)
854 {
855  if (!link) return;
856 
857  if (isMainThread()) {
858  if (Arn::debugLinkDestroy) qDebug() << "destroyLink-mainA: start path=" << link->linkPath();
859  destroyLinkMain( link, link, isGlobal);
860  return;
861  }
862 
864  destroyLink( link->linkPath(), isGlobal);
865 }
866 
867 
868 void ArnM::destroyLink( const QString& path, bool isGlobal)
869 {
870  if (isMainThread()) {
871  Arn::LinkFlags flags;
872  ArnLink* link = ArnM::link( path, flags.SilentError);
873  if (link) {
874  link->deref(); // Ok, as this is main thread. Avoid locking this link
875  if (Arn::debugLinkDestroy) qDebug() << "destroyLink-mainB: start path=" << link->linkPath();
876  destroyLinkMain( link, link, isGlobal);
877  }
878  return;
879  }
880 
882  if (Arn::debugThreading) qDebug() << "destroyLink-thread: start path=" << path;
883  QMetaObject::invokeMethod( &instance(),
884  "destroyLink",
885  Qt::QueuedConnection,
886  Q_ARG( QString, path),
887  Q_ARG( bool, isGlobal));
888 }
889 
890 
892 void ArnM::destroyLinkMain( ArnLink* link, ArnLink* startLink, bool isGlobal)
893 {
894  if (!link) return;
895  if (link->isRetired()) return; // This link is already retired
896 
898  ArnLink::RetireType rt;
899  rt = startLink->isFolder() ? rt.Tree
900  : isGlobal ? rt.LeafGlobal
901  : rt.LeafLocal;
902  ArnLink* twin = link->twinLink();
903  link->setRetired( rt);
904  if (twin)
905  twin->setRetired( rt);
906  link->ref(); // At least one ref to protect this link
907 
909  for (int i = 0; i < link->children().size();) {
910  ArnLink* dLink = link->children().at( i);
911  if (dLink->isRetired())
912  ++i;
913  else
914  destroyLinkMain( dLink, startLink, isGlobal);
915  }
916 
918  link->doRetired( startLink, isGlobal);
919  if (twin)
920  twin->doRetired( startLink, isGlobal);
921  link->deref(); // Remove link protection, can also trigger zero-ref event
923 }
924 
925 
926 void ArnM::doZeroRefLink( ArnLink* link)
927 {
928  if (!link) return;
929  link->decZeroRefs();
930  if (!link->isLastZeroRef()) return; // Link reused & more zeroRefs will come
931 
932  link->setRefCount( -1); // Mark link as fully de-referenced
933  // qDebug() << "ZeroRef: set fully deref path=" << link->linkPath();
934 
935  while (link->isRetired() &&
936  link->refCount() < 0 &&
937  link->children().size() == 0) {
938  ArnLink* parent = link->parent();
939  if (Arn::debugLinkDestroy) qDebug() << "ZeroRef: delete link path=" << link->linkPath();
940 
941  if (link->isFolder())
942  --_countFolder;
943  else {
944  --_countLeaf;
945  if (link->isBiDirMode())
946  --_countLeaf;
947  }
948  delete link; // This will also delete an existing twin
949 
950  link = parent;
951  }
952 }
953 
954 
955 void ArnM::changeRefCounter( int step)
956 {
957  _countRef.fetchAndAddRelaxed( step);
958 }
959 
960 
962 {
963  return QString(tr("Arn"));
964 }
965 
966 
967 QByteArray ArnM::info()
968 {
969  return QByteArray("Name=ArnLib Ver=" ARNLIBVER " Date=" ARNBUILDDATE " Time=" ARNBUILDTIME);
970 }
971 
972 
973 void ArnM::setupErrorlog( QObject* errLog)
974 {
975  if (!errLog) return;
976 
977  instance()._errorLogger = errLog;
978  setConsoleError( false);
979 
981  QMetaObject::invokeMethod( errLog,
982  "setSysName",
983  Qt::QueuedConnection, // Thread safe
984  Q_ARG( QString, errorSysName()));
985 
987  for (int code = 0; code < instance()._errTextTab.size(); ++code) {
988  QMetaObject::invokeMethod( errLog,
989  "setErrorCode",
990  Qt::QueuedConnection, // Thread safe
991  Q_ARG( uint, uint( code)),
992  Q_ARG( QString, instance()._errTextTab.at( code)));
993  }
994 
996  connect( &instance(), SIGNAL(errorLogSig(QString,uint,void*)),
997  errLog, SLOT(add(QString,uint,void*)));
998 }
999 
1000 
1001 void ArnM::onTimerMetrics()
1002 {
1003  _countFolderLink->setValue( _countFolder);
1004  _countLeafLink->setValue( _countLeaf);
1005  _countRefLink->setValue( _countRef);
1006 }
1007 
1008 
1009 void ArnM::customEvent( QEvent* ev)
1010 {
1011  int evIdx = ev->type() - ArnEvent::baseType();
1012  switch (evIdx) {
1014  {
1015  ArnEvZeroRef* e = static_cast<ArnEvZeroRef*>( ev);
1016  // qDebug() << "ArnEvZeroRef: path=" << e->arnLink()->linkPath();
1017  doZeroRefLink( e->arnLink());
1018  return;
1019  }
1020  default:;
1021  }
1022 }
1023 
1024 
1025 void ArnM::errorLog( QString errText, ArnError err, void* reference )
1026 {
1027  QString errTextSum;
1028 
1029  if (!instance()._errorLogger) { // If no error logger has been setup, add standard error text
1030  if (err.e < err.Err_N) { // Common message text in table
1031  errTextSum += instance()._errTextTab.at( err.e);
1032  }
1033  else {
1034  errTextSum += QString(tr("Error ")) + QString::number( err.e);
1035  }
1036  if (!errTextSum.isEmpty()) errTextSum += ": ";
1037  }
1038  errTextSum += errText;
1039 
1040  if (instance()._consoleError) {
1041  std::cerr << errTextSum.toUtf8().constData()
1042  << (QString(tr(" In ")) + errorSysName()).toUtf8().constData() << std::endl;
1043  }
1044  emit instance().errorLogSig( errTextSum, err.e, reference);
1045 }
1046 
1047 
1049 {
1050  static ArnM instance_;
1051 
1052  return instance_;
1053 }
1054 
1055 
1056 void ArnM::setConsoleError( bool isConsoleError)
1057 {
1058  instance()._consoleError = isConsoleError;
1059 }
1060 
1061 
1063 {
1064  instance()._defaultIgnoreSameValue = isIgnore;
1065 }
1066 
1067 
1069 {
1070  return instance()._defaultIgnoreSameValue;
1071 }
1072 
1073 
1075 {
1076  return _skipLocalSysLoading;
1077 }
1078 
1079 
1080 void ArnM::setSkipLocalSysLoading( bool skipLocalSysLoading)
1081 {
1082  _skipLocalSysLoading = skipLocalSysLoading;
1083 }
Data type of an Arn Data Object
Definition: Arn.hpp:74
bool debugThreading
Definition: ArnLib.cpp:38
static int baseType(int setVal=-1)
Definition: ArnEvent.cpp:62
static bool isMainThread()
Definition: ArnM.cpp:379
static bool isFolder(const QString &path)
Definition: ArnM.cpp:413
static QByteArray info()
Give information about this library.
Definition: ArnM.cpp:967
Arn main class.
Definition: ArnM.hpp:106
static void setValue(const QString &path, int value)
Assign an integer to an Arn Data Object at path
Definition: ArnM.cpp:473
static bool isThreadedApp()
Definition: ArnM.cpp:396
const QString resourceArnRoot
Definition: ArnLib.cpp:55
static void setConsoleError(bool isConsoleError)
Definition: ArnM.cpp:1056
bool debugLinkDestroy
Definition: ArnLib.cpp:40
static bool exist(const QString &path)
Definition: ArnM.cpp:402
static void setAtomicOpProvider(const QString &path)
Set this Arn Data Object as Atomic Operator Provider
Definition: ArnM.cpp:436
static int valueInt(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:244
QString fullPath(const QString &path)
Convert a path to a full absolute path.
Definition: Arn.cpp:82
static ARNREAL valueReal(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:261
static bool loadFromFile(const QString &path, const QString &fileName, Arn::Coding coding)
Load from a file to an Arn Data Object at path
Definition: ArnM.cpp:538
const QString pathLocalSys
Definition: Arn.cpp:46
static bool loadFromDirRoot(const QString &path, const QDir &dirRoot, Arn::Coding coding)
Load relative a directory root to an Arn Data Object at path
Definition: ArnM.cpp:556
static QString valueString(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:272
static void setupErrorlog(QObject *errLog)
Definition: ArnM.cpp:973
Only on path, no effect on discrete names. "/test/value" ==> "test/value".
Definition: Arn.hpp:191
static bool saveToFile(const QString &path, const QString &fileName, Arn::Coding coding)
Save to a file from an Arn Data Object at path
Definition: ArnM.cpp:565
static double valueDouble(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:255
static QStringList items(const QString &path)
Get the childrens of the folder at path
Definition: ArnM.cpp:315
void setSkipLocalSysLoading(bool skipLocalSysLoading)
Set mode skip "/Local/Sys/" loading.
Definition: ArnM.cpp:1080
Text coding, can be any character set.
Definition: Arn.hpp:201
bool debugSizes
Definition: ArnLib.cpp:37
static bool isLeaf(const QString &path)
Definition: ArnM.cpp:424
static QVariant valueVariant(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:294
static QByteArray valueByteArray(const QString &path)
Get the value of Arn Data Object at path
Definition: ArnM.cpp:283
QString convertPath(const QString &path, Arn::NameF nameF)
Convert a path to a specific format.
Definition: Arn.cpp:148
static void setDefaultIgnoreSameValue(bool isIgnore=true)
Set system default skipping of equal assignment value.
Definition: ArnM.cpp:1062
QString twinPath(const QString &path)
Get the bidirectional twin to a given path
Definition: Arn.cpp:195
#define ARNREAL
Definition: Arn.hpp:44
void errorLogSig(const QString &errText, uint errCode, void *reference)
static bool defaultIgnoreSameValue()
Definition: ArnM.cpp:1068
bool skipLocalSysLoading() const
Return mode skip "/Local/Sys/" loading.
Definition: ArnM.cpp:1074
Base class handle for an Arn Data Object.
Definition: ArnItemB.hpp:59
static ArnM & instance()
Definition: ArnM.cpp:1048
static void errorLog(QString errText, ArnError err=ArnError::Undef, void *reference=arnNullptr)
Definition: ArnM.cpp:1025
static void destroyLink(const QString &path, bool isGlobal=true)
Destroy the Arn Data Object at path
Definition: ArnM.cpp:853
ArnLink * arnLink() const
Definition: ArnEvent.hpp:230
bool isFolderPath(const QString &path)
Test if path is a folder path
Definition: Arn.cpp:210
static QString errorSysName()
Definition: ArnM.cpp:961
static bool isAtomicOpProvider(const QString &path)
Definition: ArnM.cpp:449
Handle for an Arn Data Object.
Definition: ArnItem.hpp:72