File indexing completed on 2024-05-12 17:16:09

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/svnqttypes.h"
0024 #include "svnqt/status.h"
0025 
0026 // std::map 'cause QMap isn't usable, it don't work with with the typenames in class
0027 #include <map>
0028 #include <algorithm>
0029 #include <QString>
0030 #include <QStringList>
0031 #include <QReadWriteLock>
0032 #include <QReadLocker>
0033 #include <QWriteLocker>
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> class cacheEntry
0044 {
0045 public:
0046     typedef cacheEntry<C> cache_type;
0047     typedef typename std::map<QString, cache_type> cache_map_type;
0048     typedef typename cache_map_type::const_iterator citer;
0049     typedef typename cache_map_type::iterator iter;
0050 
0051 protected:
0052     QString m_key;
0053     bool m_isValid;
0054     C m_content;
0055     cache_map_type m_subMap;
0056 
0057 public:
0058     cacheEntry();
0059     cacheEntry(const QString &key);
0060     cacheEntry(const cacheEntry<C> &other);
0061 
0062     virtual ~cacheEntry() {};
0063 
0064     virtual bool find(QStringList &, QList<C> &)const;
0065     //! Checks if cache contains a specific item
0066     /*!
0067      * the keylist will manipulated - so copy-operations aren't needed.
0068      * \param what Stringlist containing the components to search for
0069      * \return true if found (may or may not valid!) otherwise false
0070      */
0071     virtual bool find(QStringList &what)const;
0072     //! Checks if cache contains a specific valid item
0073     /*!
0074      * if yes, the content will stored in st
0075      * \param what the keylist to search for
0076      * \param st target status to store content if found
0077      * \return true if found
0078      */
0079     virtual bool findSingleValid(QStringList &what, C &st)const;
0080     //! Checks if cache contains a specific valid item
0081     /*!
0082      * in difference to virtual bool find(QStringList&,svn::StatusEntries&)const no copy operations
0083      * are made inside so it works much faster for simple find.
0084      * \param what the keylist to search for
0085      * \param check_valid_subs if true, return true if a subitem is valid even the item isn't valid
0086      * \return true if found
0087      */
0088     virtual bool findSingleValid(QStringList &what, bool check_valid_subs)const;
0089     template<class T> 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         std::map<QString, cacheEntry>::const_iterator it;
0126         for (it = m_subMap.begin(); it != m_subMap.end(); ++it) {
0127             std::cout << pre.latin1() << it->first.latin1() << " (" << it->second.m_key.latin1() << ")" << std::endl;
0128             it->second.dump_tree(level + 1);
0129         }
0130     }
0131 #endif
0132 };
0133 
0134 typedef cacheEntry<svn::StatusPtr> statusEntry;
0135 
0136 template<class C> inline cacheEntry<C>::cacheEntry()
0137     : m_key(), m_isValid(false), m_content()
0138 {
0139 }
0140 
0141 template<class C> inline cacheEntry<C>::cacheEntry(const QString &key)
0142     : m_key(key), m_isValid(false), m_content()
0143 {
0144 }
0145 
0146 template<class C> inline cacheEntry<C>::cacheEntry(const cacheEntry<C> &other)
0147     : m_key(other.m_key), m_isValid(other.m_isValid),
0148       m_content(other.m_content), m_subMap(other.m_subMap)
0149 {
0150 }
0151 
0152 template<class C> inline cacheEntry<C> &cacheEntry<C>::operator=(const cacheEntry<C> &other)
0153 {
0154     m_key = other.m_key;
0155     m_isValid = other.m_isValid;
0156     m_content = other.m_content;
0157     m_subMap = other.m_subMap;
0158     return *this;
0159 }
0160 
0161 template<class C> inline  bool cacheEntry<C>::find(QStringList &what, QList<C> &t)const
0162 {
0163     if (what.empty()) {
0164         return false;
0165     }
0166     citer it = m_subMap.find(what.at(0));
0167     if (it == m_subMap.end()) {
0168         return false;
0169     }
0170     if (what.count() == 1) {
0171         if (it->second.isValid()) {
0172             t.append(it->second.content());
0173         }
0174         it->second.appendValidSub(t);
0175         return true;
0176     }
0177     what.erase(what.begin());
0178     return it->second.find(what, t);
0179 }
0180 
0181 template<class C> inline bool cacheEntry<C>::find(QStringList &what)const
0182 {
0183     if (what.isEmpty()) {
0184         return false;
0185     }
0186     citer it = m_subMap.find(what.at(0));
0187     if (it == m_subMap.end()) {
0188         return false;
0189     }
0190     if (what.count() == 1) {
0191         return true;
0192     }
0193     what.erase(what.begin());
0194     return it->second.find(what);
0195 }
0196 
0197 template<class C> inline bool cacheEntry<C>::findSingleValid(QStringList &what, C &t)const
0198 {
0199     if (what.isEmpty()) {
0200         return false;
0201     }
0202     citer it = m_subMap.find(what.at(0));
0203     if (it == m_subMap.end()) {
0204         return false;
0205     }
0206     if (what.count() == 1) {
0207         t = it->second.content();
0208         return it->second.isValid();
0209     }
0210     what.erase(what.begin());
0211     return it->second.findSingleValid(what, t);
0212 }
0213 
0214 template<class C> inline bool cacheEntry<C>::findSingleValid(QStringList &what, bool check_valid_subs)const
0215 {
0216     if (what.isEmpty()) {
0217         return false;
0218     }
0219     citer it = m_subMap.find(what.at(0));
0220     if (it == m_subMap.end()) {
0221         return false;
0222     }
0223     if (what.count() == 1) {
0224         return it->second.isValid() || (check_valid_subs && it->second.hasValidSubs());
0225     }
0226     what.erase(what.begin());
0227     return it->second.findSingleValid(what, check_valid_subs);
0228 }
0229 
0230 template<class C> inline void cacheEntry<C>::appendValidSub(QList<C> &t)const
0231 {
0232     citer it;
0233     for (it = m_subMap.begin(); it != m_subMap.end(); ++it) {
0234         if (it->second.isValid()) {
0235             t.append(it->second.content());
0236         }
0237         it->second.appendValidSub(t);
0238     }
0239 }
0240 
0241 template<class C> inline bool cacheEntry<C>::deleteKey(QStringList &what, bool exact)
0242 {
0243     if (what.isEmpty()) {
0244         return true;
0245     }
0246     iter it = m_subMap.find(what.at(0));
0247     if (it == m_subMap.end()) {
0248         return true;
0249     }
0250     bool caller_must_check = false;
0251     /* first stage - we are the one holding the right key */
0252     if (what.count() == 1) {
0253         if (!exact || !it->second.hasValidSubs()) {
0254             m_subMap.erase(it);
0255             caller_must_check = true;
0256         } else {
0257             it->second.markInvalid();
0258         }
0259     } else {
0260         /* otherwise go trough tree */
0261         what.erase(what.begin());
0262         bool b = it->second.deleteKey(what, exact);
0263         if (b && !it->second.hasValidSubs()) {
0264             m_subMap.erase(it);
0265             caller_must_check = true;
0266         }
0267     }
0268     return caller_must_check;
0269 }
0270 
0271 template<class C> inline bool cacheEntry<C>::hasValidSubs()const
0272 {
0273     citer it;
0274     for (it = m_subMap.begin(); it != m_subMap.end(); ++it) {
0275         if (it->second.isValid() || it->second.hasValidSubs()) {
0276             return true;
0277         }
0278     }
0279     return false;
0280 }
0281 
0282 template<class C> inline void cacheEntry<C>::insertKey(QStringList &what, const C &st)
0283 {
0284     if (what.isEmpty()) {
0285         return;
0286     }
0287     const QString m = what.at(0);
0288 
0289     if (m_subMap.find(m) == m_subMap.end()) {
0290         m_subMap[m].m_key = m;
0291     }
0292     if (what.count() == 1) {
0293         m_subMap[m].setValidContent(m, st);
0294         return;
0295     }
0296     what.erase(what.begin());
0297     m_subMap[m].insertKey(what, st);
0298 }
0299 
0300 template<class C> template<class T> inline void cacheEntry<C>::listsubs_if(QStringList &what, T &oper)const
0301 {
0302     if (what.isEmpty()) {
0303         /* we are the one to get the list for*/
0304         oper = for_each(m_subMap.begin(), m_subMap.end(), oper);
0305         return;
0306     }
0307     /* otherwise find next */
0308     citer it = m_subMap.find(what.at(0));
0309     if (it == m_subMap.end()) {
0310         /* not found */
0311         return;
0312     }
0313     what.erase(what.begin());
0314     it->second.listsubs_if(what, oper);
0315 }
0316 
0317 template<class C> class itemCache
0318 {
0319 public:
0320     typedef cacheEntry<C> cache_type;
0321     typedef typename std::map<QString, cache_type> cache_map_type;
0322     typedef typename cache_map_type::const_iterator citer;
0323     typedef typename cache_map_type::iterator iter;
0324 
0325 protected:
0326     cache_map_type m_contentMap;
0327 
0328     mutable QReadWriteLock m_RWLock;
0329 
0330 public:
0331     itemCache(): m_contentMap(), m_RWLock() {}
0332     virtual ~itemCache() {}
0333 
0334     void clear()
0335     {
0336         QWriteLocker locker(&m_RWLock);
0337         m_contentMap.clear();
0338     }
0339     //! Checks if cache contains a specific item
0340     /*!
0341      * the keylist will manipulated - so copy-operations aren't needed.
0342      * \param what Stringlist containing the components to search for
0343      * \return true if found (may or may not valid!) otherwise false
0344      */
0345     virtual bool find(const QString &what)const;
0346 
0347     virtual void deleteKey(const QString &what, bool exact);
0348     virtual void insertKey(const C &, const QString &path);
0349     virtual bool findSingleValid(const QString &what, C &)const;
0350     virtual bool findSingleValid(const QString &what, bool check_valid_subs)const;
0351 
0352     template<class T>void listsubs_if(const QString &what, T &oper)const;
0353 
0354     void dump_tree();
0355 };
0356 
0357 
0358 template<class C> inline void itemCache<C>::insertKey(const C &st, const QString &path)
0359 {
0360     QStringList _keys = path.split(QLatin1Char('/'));
0361     if (_keys.isEmpty()) {
0362         return;
0363     }
0364     QWriteLocker locker(&m_RWLock);
0365 
0366     const QString m = _keys.at(0);
0367     citer it = m_contentMap.find(m);
0368 
0369     if (it == m_contentMap.end()) {
0370         m_contentMap[m] = cache_type(m);
0371     }
0372     if (_keys.count() == 1) {
0373         m_contentMap[m].setValidContent(m, st);
0374     } else {
0375         _keys.erase(_keys.begin());
0376         m_contentMap[m].insertKey(_keys, st);
0377     }
0378 }
0379 
0380 template<class C> inline bool itemCache<C>::find(const QString &what)const
0381 {
0382     QReadLocker locker(&m_RWLock);
0383 
0384     if (m_contentMap.empty()) {
0385         return false;
0386     }
0387     QStringList _keys = what.split(QLatin1Char('/'));
0388     if (_keys.isEmpty()) {
0389         return false;
0390     }
0391     citer it = m_contentMap.find(_keys.at(0));
0392     if (it == m_contentMap.end()) {
0393         return false;
0394     }
0395     if (_keys.count() == 1) {
0396         return true;
0397     }
0398     _keys.erase(_keys.begin());
0399     return it->second.find(_keys);
0400 }
0401 
0402 template<class C> inline void itemCache<C>::deleteKey(const QString &_what, bool exact)
0403 {
0404     QWriteLocker locker(&m_RWLock);
0405 
0406     if (m_contentMap.empty()) {
0407         return;
0408     }
0409     QStringList what = _what.split(QLatin1Char('/'));
0410     if (what.isEmpty()) {
0411         return;
0412     }
0413     iter it = m_contentMap.find(what.at(0));
0414     if (it == m_contentMap.end()) {
0415         return;
0416     }
0417     /* first stage - we are the one holding the right key */
0418     if (what.count() == 1) {
0419         if (!exact || !it->second.hasValidSubs()) {
0420             /* if it has no valid subs delete it */
0421             m_contentMap.erase(it);
0422         } else {
0423             /* otherwise mark as invalid */
0424             it->second.markInvalid();
0425         }
0426         return;
0427     } else {
0428         /* otherwise go trough tree */
0429         what.erase(what.begin());
0430         bool b = it->second.deleteKey(what, exact);
0431         if (b && !it->second.hasValidSubs()) {
0432             m_contentMap.erase(it);
0433         }
0434     }
0435 }
0436 
0437 template<class C> inline void itemCache<C>::dump_tree()
0438 {
0439     QReadLocker locker(&m_RWLock);
0440     citer it;
0441     for (it = m_contentMap.begin(); it != m_contentMap.end(); ++it) {
0442 //        std::cout<<it->first.latin1() << " (" << it->second.key().latin1() << ")"<<std::endl;
0443 //        it->second.dump_tree(1);
0444     }
0445 }
0446 
0447 template<class C> inline bool itemCache<C>::findSingleValid(const QString &_what, C &st)const
0448 {
0449     QReadLocker locker(&m_RWLock);
0450 
0451     if (m_contentMap.empty()) {
0452         return false;
0453     }
0454     QStringList what = _what.split(QLatin1Char('/'));
0455     if (what.isEmpty()) {
0456         return false;
0457     }
0458     citer it = m_contentMap.find(what.at(0));
0459     if (it == m_contentMap.end()) {
0460         return false;
0461     }
0462     if (what.count() == 1) {
0463         if (it->second.isValid()) {
0464             st = it->second.content();
0465             return true;
0466         }
0467         return false;
0468     }
0469     what.erase(what.begin());
0470     return it->second.findSingleValid(what, st);
0471 }
0472 
0473 template<class C> inline bool itemCache<C>::findSingleValid(const QString &_what, bool check_valid_subs)const
0474 {
0475     QReadLocker locker(&m_RWLock);
0476 
0477     if (m_contentMap.empty()) {
0478         return false;
0479     }
0480     QStringList what = _what.split(QLatin1Char('/'));
0481     if (what.isEmpty()) {
0482         return false;
0483     }
0484     citer it = m_contentMap.find(what.at(0));
0485     if (it == m_contentMap.end()) {
0486         return false;
0487     }
0488     if (what.count() == 1) {
0489         return it->second.isValid() || (check_valid_subs && it->second.hasValidSubs());
0490     }
0491     what.erase(what.begin());
0492     return it->second.findSingleValid(what, check_valid_subs);
0493 }
0494 
0495 template<class C> template<class T> inline void itemCache<C>::listsubs_if(const QString &_what, T &oper)const
0496 {
0497     QReadLocker locker(&m_RWLock);
0498 
0499     if (m_contentMap.empty()) {
0500         return;
0501     }
0502     QStringList what = _what.split(QLatin1Char('/'));
0503     if (what.isEmpty()) {
0504         return;
0505     }
0506     citer it = m_contentMap.find(what.at(0));
0507 
0508     if (it == m_contentMap.end()) {
0509         return;
0510     }
0511     if (what.count() == 1) {
0512         oper = for_each(m_contentMap.begin(), m_contentMap.end(), oper);
0513         return;
0514     }
0515     what.erase(what.begin());
0516     it->second.listsubs_if(what, oper);
0517 }
0518 
0519 typedef cacheEntry<svn::StatusPtr> ptrEntry;
0520 typedef itemCache<svn::StatusPtr> statusCache;
0521 
0522 class ValidRemoteOnly
0523 {
0524     svn::StatusEntries m_List;
0525 public:
0526     ValidRemoteOnly(): m_List() {}
0527     void operator()(const std::pair<QString, helpers::ptrEntry> &_data)
0528     {
0529         if (_data.second.isValid() && _data.second.content()->validReposStatus() && !_data.second.content()->validLocalStatus()) {
0530             m_List.push_back(_data.second.content());
0531         }
0532     }
0533     const svn::StatusEntries &liste()const
0534     {
0535         return m_List;
0536     }
0537 };
0538 
0539 }
0540 
0541 #endif