1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PoolQuery.cc
19 #include "zypp/base/Logger.h"
20 #include "zypp/base/PtrTypes.h"
21 #include "zypp/base/DefaultIntegral.h"
22 #include "zypp/base/Regex.h"
23 #include "zypp/base/Algorithm.h"
24 #include "zypp/base/UserRequestException.h"
25 #include "zypp/repo/RepoException.h"
27 #include "zypp/sat/Pool.h"
28 #include "zypp/sat/Solvable.h"
29 #include "zypp/sat/SolvAttr.h"
30 #include "zypp/sat/detail/PoolImpl.h"
34 #include "satsolver/repo.h"
37 #include "zypp/PoolQuery.h"
40 using namespace zypp::sat;
42 ///////////////////////////////////////////////////////////////////
44 { /////////////////////////////////////////////////////////////////
46 ///////////////////////////////////////////////////////////////////
48 // CLASS NAME : PoolQuery::Impl
54 : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
66 static int repo_search_cb(void *cbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
68 PoolQuery *me = (PoolQuery*) cbdata;
72 sat::Solvable solvable(s - sat::Pool::instance().get()->solvables);
74 // now filter by kind here (we cant do it before)
75 if ( ! me->_pimpl->_kinds.empty() )
77 // the user wants to filter by kind.
78 if ( find( me->_pimpl->_kinds.begin(),
79 me->_pimpl->_kinds.end(),
81 == me->_pimpl->_kinds.end() )
83 // we did not find the kind in the list
84 // so this is not a result.
85 return SEARCH_NEXT_SOLVABLE;
90 r = me->_pimpl->_fnc( solvable );//makeResObject(solvable) );
94 return SEARCH_NEXT_SOLVABLE;
97 ResultIterator begin();
100 string asString() const;
104 string createRegex(StrContainer & container);
107 /** Raw search strings. */
108 StrContainer _strings;
109 /** Regex-compiled search strings. */
111 /** Raw attributes */
113 /** Regex-compiled attributes */
114 CompiledAttrMap _rcattrs;
116 /** Repos to search. */
118 /** Kinds to search */
121 /** Sat solver search flags */
123 /** Backup of search flags. compile() may change the flags if needed, so
124 * in order to reuse the query, the original flags need to be stored
125 * at the start of compile() */
127 /** Sat solver status flags */
134 /** Sat solver Dataiterator structure */
135 ::_Dataiterator _rdit;
139 /** Function for processing found solvables. Used in execute(). */
140 mutable PoolQuery::ProcessResolvable _fnc;
142 friend Impl * rwcowClone<Impl>( const Impl * rhs );
143 /** clone for RWCOW_pointer */
145 { return new Impl( *this ); }
148 template <class _OutputIterator>
149 struct CollectNonEmpty
151 CollectNonEmpty( _OutputIterator iter_r ) : _iter( iter_r ) {}
154 bool operator()( const _Tp & value_r ) const
163 mutable _OutputIterator _iter;
166 void PoolQuery::Impl::compile()
171 // 'different' - will have to iterate through all and match by ourselves (slow)
172 // 'same' - will pass the compiled string to dataiterator_init
173 // 'one-attr' - will pass it to dataiterator_init
174 // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
179 // create regex; store in _rcstrings; if more strings flag regex;
182 _rcstrings = createRegex(_strings);
183 if (_strings.size() > 1)
184 _flags = (_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
188 // else if _attrs is not empty but it contains just one attr
189 // for all _strings and _attr[key] strings
190 // create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
191 else if (_attrs.size() == 1)
194 for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
197 for(StrContainer::const_iterator it = _attrs.begin()->second.begin(); it != _attrs.begin()->second.end(); ++it)
200 _rcstrings = createRegex(joined);
201 _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
205 // // MULTIPLE ATTRIBUTES
208 bool attrvals_empty = true;
209 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
210 if (!ai->second.empty())
211 for(StrContainer::const_iterator it = ai->second.begin();
212 it != ai->second.end(); it++)
215 attrvals_empty = false;
216 goto attremptycheckend;
221 bool attrvals_thesame = true;
222 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
227 // // THE SAME STRINGS FOR DIFFERENT ATTRS
228 // else if _attrs is not empty but it does not contain strings
229 // for each key in _attrs take all _strings
230 // create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
231 if (attrvals_empty || attrvals_thesame)
235 // compile the search string
237 for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
240 _rcstrings = createRegex(joined);
242 // copy the _attrs keys to _rcattrs
243 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
244 _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
247 // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
248 // if _attrs is not empty and it contains non-empty vectors with non-empty strings
249 // for each key in _attrs take all _strings + all _attrs[key] strings
250 // create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
257 // tell the Dataiterator to search only in one repo if only one specified
258 if (_repos.size() == 1)
259 _flags &= ~SEARCH_ALL_REPOS;
263 DBG << asString() << endl;
267 * Converts '*' and '?' wildcards within str into their regex equivalents.
269 static string wildcards2regex(const string & str)
271 string regexed = str;
273 str::regex all("\\*"); // regex to search for '*'
274 str::regex one("\\?"); // regex to search for '?'
275 string r_all(".*"); // regex equivalent of '*'
276 string r_one("."); // regex equivalent of '?'
277 string::size_type pos;
279 // replace all "*" in input with ".*"
280 for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
281 regexed = regexed.replace(pos, 1, r_all);
283 // replace all "?" in input with "."
284 for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
285 regexed = regexed.replace(pos, 1, r_one);
287 DBG << " -> " << regexed << endl;
292 //! macro for word boundary tags for regexes
293 #define WB (_match_word ? string("\\b") : string())
295 string PoolQuery::Impl::createRegex(StrContainer & container)
299 if (container.empty())
302 if (container.size() == 1)
305 return ".*" + WB + *container.begin() + WB + ".*";
307 return *container.begin();
312 bool _use_wildcards = (_flags & SEARCH_STRINGMASK) == SEARCH_GLOB;
313 StrContainer::const_iterator it = container.begin();
317 tmp = wildcards2regex(*it);
321 if (!(_flags & SEARCH_STRING)) // not match exact
322 tmp += ".*" + WB + tmp;
323 rstr = "(?=" + tmp + ")";
327 if (_flags & SEARCH_STRING) // match exact
337 for (; it != container.end(); ++it)
340 tmp = wildcards2regex(*it);
344 if (!(_flags & SEARCH_STRING)) // not match exact
345 tmp += ".*" + WB + tmp;
346 rstr += "(?=" + tmp + ")";
356 if (!(_flags & SEARCH_STRING)) // not match exact
362 if (_flags & SEARCH_STRING) // match exact
372 PoolQuery::ResultIterator PoolQuery::Impl::begin()
376 // if only one repository has been specified, find it in the pool
377 sat::Pool pool(sat::Pool::instance());
378 sat::Pool::RepositoryIterator itr = pool.reposBegin();
379 if (!(_flags & SEARCH_ALL_REPOS) && _repos.size() == 1)
381 string theone = *_repos.begin();
382 for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
383 if (itr == pool.reposEnd())
385 RepoInfo info; info.setAlias(theone);
386 ERR << "Repository not found in sat pool." << endl;
387 ZYPP_THROW(repo::RepoNotFoundException(info));
391 DBG << "_flags:" << _flags << endl;
393 if (_rcattrs.empty())
395 ::dataiterator_init(&_rdit,
396 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
397 0, // search all solvables
398 0, // attribute id - only if 1 attr key specified
399 _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
402 else if (_rcattrs.size() == 1)
404 ::dataiterator_init(&_rdit,
405 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
406 0, // search all solvables
407 _rcattrs.begin()->first.id(), // keyname - attribute id - only if 1 attr key specified
408 _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
413 ::dataiterator_init(&_rdit,
414 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */
415 0, /*search all resolvables */
416 0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
417 0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
421 PoolQuery::ResultIterator it(this);
426 PoolQuery::ResultIterator PoolQuery::Impl::end()
428 INT << "end" << endl;
429 return PoolQuery::ResultIterator();
433 string PoolQuery::Impl::asString() const
437 o << "compiled: " << _compiled << endl;
440 for(Kinds::const_iterator it = _kinds.begin();
441 it != _kinds.end(); ++it)
446 for(StrContainer::const_iterator it = _repos.begin();
447 it != _repos.end(); ++it)
451 o << "string match flags:" << endl;
452 o << "* string/substring/glob/regex: " << (_flags & SEARCH_STRINGMASK) << endl;
453 o << "* SEARCH_NOCASE: " << ((_flags & SEARCH_NOCASE) ? "yes" : "no") << endl;
454 o << "* SEARCH_ALL_REPOS: " << ((_flags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
455 o << "status filter flags:" << _status_flags << endl;
460 for(StrContainer::const_iterator it = _strings.begin();
461 it != _strings.end(); ++it)
465 o << "attributes: " << endl;
466 for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
468 o << "* " << ai->first << ": ";
469 for(StrContainer::const_iterator vi = ai->second.begin();
470 vi != ai->second.end(); ++vi)
477 o << "regex compiled strings: " << _rcstrings << endl;
478 o << "regex compiled attributes:" << endl;
479 for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
480 o << "* " << ai->first << ": " << ai->second << endl;
485 /** \relates PoolQuery::Impl Stream output *//*
486 inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
488 return str << "PoolQuery::Impl";
491 ///////////////////////////////////////////////////////////////////
493 ///////////////////////////////////////////////////////////////////
495 // CLASS NAME : PoolQuery::ResultIterator
497 ///////////////////////////////////////////////////////////////////
499 PoolQuery::ResultIterator::ResultIterator(Impl * pqimpl)
500 : PoolQuery::ResultIterator::iterator_adaptor_(pqimpl ? &pqimpl->_rdit : 0)
501 , _rdit(pqimpl ? &pqimpl->_rdit : 0)
505 , _attrs(pqimpl->_rcattrs)
506 , _do_matching(false)
507 , _pool((sat::Pool::instance()))
509 if (_attrs.size() > 1)
513 void PoolQuery::ResultIterator::increment()
518 bool got_match = false;
521 DBG << "last: " << _sid << endl;
522 while (_has_next && !(got_match = matchSolvable()));
525 // no more solvables and the last did not match
526 if (!got_match && !_has_next)
528 base_reference() = 0;
532 DBG << "next: " << _sid << endl;
535 bool PoolQuery::ResultIterator::matchSolvable()
537 _sid = _rdit->solvid;
539 bool new_solvable = true;
540 bool matches = !_do_matching;
542 bool drop_by_kind_status = false;
543 bool drop_by_repo = false;
546 //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
547 in_repo = _sid >= _rdit->repo->start;
549 if (in_repo && new_solvable)
553 drop_by_repo = false;
554 if (!_pqimpl->_repos.empty() &&
555 _pqimpl->_repos.find(_rdit->repo->name) == _pqimpl->_repos.end())
561 drop_by_kind_status = false;
563 // whether to drop an uninstalled (repo) solvable
564 if ( (_pqimpl->_status_flags & INSTALLED_ONLY) &&
565 _rdit->repo->name != _pool.systemRepoName() )
567 drop_by_kind_status = true;
571 // whether to drop an installed (target) solvable
572 if ((_pqimpl->_status_flags & UNINSTALLED_ONLY) &&
573 _rdit->repo->name == _pool.systemRepoName())
575 drop_by_kind_status = true;
579 // whether to drop unwanted kind
580 if (!_pqimpl->_kinds.empty())
582 sat::Solvable s(_sid);
583 // the user wants to filter by kind.
584 if (_pqimpl->_kinds.find(s.kind()) == _pqimpl->_kinds.end())
585 drop_by_kind_status = true;
591 matches = matches && !drop_by_kind_status && !drop_by_repo;
594 if (_do_matching && !drop_by_kind_status)
596 if (!matches && in_repo)
598 SolvAttr attr(_rdit->key->name);
599 CompiledAttrMap::const_iterator ai = _attrs.find(attr);
600 if (ai != _attrs.end())
602 const string & sstr =
603 _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
604 const IdString & value =
605 IdString(_rdit->kv.id);
607 switch(_pqimpl->_flags & SEARCH_STRINGMASK)
610 if (_pqimpl->_flags & SEARCH_NOCASE)
611 matches = ! str::compareCI(sstr.c_str(), value.c_str());
613 matches = (sstr == value.asString());
615 case SEARCH_SUBSTRING:
616 if (_pqimpl->_flags & SEARCH_NOCASE)
617 matches = ::strcasestr(value.c_str(), sstr.c_str());
619 matches = (value.asString().find(sstr) != string::npos);
622 matches = !::fnmatch(sstr.c_str(), value.c_str(),
623 (_pqimpl->_flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
630 ERR << "invalid string matching type: "
631 << (_pqimpl->_flags & SEARCH_STRINGMASK) << endl;
634 INT << "value: " << value.asString() << endl
635 << " mstr: " << sstr << endl;
642 Repository nextRepo(Repository(_rdit->repo).nextInPool());
643 ::dataiterator_skip_repo(_rdit);
645 ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
646 drop_by_repo = false;
648 else if (drop_by_kind_status)
650 ::dataiterator_skip_solvable(_rdit);
651 drop_by_kind_status = false;
654 if ((_has_next = ::dataiterator_step(_rdit)))
656 new_solvable = _rdit->solvid != _sid;
658 _sid = _rdit->solvid;
660 // no more attributes in this repo, return
663 // check for more repos to jump to
664 if (!_pqimpl->_repos.empty())
666 Repository nextRepo(Repository(_rdit->repo).nextInPool());
669 ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
670 _has_next = ::dataiterator_step(_rdit);
674 // did the last solvable match conditions?
675 return matches && in_repo;
678 while (!new_solvable || !in_repo);
683 ///////////////////////////////////////////////////////////////////
685 // CLASS NAME : PoolQuery
687 ///////////////////////////////////////////////////////////////////
689 PoolQuery::PoolQuery()
694 PoolQuery::~PoolQuery()
698 void PoolQuery::addRepo(const std::string &repoalias)
700 _pimpl->_repos.insert(repoalias);
701 _pimpl->_flags &= ~SEARCH_ALL_REPOS;
705 void PoolQuery::addKind(const Resolvable::Kind &kind)
706 { _pimpl->_kinds.insert(kind); }
709 void PoolQuery::addString(const string & value)
710 { _pimpl->_strings.insert(value); }
713 void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
714 { _pimpl->_attrs[attr].insert(value); }
717 void PoolQuery::setCaseSensitive(const bool value)
720 _pimpl->_flags &= ~SEARCH_NOCASE;
722 _pimpl->_flags |= SEARCH_NOCASE;
726 void PoolQuery::setMatchSubstring()
727 { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
728 void PoolQuery::setMatchExact()
729 { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
730 void PoolQuery::setMatchRegex()
731 { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
732 void PoolQuery::setMatchGlob()
733 { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
734 void PoolQuery::setMatchWord()
736 _pimpl->_match_word = true;
737 _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
740 void PoolQuery::setFlags(int flags)
741 { _pimpl->_flags = flags; }
744 void PoolQuery::setInstalledOnly()
745 { _pimpl->_status_flags = INSTALLED_ONLY; }
746 void PoolQuery::setUninstalledOnly()
747 { _pimpl->_status_flags = UNINSTALLED_ONLY; }
748 void PoolQuery::setStatusFilterFlags( int flags )
749 { _pimpl->_status_flags = flags; }
752 void PoolQuery::setRequireAll(const bool require_all)
753 { _pimpl->_require_all = require_all; }
756 const PoolQuery::StrContainer &
757 PoolQuery::strings() const
758 { return _pimpl->_strings; }
760 const PoolQuery::AttrMap &
761 PoolQuery::attributes() const
762 { return _pimpl->_attrs; }
764 const PoolQuery::Kinds &
765 PoolQuery::kinds() const
766 { return _pimpl->_kinds; }
768 const PoolQuery::StrContainer &
769 PoolQuery::repos() const
770 { return _pimpl->_repos; }
772 bool PoolQuery::caseSensitive() const
773 { return _pimpl->_flags & SEARCH_NOCASE; }
775 bool PoolQuery::matchExact() const
776 { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
777 bool PoolQuery::matchSubstring() const
778 { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
779 bool PoolQuery::matchGlob() const
780 { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
781 bool PoolQuery::matchRegex() const
782 { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
783 int PoolQuery::matchType() const
784 { return _pimpl->_flags & SEARCH_STRINGMASK; }
786 bool PoolQuery::matchWord() const
787 { return _pimpl->_match_word; }
789 bool PoolQuery::requireAll() const
790 { return _pimpl->_require_all; }
794 PoolQuery::ResultIterator PoolQuery::begin()
795 { return _pimpl->begin(); }
798 PoolQuery::ResultIterator PoolQuery::end()
799 { return _pimpl->end(); }
802 bool PoolQuery::empty()
803 { return _pimpl->begin() == _pimpl->end(); }
805 //! \todo collect the result, reuse if not dirty
806 PoolQuery::size_type PoolQuery::size()
809 for(ResultIterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
814 void PoolQuery::execute(ProcessResolvable fnc)
816 invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc);
820 if (!_pimpl->_strings.empty())
821 term = *_pimpl->_strings.begin();
823 sat::Pool pool(sat::Pool::instance());
824 for ( sat::Pool::RepositoryIterator itr = pool.reposBegin();
825 itr != pool.reposEnd();
828 // filter by installed uninstalled
829 if ( ( _pimpl->_status_flags & INSTALLED_ONLY ) && (itr->name() != sat::Pool::instance().systemRepoName()) )
832 if ( ( _pimpl->_status_flags & UNINSTALLED_ONLY ) && (itr->name() == sat::Pool::instance().systemRepoName()) )
835 // is this repo in users repos?
836 bool included = ( find(_pimpl->_repos.begin(), _pimpl->_repos.end(), itr->name()) != _pimpl->_repos.end() );
838 // only look in user repos filter if the filter is not empty
839 // in this case we search in all
840 if ( _pimpl->_repos.empty() || included )
842 repo_search( itr->get(), 0, 0, term.c_str(), _pimpl->_flags, Impl::repo_search_cb, (void*) (this));
849 ///////////////////////////////////////////////////////////////////
851 // CLASS NAME : PoolQuery::Impl
854 * represents all atributes in PoolQuery except SolvAtributes, which are
855 * used as is (not needed extend anything if someone adds new solv attr)
857 struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
860 friend class IdStringType<PoolQueryAttr>;
866 PoolQueryAttr():isSolvAttr(false){}
868 explicit PoolQueryAttr( const char* cstr_r )
869 : _str( cstr_r ),isSolvAttr(false){}
871 explicit PoolQueryAttr( const std::string & str_r )
872 : _str( str_r ),isSolvAttr(false)
875 sat::SolvAttr sa(str_r);
876 if( sa != sat::SolvAttr::noAttr )
884 static const PoolQueryAttr noAttr;
887 static const PoolQueryAttr nameAttr;
888 static const PoolQueryAttr repoAttr;
889 static const PoolQueryAttr kindAttr;
891 // exported attributes from SolvAtributes
895 const PoolQueryAttr PoolQueryAttr::noAttr;
897 const PoolQueryAttr PoolQueryAttr::nameAttr( "name" );
898 const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
899 const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
901 ///////////////////////////////////////////////////////////////////
904 //\TODO maybe ctor with stream can be usefull
905 bool PoolQuery::recover( istream &str, char delim )
907 bool finded_something = false; //indicates some atributes is finded
913 getline( str, s, delim );
915 if ((!s.empty()) && s[0]=='#') //comment
920 string::size_type pos = s.find(':');
921 if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
923 if (finded_something) //is first blank line after record?
933 finded_something = true;
935 string atrName(str::trim(string(s,0,pos))); // trimmed name of atribute
936 string atrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
938 PoolQueryAttr attribute( atrName );
940 if ( attribute==PoolQueryAttr::nameAttr)
942 //setName...maybe some regex test
945 else if ( attribute==PoolQueryAttr::repoAttr )
949 else if ( attribute==PoolQueryAttr::kindAttr )
951 addKind( Resolvable::Kind(atrValue) );
953 else if ( attribute==PoolQueryAttr::noAttr )
955 if (attribute.isSolvAttr)
961 //log unknwon atribute
966 //some forget handle new atribute
972 return finded_something;
975 void PoolQuery::serialize( ostream &str, char delim ) const
979 //iterate thrue all settings and write it
981 for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
983 str << "repo: " << *it << delim ;
986 for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
988 str << "kind: " << it->idStr() << delim ;
991 //separating delim - protection
997 string PoolQuery::asString() const
998 { return _pimpl->asString(); }
1001 ostream & operator<<( ostream & str, const PoolQuery & obj )
1002 { return str << obj.asString(); }
1004 bool operator==(const PoolQuery& a, const PoolQuery& b)
1009 //internal matching two containers O(n^2)
1010 template <class Container>
1011 bool equalContainers(const Container& a, const Container& b)
1013 for_(it,a.begin(),a.end())
1015 bool finded = false;
1016 for_( it2, b.begin(),b.end() )
1031 bool equal(const PoolQuery& a, const PoolQuery& b)
1033 if( a.matchType()!=b.matchType() )
1035 if( a.matchWord()!=b.matchWord())
1037 if( a.requireAll()!=b.requireAll() )
1039 if(!equalContainers(a.strings(), b.strings()))
1041 if(!equalContainers(a.kinds(), b.kinds()))
1043 if(!equalContainers(a.repos(), b.repos()))
1045 if(!equalContainers(a.attributes(), b.attributes()))
1052 /////////////////////////////////////////////////////////////////
1054 ///////////////////////////////////////////////////////////////////