File indexing completed on 2024-04-28 05:41:59

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht                             *
0003  *   ral@alwins-world.de                                                   *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0019  ***************************************************************************/
0020 #ifndef HELPERSCACHEENTRY_H
0021 #define HELPERSCACHEENTRY_H
0022 
0023 #include "svnqt/status.h"
0024 #include "svnqt/svnqttypes.h"
0025 
0026 // std::map 'cause QMap isn't usable, it don't work with with the typenames in class
0027 #include <QReadLocker>
0028 #include <QReadWriteLock>
0029 #include <QString>
0030 #include <QStringList>
0031 #include <QWriteLocker>
0032 #include <algorithm>
0033 #include <map>
0034 
0035 namespace helpers
0036 {
0037 
0038 /**
0039     Class for fast search of path based items.
0040 
0041     @author Rajko Albrecht <ral@alwins-world.de>
0042 */
0043 template<class C>
0044 class cacheEntry
0045 {
0046 public:
0047     typedef cacheEntry<C> cache_type;
0048     typedef typename std::map<QString, cache_type> cache_map_type;
0049 
0050 protected:
0051     QString m_key;
0052     bool m_isValid;
0053     C m_content;
0054     cache_map_type m_subMap;
0055 
0056 public:
0057     cacheEntry();
0058     cacheEntry(const QString &key);
0059     cacheEntry(const cacheEntry<C> &other);
0060 
0061     virtual ~cacheEntry() = default;
0062 
0063     virtual bool find(QStringList &, QList<C> &) const;
0064     //! Checks if cache contains a specific item
0065     /*!
0066      * the keylist will manipulated - so copy-operations aren't needed.
0067      * \param what Stringlist containing the components to search for
0068      * \return true if found (may or may not valid!) otherwise false
0069      */
0070     virtual bool find(QStringList &what) const;
0071     //! Checks if cache contains a specific valid item
0072     /*!
0073      * if yes, the content will stored in st
0074      * \param what the keylist to search for
0075      * \param st target status to store content if found
0076      * \return true if found
0077      */
0078     virtual bool findSingleValid(QStringList &what, C &st) const;
0079     //! Checks if cache contains a specific valid item
0080     /*!
0081      * in difference to virtual bool find(QStringList&,svn::StatusEntries&)const no copy operations
0082      * are made inside so it works much faster for simple find.
0083      * \param what the keylist to search for
0084      * \param check_valid_subs if true, return true if a subitem is valid even the item isn't valid
0085      * \return true if found
0086      */
0087     virtual bool findSingleValid(QStringList &what, bool check_valid_subs) const;
0088     template<class T>
0089     void listsubs_if(QStringList &_what, T &oper) const;
0090 
0091     virtual void appendValidSub(QList<C> &) const;
0092     virtual bool isValid() const
0093     {
0094         return m_isValid;
0095     }
0096     virtual const C &content() const
0097     {
0098         return m_content;
0099     }
0100     virtual bool deleteKey(QStringList &, bool exact);
0101     virtual void insertKey(QStringList &, const C &);
0102     virtual void setValidContent(const QString &key, const C &st)
0103     {
0104         m_key = key;
0105         m_isValid = true;
0106         m_content = st;
0107     }
0108     virtual bool hasValidSubs() const;
0109     virtual void markInvalid()
0110     {
0111         m_content = C();
0112         m_isValid = false;
0113     }
0114     const QString &key() const
0115     {
0116         return m_key;
0117     }
0118 
0119     cacheEntry<C> &operator=(const cacheEntry<C> &other);
0120 #if 0
0121     void dump_tree(int level = 0)const
0122     {
0123         QString pre;
0124         pre.fill('-', level);
0125         for (auto it = m_subMap.begin(); it != m_subMap.end(); ++it) {
0126             std::cout << pre.latin1() << it->first.latin1() << " (" << it->second.m_key.latin1() << ")" << std::endl;
0127             it->second.dump_tree(level + 1);
0128         }
0129     }
0130 #endif
0131 };
0132 
0133 typedef cacheEntry<svn::StatusPtr> statusEntry;
0134 
0135 template<class C>
0136 inline cacheEntry<C>::cacheEntry()
0137     : m_key()
0138     , m_isValid(false)
0139     , m_content()
0140 {
0141 }
0142 
0143 template<class C>
0144 inline cacheEntry<C>::cacheEntry(const QString &key)
0145     : m_key(key)
0146     , m_isValid(false)
0147     , m_content()
0148 {
0149 }
0150 
0151 template<class C>
0152 inline cacheEntry<C>::cacheEntry(const cacheEntry<C> &other)
0153     : m_key(other.m_key)
0154     , m_isValid(other.m_isValid)
0155     , m_content(other.m_content)
0156     , m_subMap(other.m_subMap)
0157 {
0158 }
0159 
0160 template<class C>
0161 inline cacheEntry<C> &cacheEntry<C>::operator=(const cacheEntry<C> &other)
0162 {
0163     m_key = other.m_key;
0164     m_isValid = other.m_isValid;
0165     m_content = other.m_content;
0166     m_subMap = other.m_subMap;
0167     return *this;
0168 }
0169 
0170 template<class C>
0171 inline bool cacheEntry<C>::find(QStringList &what, QList<C> &t) const
0172 {
0173     if (what.empty()) {
0174         return false;
0175     }
0176     const auto it = m_subMap.find(what.at(0));
0177     if (it == m_subMap.end()) {
0178         return false;
0179     }
0180     if (what.count() == 1) {
0181         if (it->second.isValid()) {
0182             t.append(it->second.content());
0183         }
0184         it->second.appendValidSub(t);
0185         return true;
0186     }
0187     what.erase(what.begin());
0188     return it->second.find(what, t);
0189 }
0190 
0191 template<class C>
0192 inline bool cacheEntry<C>::find(QStringList &what) const
0193 {
0194     if (what.isEmpty()) {
0195         return false;
0196     }
0197     const auto it = m_subMap.find(what.at(0));
0198     if (it == m_subMap.end()) {
0199         return false;
0200     }
0201     if (what.count() == 1) {
0202         return true;
0203     }
0204     what.erase(what.begin());
0205     return it->second.find(what);
0206 }
0207 
0208 template<class C>
0209 inline bool cacheEntry<C>::findSingleValid(QStringList &what, C &t) const
0210 {
0211     if (what.isEmpty()) {
0212         return false;
0213     }
0214     const auto it = m_subMap.find(what.at(0));
0215     if (it == m_subMap.end()) {
0216         return false;
0217     }
0218     if (what.count() == 1) {
0219         t = it->second.content();
0220         return it->second.isValid();
0221     }
0222     what.erase(what.begin());
0223     return it->second.findSingleValid(what, t);
0224 }
0225 
0226 template<class C>
0227 inline bool cacheEntry<C>::findSingleValid(QStringList &what, bool check_valid_subs) const
0228 {
0229     if (what.isEmpty()) {
0230         return false;
0231     }
0232     const auto it = m_subMap.find(what.at(0));
0233     if (it == m_subMap.end()) {
0234         return false;
0235     }
0236     if (what.count() == 1) {
0237         return it->second.isValid() || (check_valid_subs && it->second.hasValidSubs());
0238     }
0239     what.erase(what.begin());
0240     return it->second.findSingleValid(what, check_valid_subs);
0241 }
0242 
0243 template<class C>
0244 inline void cacheEntry<C>::appendValidSub(QList<C> &t) const
0245 {
0246     for (const auto &it : m_subMap) {
0247         if (it.second.isValid()) {
0248             t.append(it.second.content());
0249         }
0250         it.second.appendValidSub(t);
0251     }
0252 }
0253 
0254 template<class C>
0255 inline bool cacheEntry<C>::deleteKey(QStringList &what, bool exact)
0256 {
0257     if (what.isEmpty()) {
0258         return true;
0259     }
0260     const auto it = m_subMap.find(what.at(0));
0261     if (it == m_subMap.end()) {
0262         return true;
0263     }
0264     bool caller_must_check = false;
0265     /* first stage - we are the one holding the right key */
0266     if (what.count() == 1) {
0267         if (!exact || !it->second.hasValidSubs()) {
0268             m_subMap.erase(it);
0269             caller_must_check = true;
0270         } else {
0271             it->second.markInvalid();
0272         }
0273     } else {
0274         /* otherwise go trough tree */
0275         what.erase(what.begin());
0276         bool b = it->second.deleteKey(what, exact);
0277         if (b && !it->second.hasValidSubs()) {
0278             m_subMap.erase(it);
0279             caller_must_check = true;
0280         }
0281     }
0282     return caller_must_check;
0283 }
0284 
0285 template<class C>
0286 inline bool cacheEntry<C>::hasValidSubs() const
0287 {
0288     for (const auto &it : m_subMap) {
0289         if (it.second.isValid() || it.second.hasValidSubs()) {
0290             return true;
0291         }
0292     }
0293     return false;
0294 }
0295 
0296 template<class C>
0297 inline void cacheEntry<C>::insertKey(QStringList &what, const C &st)
0298 {
0299     if (what.isEmpty()) {
0300         return;
0301     }
0302     const QString m = what.at(0);
0303 
0304     if (m_subMap.find(m) == m_subMap.end()) {
0305         m_subMap[m].m_key = m;
0306     }
0307     if (what.count() == 1) {
0308         m_subMap[m].setValidContent(m, st);
0309         return;
0310     }
0311     what.erase(what.begin());
0312     m_subMap[m].insertKey(what, st);
0313 }
0314 
0315 template<class C>
0316 template<class T>
0317 inline void cacheEntry<C>::listsubs_if(QStringList &what, T &oper) const
0318 {
0319     if (what.isEmpty()) {
0320         /* we are the one to get the list for*/
0321         oper = for_each(m_subMap.begin(), m_subMap.end(), oper);
0322         return;
0323     }
0324     /* otherwise find next */
0325     const auto it = m_subMap.find(what.at(0));
0326     if (it == m_subMap.end()) {
0327         /* not found */
0328         return;
0329     }
0330     what.erase(what.begin());
0331     it->second.listsubs_if(what, oper);
0332 }
0333 
0334 template<class C>
0335 class itemCache
0336 {
0337 public:
0338     typedef cacheEntry<C> cache_type;
0339     typedef typename std::map<QString, cache_type> cache_map_type;
0340 
0341 protected:
0342     cache_map_type m_contentMap;
0343 
0344     mutable QReadWriteLock m_RWLock;
0345 
0346 public:
0347     itemCache() = default;
0348     virtual ~itemCache() = default;
0349 
0350     void clear()
0351     {
0352         QWriteLocker locker(&m_RWLock);
0353         m_contentMap.clear();
0354     }
0355     //! Checks if cache contains a specific item
0356     /*!
0357      * the keylist will manipulated - so copy-operations aren't needed.
0358      * \param what Stringlist containing the components to search for
0359      * \return true if found (may or may not valid!) otherwise false
0360      */
0361     virtual bool find(const QString &what) const;
0362 
0363     virtual void deleteKey(const QString &what, bool exact);
0364     virtual void insertKey(const C &, const QString &path);
0365     virtual bool findSingleValid(const QString &what, C &) const;
0366     virtual bool findSingleValid(const QString &what, bool check_valid_subs) const;
0367 
0368     template<class T>
0369     void listsubs_if(const QString &what, T &oper) const;
0370 
0371     void dump_tree();
0372 };
0373 
0374 template<class C>
0375 inline void itemCache<C>::insertKey(const C &st, const QString &path)
0376 {
0377     QStringList _keys = path.split(QLatin1Char('/'));
0378     if (_keys.isEmpty()) {
0379         return;
0380     }
0381     QWriteLocker locker(&m_RWLock);
0382 
0383     const QString m = _keys.at(0);
0384     const auto it = m_contentMap.find(m);
0385 
0386     if (it == m_contentMap.end()) {
0387         m_contentMap[m] = cache_type(m);
0388     }
0389     if (_keys.count() == 1) {
0390         m_contentMap[m].setValidContent(m, st);
0391     } else {
0392         _keys.erase(_keys.begin());
0393         m_contentMap[m].insertKey(_keys, st);
0394     }
0395 }
0396 
0397 template<class C>
0398 inline bool itemCache<C>::find(const QString &what) const
0399 {
0400     QReadLocker locker(&m_RWLock);
0401 
0402     if (m_contentMap.empty()) {
0403         return false;
0404     }
0405     QStringList _keys = what.split(QLatin1Char('/'));
0406     if (_keys.isEmpty()) {
0407         return false;
0408     }
0409     const auto it = m_contentMap.find(_keys.at(0));
0410     if (it == m_contentMap.end()) {
0411         return false;
0412     }
0413     if (_keys.count() == 1) {
0414         return true;
0415     }
0416     _keys.erase(_keys.begin());
0417     return it->second.find(_keys);
0418 }
0419 
0420 template<class C>
0421 inline void itemCache<C>::deleteKey(const QString &_what, bool exact)
0422 {
0423     QWriteLocker locker(&m_RWLock);
0424 
0425     if (m_contentMap.empty()) {
0426         return;
0427     }
0428     QStringList what = _what.split(QLatin1Char('/'));
0429     if (what.isEmpty()) {
0430         return;
0431     }
0432     const auto it = m_contentMap.find(what.at(0));
0433     if (it == m_contentMap.end()) {
0434         return;
0435     }
0436     /* first stage - we are the one holding the right key */
0437     if (what.count() == 1) {
0438         if (!exact || !it->second.hasValidSubs()) {
0439             /* if it has no valid subs delete it */
0440             m_contentMap.erase(it);
0441         } else {
0442             /* otherwise mark as invalid */
0443             it->second.markInvalid();
0444         }
0445         return;
0446     } else {
0447         /* otherwise go trough tree */
0448         what.erase(what.begin());
0449         bool b = it->second.deleteKey(what, exact);
0450         if (b && !it->second.hasValidSubs()) {
0451             m_contentMap.erase(it);
0452         }
0453     }
0454 }
0455 
0456 template<class C>
0457 inline void itemCache<C>::dump_tree()
0458 {
0459     QReadLocker locker(&m_RWLock);
0460     for (auto it = m_contentMap.begin(); it != m_contentMap.end(); ++it) {
0461         //        std::cout<<it->first.latin1() << " (" << it->second.key().latin1() << ")"<<std::endl;
0462         //        it->second.dump_tree(1);
0463     }
0464 }
0465 
0466 template<class C>
0467 inline bool itemCache<C>::findSingleValid(const QString &_what, C &st) const
0468 {
0469     QReadLocker locker(&m_RWLock);
0470 
0471     if (m_contentMap.empty()) {
0472         return false;
0473     }
0474     QStringList what = _what.split(QLatin1Char('/'));
0475     if (what.isEmpty()) {
0476         return false;
0477     }
0478     const auto it = m_contentMap.find(what.at(0));
0479     if (it == m_contentMap.end()) {
0480         return false;
0481     }
0482     if (what.count() == 1) {
0483         if (it->second.isValid()) {
0484             st = it->second.content();
0485             return true;
0486         }
0487         return false;
0488     }
0489     what.erase(what.begin());
0490     return it->second.findSingleValid(what, st);
0491 }
0492 
0493 template<class C>
0494 inline bool itemCache<C>::findSingleValid(const QString &_what, bool check_valid_subs) const
0495 {
0496     QReadLocker locker(&m_RWLock);
0497 
0498     if (m_contentMap.empty()) {
0499         return false;
0500     }
0501     QStringList what = _what.split(QLatin1Char('/'));
0502     if (what.isEmpty()) {
0503         return false;
0504     }
0505     const auto it = m_contentMap.find(what.at(0));
0506     if (it == m_contentMap.end()) {
0507         return false;
0508     }
0509     if (what.count() == 1) {
0510         return it->second.isValid() || (check_valid_subs && it->second.hasValidSubs());
0511     }
0512     what.erase(what.begin());
0513     return it->second.findSingleValid(what, check_valid_subs);
0514 }
0515 
0516 template<class C>
0517 template<class T>
0518 inline void itemCache<C>::listsubs_if(const QString &_what, T &oper) const
0519 {
0520     QReadLocker locker(&m_RWLock);
0521 
0522     if (m_contentMap.empty()) {
0523         return;
0524     }
0525     QStringList what = _what.split(QLatin1Char('/'));
0526     if (what.isEmpty()) {
0527         return;
0528     }
0529     const auto it = m_contentMap.find(what.at(0));
0530     if (it == m_contentMap.end()) {
0531         return;
0532     }
0533     if (what.count() == 1) {
0534         oper = for_each(m_contentMap.begin(), m_contentMap.end(), oper);
0535         return;
0536     }
0537     what.erase(what.begin());
0538     it->second.listsubs_if(what, oper);
0539 }
0540 
0541 typedef cacheEntry<svn::StatusPtr> ptrEntry;
0542 typedef itemCache<svn::StatusPtr> statusCache;
0543 
0544 class ValidRemoteOnly
0545 {
0546     svn::StatusEntries m_List;
0547 
0548 public:
0549     ValidRemoteOnly()
0550         : m_List()
0551     {
0552     }
0553     void operator()(const std::pair<QString, helpers::ptrEntry> &_data)
0554     {
0555         if (_data.second.isValid() && _data.second.content()->validReposStatus() && !_data.second.content()->validLocalStatus()) {
0556             m_List.push_back(_data.second.content());
0557         }
0558     }
0559     const svn::StatusEntries &liste() const
0560     {
0561         return m_List;
0562     }
0563 };
0564 
0565 }
0566 
0567 #endif