- multiple repositories filter reenabled
[platform/upstream/libzypp.git] / zypp / PoolQuery.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/PoolQuery.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14 #include <list>
15 #include <vector>
16 #include <algorithm>
17 #include <fnmatch.h>
18
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"
26
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"
31
32 extern "C"
33 {
34 #include "satsolver/repo.h"
35 }
36
37 #include "zypp/PoolQuery.h"
38
39 using namespace std;
40 using namespace zypp::sat;
41
42 ///////////////////////////////////////////////////////////////////
43 namespace zypp
44 { /////////////////////////////////////////////////////////////////
45
46   ///////////////////////////////////////////////////////////////////
47   //
48   //  CLASS NAME : PoolQuery::Impl
49   //
50   class PoolQuery::Impl
51   {
52   public:
53     Impl()
54       : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
55       , _status_flags(ALL)
56       , _match_word(false) 
57       , _require_all(false)
58       , _compiled(false)
59     {}
60
61     ~Impl()
62     {}
63
64   public:
65 /*
66     static int repo_search_cb(void *cbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
67     {
68       PoolQuery *me = (PoolQuery*) cbdata;
69
70       bool r = false;
71
72       sat::Solvable solvable(s - sat::Pool::instance().get()->solvables);
73
74       // now filter by kind here (we cant do it before)
75       if ( ! me->_pimpl->_kinds.empty() )
76       {
77         // the user wants to filter by kind.
78         if ( find( me->_pimpl->_kinds.begin(),
79                    me->_pimpl->_kinds.end(),
80                    solvable.kind() )
81              == me->_pimpl->_kinds.end() )
82         {
83           // we did not find the kind in the list
84           // so this is not a result.
85           return SEARCH_NEXT_SOLVABLE;
86         }
87       }
88
89       if (me->_pimpl->_fnc)
90         r = me->_pimpl->_fnc( solvable );//makeResObject(solvable) );
91       
92       if (!r)
93         return SEARCH_STOP;
94       return SEARCH_NEXT_SOLVABLE;
95     }
96   */  
97     ResultIterator begin();
98     ResultIterator end();
99     
100     string asString() const;
101     
102   private:
103     void compile();
104     string createRegex(StrContainer & container);
105
106   public:
107     /** Raw search strings. */
108     StrContainer _strings;
109     /** Regex-compiled search strings. */
110     string _rcstrings;
111     /** Raw attributes */
112     AttrMap _attrs;
113     /** Regex-compiled attributes */
114     CompiledAttrMap _rcattrs;
115
116     /** Repos to search. */
117     StrContainer _repos;
118     /** Kinds to search */
119     Kinds _kinds;
120
121     /** Sat solver search flags */
122     int _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() */
126     int _flags_old;
127     /** Sat solver status flags */
128     int _status_flags;
129
130     bool _match_word;
131
132     bool _require_all;
133
134     /** Sat solver Dataiterator structure */
135     ::_Dataiterator _rdit;
136
137     bool _compiled;
138
139     /** Function for processing found solvables. Used in execute(). */
140     mutable PoolQuery::ProcessResolvable _fnc;
141   private:
142     friend Impl * rwcowClone<Impl>( const Impl * rhs );
143     /** clone for RWCOW_pointer */
144     Impl * clone() const
145     { return new Impl( *this ); }
146   };
147 /*
148   template <class _OutputIterator>
149   struct CollectNonEmpty
150   {
151     CollectNonEmpty( _OutputIterator iter_r ) : _iter( iter_r ) {}
152
153     template<class _Tp>
154     bool operator()( const _Tp & value_r ) const
155     {
156       if (value_r.empty())
157         return true;
158       *_iter++ = value_r;
159       return true;
160     }
161
162     private:
163       mutable _OutputIterator _iter;
164   };
165 */
166   void PoolQuery::Impl::compile()
167   {
168     // backup the flags
169     _flags_old = _flags;
170
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
175     
176     // // NO ATTRIBUTE
177     // else
178     //   for all _strings
179     //     create regex; store in _rcstrings; if more strings flag regex;
180     if (_attrs.empty())
181     {
182       _rcstrings = createRegex(_strings);
183       if (_strings.size() > 1)
184         _flags = (_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
185     }
186
187     // // ONE ATTRIBUTE 
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)
192     {
193       StrContainer joined;
194       for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
195         if (!it->empty())
196           joined.insert(*it);
197       for(StrContainer::const_iterator it = _attrs.begin()->second.begin(); it != _attrs.begin()->second.end(); ++it)
198         if (!it->empty())
199           joined.insert(*it);
200       _rcstrings = createRegex(joined);
201       _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
202     }
203
204
205     // // MULTIPLE ATTRIBUTES
206     else
207     {
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++)
213             if (!it->empty())
214             {
215               attrvals_empty = false;
216               goto attremptycheckend;
217             }
218
219 attremptycheckend:
220
221       bool attrvals_thesame = true;
222       for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
223       {
224         
225       }
226
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)
232       {
233         if (attrvals_empty)
234         {
235           // compile the search string
236           StrContainer joined;
237           for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
238             if (!it->empty())
239               joined.insert(*it);
240           _rcstrings = createRegex(joined);
241
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()));
245         }
246       }
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;
251       else
252       {
253         
254       }
255     }
256
257     // tell the Dataiterator to search only in one repo if only one specified
258     if (_repos.size() == 1)
259       _flags &= ~SEARCH_ALL_REPOS;
260
261     _compiled = true;
262     
263     DBG << asString() << endl;
264   }
265
266   /**
267    * Converts '*' and '?' wildcards within str into their regex equivalents.
268    */
269   static string wildcards2regex(const string & str)
270   {
271     string regexed = str;
272
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;
278
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);
282
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);
286
287     DBG << " -> " << regexed << endl;
288
289     return regexed;
290   }
291
292 //! macro for word boundary tags for regexes
293 #define WB (_match_word ? string("\\b") : string())
294
295   string PoolQuery::Impl::createRegex(StrContainer & container)
296   {
297     string rstr;
298
299     if (container.empty())
300       return rstr;
301
302     if (container.size() == 1)
303     {
304       if (_match_word)
305         return ".*" + WB + *container.begin() + WB + ".*";
306
307       return *container.begin();
308     }
309
310     // multiple strings
311
312     bool _use_wildcards = (_flags & SEARCH_STRINGMASK) == SEARCH_GLOB;
313     StrContainer::const_iterator it = container.begin();
314     string tmp;
315
316     if (_use_wildcards)
317       tmp = wildcards2regex(*it);
318
319     if (_require_all)
320     {
321       if (!(_flags & SEARCH_STRING)) // not match exact
322         tmp += ".*" + WB + tmp;
323       rstr = "(?=" + tmp + ")";
324     }
325     else
326     {
327       if (_flags & SEARCH_STRING) // match exact
328         rstr = "^";
329       else
330         rstr = ".*" + WB;
331
332       rstr += "(" + tmp;
333     }
334
335     ++it;
336
337     for (; it != container.end(); ++it)
338     {
339       if (_use_wildcards)
340         tmp = wildcards2regex(*it);
341
342       if (_require_all)
343       {
344         if (!(_flags & SEARCH_STRING)) // not match exact
345           tmp += ".*" + WB + tmp;
346         rstr += "(?=" + tmp + ")";
347       }
348       else
349       {
350         rstr += "|" + tmp;
351       }
352     }
353
354     if (_require_all)
355     {
356       if (!(_flags & SEARCH_STRING)) // not match exact
357         rstr += WB + ".*";
358     }
359     else
360     {
361       rstr += ")";
362       if (_flags & SEARCH_STRING) // match exact
363         rstr += "$";
364       else
365         rstr += WB + ".*";
366     }
367
368     return rstr;
369   }
370
371
372   PoolQuery::ResultIterator PoolQuery::Impl::begin()
373   {
374     compile();
375
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)
380     {
381       string theone = *_repos.begin();
382       for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
383       if (itr == pool.reposEnd())
384       {
385         RepoInfo info; info.setAlias(theone);
386         ERR << "Repository not found in sat pool." <<  endl;
387         ZYPP_THROW(repo::RepoNotFoundException(info));
388       }
389     }
390
391     DBG << "_flags:" << _flags << endl;
392
393     if (_rcattrs.empty())
394     {
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
400       _flags);
401     }
402     else if (_rcattrs.size() == 1)
403     {
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 
409         _flags);
410     }
411     else
412     {
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 */
418         _flags);
419     }
420
421     PoolQuery::ResultIterator it(this);
422     it.increment();
423     return it;
424   }
425
426   PoolQuery::ResultIterator PoolQuery::Impl::end()
427   {
428     INT << "end" << endl;
429     return PoolQuery::ResultIterator();
430   }
431
432
433   string PoolQuery::Impl::asString() const
434   {
435     ostringstream o;
436
437     o << "compiled: " << _compiled << endl;
438     
439     o << "kinds: ";
440     for(Kinds::const_iterator it = _kinds.begin();
441         it != _kinds.end(); ++it)
442       o << *it << " ";
443     o << endl;
444
445     o << "repos: ";
446     for(StrContainer::const_iterator it = _repos.begin();
447         it != _repos.end(); ++it)
448       o << *it << " ";
449     o << endl;
450
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;
456
457     // raw
458
459     o << "strings: ";
460     for(StrContainer::const_iterator it = _strings.begin();
461         it != _strings.end(); ++it)
462       o << *it << " ";
463     o << endl;
464
465     o << "attributes: " << endl;
466     for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
467     {
468       o << "* " << ai->first << ": ";
469       for(StrContainer::const_iterator vi = ai->second.begin();
470           vi != ai->second.end(); ++vi)
471         o << *vi << " ";
472       o << endl;
473     }
474
475     // compiled
476
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;
481
482     return o.str();
483   }
484
485   /** \relates PoolQuery::Impl Stream output *//*
486   inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
487   {
488     return str << "PoolQuery::Impl";
489   }
490   */
491   ///////////////////////////////////////////////////////////////////
492
493   ///////////////////////////////////////////////////////////////////
494   //
495   //  CLASS NAME : PoolQuery::ResultIterator
496   //
497   ///////////////////////////////////////////////////////////////////
498
499   PoolQuery::ResultIterator::ResultIterator(Impl * pqimpl)
500   : PoolQuery::ResultIterator::iterator_adaptor_(pqimpl ? &pqimpl->_rdit : 0)
501   , _rdit(pqimpl ? &pqimpl->_rdit : 0)
502   , _pqimpl(pqimpl)
503   , _sid(0)
504   , _has_next(true)
505   , _attrs(pqimpl->_rcattrs)
506   , _do_matching(false)
507   , _pool((sat::Pool::instance()))
508   {
509     if (_attrs.size() > 1)
510       _do_matching = true;
511   }
512
513   void PoolQuery::ResultIterator::increment()
514   {
515     if (!_rdit)
516       return;
517
518     bool got_match = false;
519     if (_has_next)
520     {
521       DBG << "last: " << _sid << endl;
522       while (_has_next && !(got_match = matchSolvable()));
523     }
524
525     // no more solvables and the last did not match
526     if (!got_match && !_has_next)
527     {
528       base_reference() = 0;
529       _sid = 0;
530     }
531
532     DBG << "next: " << _sid << endl;
533   }
534
535   bool PoolQuery::ResultIterator::matchSolvable()
536   {
537     _sid = _rdit->solvid;
538
539     bool new_solvable = true;
540     bool matches = !_do_matching;
541     bool in_repo;
542     bool drop_by_kind_status = false;
543     bool drop_by_repo = false;
544     do
545     {
546       //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
547       in_repo = _sid >= _rdit->repo->start;
548
549       if (in_repo && new_solvable)
550       {
551         while(1)
552         {
553           drop_by_repo = false;
554           if (!_pqimpl->_repos.empty() && 
555             _pqimpl->_repos.find(_rdit->repo->name) == _pqimpl->_repos.end())
556           {
557             drop_by_repo = true;
558             break;
559           }
560
561           drop_by_kind_status = false;
562
563           // whether to drop an uninstalled (repo) solvable
564           if ( (_pqimpl->_status_flags & INSTALLED_ONLY) &&
565                _rdit->repo->name != _pool.systemRepoName() )
566           {
567             drop_by_kind_status = true;
568             break;
569           }
570
571           // whether to drop an installed (target) solvable
572           if ((_pqimpl->_status_flags & UNINSTALLED_ONLY) &&
573                _rdit->repo->name == _pool.systemRepoName())
574           {
575             drop_by_kind_status = true;
576             break;
577           }
578
579           // whether to drop unwanted kind
580           if (!_pqimpl->_kinds.empty())
581           {
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;
586           }
587
588           break;
589         }
590
591         matches = matches && !drop_by_kind_status && !drop_by_repo;
592       }
593
594       if (_do_matching && !drop_by_kind_status)
595       {
596         if (!matches && in_repo)
597         {
598           SolvAttr attr(_rdit->key->name);
599           CompiledAttrMap::const_iterator ai = _attrs.find(attr);
600           if (ai != _attrs.end())
601           {
602             const string & sstr =
603               _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
604             const IdString & value =
605               IdString(_rdit->kv.id);
606
607             switch(_pqimpl->_flags & SEARCH_STRINGMASK)
608             {
609             case SEARCH_STRING:
610               if (_pqimpl->_flags & SEARCH_NOCASE)
611                 matches = ! str::compareCI(sstr.c_str(), value.c_str());
612               else
613                 matches = (sstr == value.asString());
614               break;
615             case SEARCH_SUBSTRING:
616               if (_pqimpl->_flags & SEARCH_NOCASE)
617                 matches = ::strcasestr(value.c_str(), sstr.c_str());
618               else
619                 matches = (value.asString().find(sstr) != string::npos);
620               break;
621             case SEARCH_GLOB:
622               matches = !::fnmatch(sstr.c_str(), value.c_str(),
623                   (_pqimpl->_flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
624               break;
625             case SEARCH_REGEX:
626               matches = false;
627               break;
628             default:
629               matches = false;
630               ERR << "invalid string matching type: "
631                   << (_pqimpl->_flags & SEARCH_STRINGMASK) << endl;
632             }
633             if (matches)
634               INT << "value: " << value.asString() << endl
635                   << " mstr: " <<  sstr << endl; 
636           }
637         }
638       }
639
640       if (drop_by_repo)
641       {
642         Repository nextRepo(Repository(_rdit->repo).nextInPool());
643         ::dataiterator_skip_repo(_rdit);
644         if (nextRepo)
645           ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
646         drop_by_repo = false;
647       }
648       else if (drop_by_kind_status)
649       {
650         ::dataiterator_skip_solvable(_rdit);
651         drop_by_kind_status = false;
652       }
653
654       if ((_has_next = ::dataiterator_step(_rdit)))
655       {
656         new_solvable = _rdit->solvid != _sid;
657         if (!in_repo)
658           _sid = _rdit->solvid;
659       }
660       // no more attributes in this repo, return
661       else
662       {
663         // check for more repos to jump to
664         if (!_pqimpl->_repos.empty())
665         {
666           Repository nextRepo(Repository(_rdit->repo).nextInPool());
667           if (nextRepo)
668           {
669             ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
670             _has_next = ::dataiterator_step(_rdit);
671           }
672         }
673
674         // did the last solvable match conditions?
675         return matches && in_repo;
676       }
677     }
678     while (!new_solvable || !in_repo);
679
680     return matches;
681   }
682
683   ///////////////////////////////////////////////////////////////////
684   //
685   //    CLASS NAME : PoolQuery
686   //
687   ///////////////////////////////////////////////////////////////////
688
689   PoolQuery::PoolQuery()
690     : _pimpl(new Impl())
691   {}
692
693
694   PoolQuery::~PoolQuery()
695   {}
696
697
698   void PoolQuery::addRepo(const std::string &repoalias)
699   {
700     _pimpl->_repos.insert(repoalias);
701     _pimpl->_flags &= ~SEARCH_ALL_REPOS;
702   }
703
704
705   void PoolQuery::addKind(const Resolvable::Kind &kind)
706   { _pimpl->_kinds.insert(kind); }
707
708
709   void PoolQuery::addString(const string & value)
710   { _pimpl->_strings.insert(value); }
711
712
713   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
714   { _pimpl->_attrs[attr].insert(value); }
715
716
717   void PoolQuery::setCaseSensitive(const bool value)
718   {
719     if (value)
720       _pimpl->_flags &= ~SEARCH_NOCASE;
721     else
722       _pimpl->_flags |= SEARCH_NOCASE;
723   }
724
725
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()
735   {
736     _pimpl->_match_word = true;
737     _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
738   }
739
740   void PoolQuery::setFlags(int flags)
741   { _pimpl->_flags = flags; }
742
743
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; }
750
751
752   void PoolQuery::setRequireAll(const bool require_all)
753   { _pimpl->_require_all = require_all; }
754
755
756   const PoolQuery::StrContainer &
757   PoolQuery::strings() const
758   { return _pimpl->_strings; }
759
760   const PoolQuery::AttrMap &
761   PoolQuery::attributes() const
762   { return _pimpl->_attrs; }
763
764   const PoolQuery::Kinds &
765   PoolQuery::kinds() const
766   { return _pimpl->_kinds; }
767
768   const PoolQuery::StrContainer &
769   PoolQuery::repos() const
770   { return _pimpl->_repos; }
771
772   bool PoolQuery::caseSensitive() const
773   { return _pimpl->_flags & SEARCH_NOCASE; }
774
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; }
785
786   bool PoolQuery::matchWord() const
787   { return _pimpl->_match_word; }
788
789   bool PoolQuery::requireAll() const
790   { return _pimpl->_require_all; }
791
792
793
794   PoolQuery::ResultIterator PoolQuery::begin()
795   { return _pimpl->begin(); }
796
797
798   PoolQuery::ResultIterator PoolQuery::end()
799   { return _pimpl->end(); }
800
801
802   bool PoolQuery::empty()
803   { return _pimpl->begin() == _pimpl->end(); }
804
805   //! \todo collect the result, reuse if not dirty
806   PoolQuery::size_type PoolQuery::size()
807   {
808     size_type count = 0;
809     for(ResultIterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
810     return count;
811   }
812
813
814   void PoolQuery::execute(ProcessResolvable fnc)
815   {
816     invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc);
817     /*
818     _pimpl->_fnc = fnc;
819     string term;
820     if (!_pimpl->_strings.empty())
821       term = *_pimpl->_strings.begin();
822
823     sat::Pool pool(sat::Pool::instance());
824     for ( sat::Pool::RepositoryIterator itr = pool.reposBegin();
825           itr != pool.reposEnd();
826           ++itr )
827     {
828       // filter by installed uninstalled
829       if ( ( _pimpl->_status_flags & INSTALLED_ONLY ) && (itr->name() != sat::Pool::instance().systemRepoName()) )
830         continue;
831
832       if ( ( _pimpl->_status_flags & UNINSTALLED_ONLY ) && (itr->name() == sat::Pool::instance().systemRepoName()) )
833         continue;
834
835       // is this repo in users repos?
836       bool included = ( find(_pimpl->_repos.begin(), _pimpl->_repos.end(), itr->name()) != _pimpl->_repos.end() );
837
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  )
841       {
842         repo_search( itr->get(), 0, 0, term.c_str(), _pimpl->_flags, Impl::repo_search_cb, (void*) (this));
843       }
844
845     }
846     */
847   }
848
849   ///////////////////////////////////////////////////////////////////
850   //
851   //  CLASS NAME : PoolQuery::Impl
852   //
853   /**
854    * represents all atributes in PoolQuery except SolvAtributes, which are
855    * used as is (not needed extend anything if someone adds new solv attr)
856    */
857   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
858   {
859     private:
860       friend class IdStringType<PoolQueryAttr>;
861       IdString _str;
862
863     public:
864     
865     //noAttr
866     PoolQueryAttr():isSolvAttr(false){}
867
868     explicit PoolQueryAttr( const char* cstr_r )
869         : _str( cstr_r ),isSolvAttr(false){}
870
871     explicit PoolQueryAttr( const std::string & str_r )
872         : _str( str_r ),isSolvAttr(false)
873     {
874       if( _str==noAttr ){
875         sat::SolvAttr sa(str_r);
876         if( sa != sat::SolvAttr::noAttr )
877         {
878           isSolvAttr = true; 
879         }
880       }
881     }
882
883     //unknown atributes
884     static const PoolQueryAttr noAttr;
885
886     // own attributes
887     static const PoolQueryAttr nameAttr;
888     static const PoolQueryAttr repoAttr;
889     static const PoolQueryAttr kindAttr;
890
891     // exported attributes from SolvAtributes
892     bool isSolvAttr;
893   };
894
895   const PoolQueryAttr PoolQueryAttr::noAttr;
896
897   const PoolQueryAttr PoolQueryAttr::nameAttr( "name" );
898   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
899   const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
900
901   ///////////////////////////////////////////////////////////////////
902
903
904   //\TODO maybe ctor with stream can be usefull
905   bool PoolQuery::recover( istream &str, char delim )
906   {
907     bool finded_something = false; //indicates some atributes is finded
908     string s;
909     do {
910       if ( str.eof() )
911         break;
912
913       getline( str, s, delim );
914
915       if ((!s.empty()) && s[0]=='#') //comment
916       {
917         continue;
918       }
919
920       string::size_type pos = s.find(':');
921       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
922       {
923         if (finded_something) //is first blank line after record?
924         {
925           break;
926         }
927         else
928         {
929           continue;
930         }
931       }
932
933       finded_something = true;
934
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
937
938       PoolQueryAttr attribute( atrName );
939
940       if ( attribute==PoolQueryAttr::nameAttr)
941       {
942         //setName...maybe some regex test
943         break;
944       }
945       else if ( attribute==PoolQueryAttr::repoAttr )
946       {
947         addRepo( atrValue );
948       }
949       else if ( attribute==PoolQueryAttr::kindAttr )
950       {
951         addKind( Resolvable::Kind(atrValue) );
952       }
953       else if ( attribute==PoolQueryAttr::noAttr )
954       {
955         if (attribute.isSolvAttr)
956         {
957           //setAtribute
958         }
959         else
960         {
961           //log unknwon atribute
962         }
963       }
964       else
965       {
966         //some forget handle new atribute
967         ;
968       }
969       
970     } while ( true );
971
972     return finded_something;
973   }
974
975   void PoolQuery::serialize( ostream &str, char delim ) const
976   {
977     //separating delim
978     str << delim; 
979     //iterate thrue all settings and write it
980     
981     for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
982     {
983       str << "repo: " << *it << delim ;
984     }
985
986     for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
987     {
988       str << "kind: " << it->idStr() << delim ;
989     }
990
991     //separating delim - protection
992     str << delim; 
993
994   }
995
996
997   string PoolQuery::asString() const
998   { return _pimpl->asString(); }
999
1000
1001   ostream & operator<<( ostream & str, const PoolQuery & obj )
1002   { return str << obj.asString(); }
1003
1004   bool operator==(const PoolQuery& a, const PoolQuery& b)
1005   {
1006     return equal(a,b);
1007   }
1008
1009   //internal matching two containers O(n^2)
1010   template <class Container>
1011   bool equalContainers(const Container& a, const Container& b)
1012   {
1013     for_(it,a.begin(),a.end())
1014     {
1015       bool finded = false;
1016       for_( it2, b.begin(),b.end() )
1017       {
1018         if (*it==*it2)
1019         {
1020           finded = true;
1021           break;
1022         }
1023       }
1024
1025       if (!finded)
1026         return false;
1027     }
1028     return true;
1029   }
1030
1031   bool equal(const PoolQuery& a, const PoolQuery& b)
1032   {
1033     if( a.matchType()!=b.matchType() )
1034       return false;
1035     if( a.matchWord()!=b.matchWord())
1036       return false;
1037     if( a.requireAll()!=b.requireAll() )
1038       return false;
1039     if(!equalContainers(a.strings(), b.strings()))
1040       return false;
1041     if(!equalContainers(a.kinds(), b.kinds()))
1042       return false;
1043     if(!equalContainers(a.repos(), b.repos()))
1044       return false;
1045     if(!equalContainers(a.attributes(), b.attributes()))
1046       return false;
1047
1048     return true;
1049   }
1050
1051
1052   /////////////////////////////////////////////////////////////////
1053 } // namespace zypp
1054 ///////////////////////////////////////////////////////////////////
1055