- make use of LookupAttr::iterator for the common stuff
[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 <boost/algorithm/string/replace.hpp>
15
16 #include "zypp/base/Gettext.h"
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/Regex.h"
19 #include "zypp/base/Algorithm.h"
20 #include "zypp/base/String.h"
21 #include "zypp/repo/RepoException.h"
22
23 #include "zypp/sat/Pool.h"
24 #include "zypp/sat/Solvable.h"
25
26 #include "zypp/PoolQuery.h"
27
28 extern "C"
29 {
30 #include "satsolver/repo.h"
31 }
32
33 using namespace std;
34 using namespace zypp::sat;
35
36 ///////////////////////////////////////////////////////////////////
37 namespace zypp
38 { /////////////////////////////////////////////////////////////////
39
40   ///////////////////////////////////////////////////////////////////
41   //
42   //  CLASS NAME : PoolQuery::Impl
43   //
44   class PoolQuery::Impl
45   {
46   public:
47     Impl()
48       : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
49       , _status_flags(ALL)
50       , _match_word(false) 
51       , _require_all(false)
52       , _compiled(false)
53     {}
54     
55     ~Impl()
56     {}
57
58   public:
59     const_iterator begin() const;
60     const_iterator end() const;
61     
62     string asString() const;
63     
64     void compile() const;
65   private:
66     string createRegex(const StrContainer & container) const;
67
68   public:
69     /** Raw search strings. */
70     StrContainer _strings;
71     /** Regex-compiled search strings. */
72     mutable string _rcstrings;
73     mutable regex_t _regex;
74     /** Raw attributes */
75     AttrMap _attrs;
76     /** Regex-compiled attributes */
77     mutable CompiledAttrMap _rcattrs;
78     mutable map<sat::SolvAttr, regex_t> _rattrs;
79
80     /** Repos to search. */
81     StrContainer _repos;
82     /** Kinds to search */
83     Kinds _kinds;
84
85     /** Sat solver search flags */
86     int _flags;
87     /** Backup of search flags. compile() may change the flags if needed, so
88      * in order to reuse the query, the original flags need to be stored
89      * at the start of compile() */
90     mutable int _cflags;
91     /** Sat solver status flags */
92     PoolQuery::StatusFilter _status_flags;
93
94     bool _match_word;
95
96     bool _require_all;
97
98     mutable bool _compiled;
99
100   private:
101     friend Impl * rwcowClone<Impl>( const Impl * rhs );
102     /** clone for RWCOW_pointer */
103     Impl * clone() const
104     { return new Impl( *this ); }
105   };
106
107   
108   static void
109   compileRegex(regex_t * regex, const string & str, bool nocase)
110   {
111     /* We feed multiple lines eventually (e.g. authors or descriptions),
112        so set REG_NEWLINE. */
113     if (regcomp(regex, str.c_str(),
114         REG_EXTENDED | REG_NOSUB | REG_NEWLINE | (nocase ? REG_ICASE : 0)) != 0)
115       ZYPP_THROW(Exception(
116         str::form(_("Invalid regular expression '%s'"), str.c_str())));
117   }
118
119   struct MyInserter
120   {
121     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
122     
123     bool operator()(const string & str)
124     {
125       _cont.insert(str);
126       return true;
127     }
128     
129     PoolQuery::StrContainer & _cont;
130   };
131
132   
133   struct EmptyFilter
134   {
135     bool operator()(const string & str)
136     {
137       return !str.empty();
138     }
139   };
140
141
142   void PoolQuery::Impl::compile() const
143   {
144     _cflags = _flags;
145
146     // 'different'         - will have to iterate through all and match by ourselves (slow)
147     // 'same'              - will pass the compiled string to dataiterator_init
148     // 'one-attr'          - will pass it to dataiterator_init
149     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
150     
151     // // NO ATTRIBUTE
152     // else
153     //   for all _strings
154     //     create regex; store in _rcstrings; if more strings flag regex;
155     if (_attrs.empty())
156     {
157       _rcstrings = createRegex(_strings);
158       if (_strings.size() > 1)
159         _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
160     }
161
162     // // ONE ATTRIBUTE 
163     // else if _attrs is not empty but it contains just one attr
164     //   for all _strings and _attr[key] strings
165     //     create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
166     else if (_attrs.size() == 1)
167     {
168       StrContainer joined;
169       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
170       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
171       _rcstrings = createRegex(joined);
172       _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
173     }
174
175     // // MULTIPLE ATTRIBUTES
176     else
177     {
178       // check whether there are any per-attribute strings 
179       bool attrvals_empty = true;
180       for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
181         if (!ai->second.empty())
182           for(StrContainer::const_iterator it = ai->second.begin();
183               it != ai->second.end(); it++)
184             if (!it->empty())
185             {
186               attrvals_empty = false;
187               goto attremptycheckend;
188             }
189 attremptycheckend:
190
191       // chceck whether the per-attribute strings are all the same
192       bool attrvals_thesame = true;
193       AttrMap::const_iterator ai = _attrs.begin();
194       const StrContainer & set1 = ai->second;
195       ++ai;
196       for (; ai != _attrs.end(); ++ai)
197       {
198         StrContainer result;
199         set_difference(
200           set1.begin(), set1.end(),
201           ai->second.begin(), ai->second.end(),
202           inserter(result, result.begin())/*, ltstr()*/);
203         if (!result.empty())
204         {
205           attrvals_thesame = false;
206           break;
207         }
208       }
209
210       // // THE SAME STRINGS FOR DIFFERENT ATTRS
211       // else if _attrs is not empty but it does not contain strings
212       //   for each key in _attrs take all _strings
213       //     create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
214       if (attrvals_empty || attrvals_thesame)
215       {
216         StrContainer joined;
217         if (attrvals_empty)
218         {
219           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
220           _rcstrings = createRegex(joined);
221         }
222         else
223         {
224           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
225           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
226           _rcstrings = createRegex(joined);
227         }
228         // copy the _attrs keys to _rcattrs
229         for_(ai, _attrs.begin(), _attrs.end())
230           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
231
232         if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
233           compileRegex(&_regex, _rcstrings, _cflags & SEARCH_NOCASE);
234       }
235
236       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
237       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
238       //   for each key in _attrs take all _strings + all _attrs[key] strings
239       //     create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
240       else
241       {
242         for_(ai, _attrs.begin(), _attrs.end())
243         {
244           StrContainer joined;
245           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
246           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
247           string s = createRegex(joined);
248           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, s));
249
250           if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
251           {
252             regex_t regex;
253             compileRegex(&regex, s, _cflags & SEARCH_NOCASE);
254             _rattrs.insert(pair<sat::SolvAttr, regex_t>(ai->first, regex));
255           }
256         }
257       }
258     }
259
260     // tell the Dataiterator to search only in one repo if only one specified
261     if (_repos.size() == 1)
262       _cflags &= ~SEARCH_ALL_REPOS;
263
264     _compiled = true;
265     
266     DBG << asString() << endl;
267   }
268
269   /**
270    * Converts '*' and '?' wildcards within str into their regex equivalents.
271    */
272   static string wildcards2regex(const string & str)
273   {
274     string regexed = str;
275
276     str::regex all("\\*"); // regex to search for '*'
277     str::regex one("\\?"); // regex to search for '?'
278     string r_all(".*"); // regex equivalent of '*'
279     string r_one(".");  // regex equivalent of '?'
280     string::size_type pos;
281
282     // replace all "*" in input with ".*"
283     for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
284       regexed = regexed.replace(pos, 1, r_all);
285
286     // replace all "?" in input with "."
287     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
288       regexed = regexed.replace(pos, 1, r_one);
289
290     DBG << " -> " << regexed << endl;
291
292     return regexed;
293   }
294
295 //! macro for word boundary tags for regexes
296 #define WB (_match_word ? string("\\b") : string())
297
298   string PoolQuery::Impl::createRegex(const StrContainer & container) const
299   {
300     string rstr;
301
302     if (container.empty())
303       return rstr;
304
305     if (container.size() == 1)
306     {
307       if (_match_word)
308         return ".*" + WB + *container.begin() + WB + ".*";
309
310       return *container.begin();
311     }
312
313     // multiple strings
314
315     bool use_wildcards = (_cflags & SEARCH_STRINGMASK) == SEARCH_GLOB;
316     StrContainer::const_iterator it = container.begin();
317     string tmp;
318
319     if (use_wildcards)
320       tmp = wildcards2regex(*it);
321
322     if (_require_all)
323     {
324       if (!(_cflags & SEARCH_STRING)) // not match exact
325         tmp += ".*" + WB + tmp;
326       rstr = "(?=" + tmp + ")";
327     }
328     else
329     {
330       if (_cflags & SEARCH_STRING) // match exact
331         rstr = "^";
332       else
333         rstr = ".*" + WB;
334
335       rstr += "(" + tmp;
336     }
337
338     ++it;
339
340     for (; it != container.end(); ++it)
341     {
342       if (use_wildcards)
343         tmp = wildcards2regex(*it);
344
345       if (_require_all)
346       {
347         if (!(_cflags & SEARCH_STRING)) // not match exact
348           tmp += ".*" + WB + tmp;
349         rstr += "(?=" + tmp + ")";
350       }
351       else
352       {
353         rstr += "|" + tmp;
354       }
355     }
356
357     if (_require_all)
358     {
359       if (!(_cflags & SEARCH_STRING)) // not match exact
360         rstr += WB + ".*";
361     }
362     else
363     {
364       rstr += ")";
365       if (_cflags & SEARCH_STRING) // match exact
366         rstr += "$";
367       else
368         rstr += WB + ".*";
369     }
370
371     return rstr;
372   }
373
374
375   PoolQuery::const_iterator PoolQuery::Impl::begin() const
376   {
377     compile();
378
379     // if only one repository has been specified, find it in the pool
380     sat::Pool pool(sat::Pool::instance());
381     sat::Pool::RepositoryIterator itr = pool.reposBegin();
382     if (!(_cflags & SEARCH_ALL_REPOS) && _repos.size() == 1)
383     {
384       string theone = *_repos.begin();
385       for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
386       if (itr == pool.reposEnd())
387       {
388         RepoInfo info; info.setAlias(theone);
389         ERR << "Repository not found in sat pool." <<  endl;
390         ZYPP_THROW(repo::RepoNotFoundException(info));
391       }
392     }
393
394     DBG << "_cflags:" << _cflags << endl;
395
396     scoped_ptr< ::_Dataiterator> _rdit( new ::Dataiterator );
397     // needed while LookupAttr::iterator::dip_equal does ::memcmp:
398     ::memset( _rdit.get(), 0, sizeof(::_Dataiterator) );
399
400     if (_rcattrs.empty())
401     {
402     ::dataiterator_init(_rdit.get(),
403       _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
404       0,                                           // search all solvables
405       0,                                           // attribute id - only if 1 attr key specified
406       _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
407       _cflags);
408     }
409     else if (_rcattrs.size() == 1)
410     {
411       ::dataiterator_init(_rdit.get(),
412         _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this 
413         0,                                           // search all solvables
414         _rcattrs.begin()->first.id(),                // keyname - attribute id - only if 1 attr key specified
415         _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string 
416         _cflags);
417     }
418     else
419     {
420       ::dataiterator_init(_rdit.get(),
421         _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */ 
422         0, /*search all resolvables */
423         0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
424         0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
425         _cflags);
426     }
427
428     if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX && _rdit->regex_err != 0)
429       ZYPP_THROW(Exception(str::form(
430           _("Invalid regular expression '%s': regcomp returned %d"),
431           _rcstrings.c_str(), _rdit->regex_err)));
432
433     PoolQuery::const_iterator it(_rdit, this);
434     it.increment();
435     return it;
436   }
437
438   PoolQuery::const_iterator PoolQuery::Impl::end() const
439   {
440     INT << "end" << endl;
441     return PoolQuery::const_iterator();
442   }
443
444
445   string PoolQuery::Impl::asString() const
446   {
447     ostringstream o;
448
449     o << "compiled: " << _compiled << endl;
450     
451     o << "kinds: ";
452     for(Kinds::const_iterator it = _kinds.begin();
453         it != _kinds.end(); ++it)
454       o << *it << " ";
455     o << endl;
456
457     o << "repos: ";
458     for(StrContainer::const_iterator it = _repos.begin();
459         it != _repos.end(); ++it)
460       o << *it << " ";
461     o << endl;
462
463     o << "string match flags:" << endl;
464     o << "* string/substring/glob/regex: " << (_cflags & SEARCH_STRINGMASK) << endl; 
465     o << "* SEARCH_NOCASE: " << ((_cflags & SEARCH_NOCASE) ? "yes" : "no") << endl;
466     o << "* SEARCH_ALL_REPOS: " << ((_cflags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
467     o << "status filter flags:" << _status_flags << endl;
468
469     // raw
470
471     o << "strings: ";
472     for(StrContainer::const_iterator it = _strings.begin();
473         it != _strings.end(); ++it)
474       o << *it << " ";
475     o << endl;
476
477     o << "attributes: " << endl;
478     for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
479     {
480       o << "* " << ai->first << ": ";
481       for(StrContainer::const_iterator vi = ai->second.begin();
482           vi != ai->second.end(); ++vi)
483         o << *vi << " ";
484       o << endl;
485     }
486
487     // compiled
488
489     o << "compiled strings: " << _rcstrings << endl;
490     o << "compiled attributes:" << endl;
491     for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
492       o << "* " << ai->first << ": " << ai->second << endl;
493
494     return o.str();
495   }
496
497   /** \relates PoolQuery::Impl Stream output *//*
498   inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
499   {
500     return str << "PoolQuery::Impl";
501   }
502   */
503   ///////////////////////////////////////////////////////////////////
504
505   ///////////////////////////////////////////////////////////////////
506   namespace detail
507   { /////////////////////////////////////////////////////////////////
508
509   ///////////////////////////////////////////////////////////////////
510   //
511   //  CLASS NAME : PoolQuery::ResultIterator
512   //
513   ///////////////////////////////////////////////////////////////////
514
515   PoolQueryIterator::PoolQueryIterator()
516     : _sid(0), _has_next(false), _do_matching(false)
517   { this->base_reference() = LookupAttr::iterator(); }
518
519
520   PoolQueryIterator::PoolQueryIterator(
521       scoped_ptr< ::_Dataiterator> & dip_r,
522       const PoolQuery::Impl * pqimpl)
523   : _pqimpl(pqimpl)
524   , _sid(0)
525   , _has_next(true)
526   , _do_matching(_pqimpl->_rcattrs.size() > 1)
527   {
528     this->base_reference() = LookupAttr::iterator(dip_r, true); //!\todo pass chain_repos
529     _has_next = (*base_reference() != sat::detail::noId); 
530   }
531
532
533   PoolQueryIterator::PoolQueryIterator(const PoolQueryIterator & rhs)
534     : _pqimpl(rhs._pqimpl)
535     , _sid(rhs._sid)
536     , _has_next(rhs._has_next)
537     , _do_matching(rhs._do_matching)
538   { base_reference() = LookupAttr::iterator(rhs.base()); }
539
540
541   PoolQueryIterator::~PoolQueryIterator()
542   {}
543
544
545   PoolQueryIterator & PoolQueryIterator::operator=( const PoolQueryIterator & rhs )
546   {
547     base_reference() = rhs.base();
548     _pqimpl = rhs._pqimpl; //! \todo FIXME
549     _sid = rhs._sid;
550     _has_next = rhs._has_next;
551     _do_matching = rhs._do_matching;
552     return *this;
553   }
554
555
556   void PoolQueryIterator::increment()
557   {
558     if (!base().get())
559       return;
560
561     bool got_match = false;
562     if (_has_next)
563     {
564       DBG << "last: " << _sid << endl;
565       while (_has_next && !(got_match = matchSolvable()));
566     }
567
568     // no more solvables and the last did not match
569     if (!got_match && !_has_next)
570     {
571       base_reference() = LookupAttr::iterator();
572       _sid = 0;
573     }
574
575     DBG << "next: " << _sid << endl;
576   }
577
578   bool PoolQueryIterator::matchSolvable()
579   {
580     _sid = base().get()->solvid;
581
582     bool new_solvable = true;
583     bool matches = !_do_matching;
584     bool in_repo;
585     bool drop_by_kind_status = false;
586     bool drop_by_repo = false;
587     do
588     {
589       //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
590       in_repo = _sid >= base().get()->repo->start;
591
592       if (in_repo && new_solvable)
593       {
594         while(1)
595         {
596           drop_by_repo = false;
597           if (!_pqimpl->_repos.empty() && 
598             _pqimpl->_repos.find(base().get()->repo->name) == _pqimpl->_repos.end())
599           {
600             drop_by_repo = true;
601             break;
602           }
603
604           drop_by_kind_status = false;
605
606           // whether to drop an uninstalled (repo) solvable
607           if ( (_pqimpl->_status_flags & PoolQuery::INSTALLED_ONLY) &&
608               base().get()->repo->name != sat::Pool::instance().systemRepoName() )
609           {
610             drop_by_kind_status = true;
611             break;
612           }
613
614           // whether to drop an installed (target) solvable
615           if ((_pqimpl->_status_flags & PoolQuery::UNINSTALLED_ONLY) &&
616               base().get()->repo->name == sat::Pool::instance().systemRepoName())
617           {
618             drop_by_kind_status = true;
619             break;
620           }
621
622           // whether to drop unwanted kind
623           if (!_pqimpl->_kinds.empty())
624           {
625             sat::Solvable s(_sid);
626             // the user wants to filter by kind.
627             if (_pqimpl->_kinds.find(s.kind()) == _pqimpl->_kinds.end())
628               drop_by_kind_status = true;
629           }
630
631           break;
632         }
633
634         matches = matches && !drop_by_kind_status && !drop_by_repo;
635       }
636
637       if (_do_matching && !drop_by_kind_status)
638       {
639         if (!matches && in_repo)
640         {
641           SolvAttr attr(base().get()->key->name);
642           PoolQuery::CompiledAttrMap::const_iterator ai = _pqimpl->_rcattrs.find(attr);
643           if (ai != _pqimpl->_rcattrs.end())
644           {
645             if ((_pqimpl->_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
646             {
647               const regex_t * regex;
648               if (_pqimpl->_rcstrings.empty())
649               {
650                 map<sat::SolvAttr, regex_t>::const_iterator rai = _pqimpl->_rattrs.find(attr);
651                 if (rai != _pqimpl->_rattrs.end())
652                   regex = &rai->second;
653                 else
654                 {
655                   ERR << "no compiled regex found for " <<  attr << endl;
656                   continue;
657                 }
658               }
659               else
660                 regex = &_pqimpl->_regex;
661               matches = ::dataiterator_match(base().get(), _pqimpl->_cflags, regex);
662             }
663             else
664             {
665               const string & sstr =
666                 _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
667               matches = ::dataiterator_match(base().get(), _pqimpl->_cflags, sstr.c_str());
668             }
669
670 //            if (matches)
671               /* After calling dataiterator_match (with any string matcher set)
672                  the kv.str member will be filled with something sensible.  */
673   /*            INT << "value: " << base().get()->kv.str << endl
674                   << " mstr: " <<  sstr << endl;*/ 
675           }
676         }
677       }
678
679       if (drop_by_repo)
680       {
681         base_reference().nextSkipRepo();
682         drop_by_repo = false;
683       }
684       else if (drop_by_kind_status)
685       {
686         base_reference().nextSkipSolvable();
687         drop_by_kind_status = false;
688       }
689
690       // copy the iterator to forward check for the next attribute ***
691       _tmpit = base_reference();
692       _has_next = (*(++_tmpit) != sat::detail::noId);
693
694       if (_has_next)
695       {
696         // *** now increment. Had it not be done this way,
697         // the LookupAttr::iterator could have reached the end() while
698         // trying to reach a matching attribute or the next solvable
699         // thus resulting to a problem in the equal() method
700         ++base_reference();
701         new_solvable = base().get()->solvid != _sid;
702         if (!in_repo)
703           _sid = base().get()->solvid;
704       }
705       // no more attributes in this repo, return
706       else
707         return matches && in_repo; // did the last solvable match conditions?
708     }
709     while (!new_solvable || !in_repo);
710
711     return matches;
712   }
713
714   ///////////////////////////////////////////////////////////////////
715   } //namespace detail
716   ///////////////////////////////////////////////////////////////////
717
718   ///////////////////////////////////////////////////////////////////
719   //
720   //    CLASS NAME : PoolQuery
721   //
722   ///////////////////////////////////////////////////////////////////
723
724   PoolQuery::PoolQuery()
725     : _pimpl(new Impl())
726   {}
727
728
729   PoolQuery::~PoolQuery()
730   {}
731
732
733   void PoolQuery::addRepo(const std::string &repoalias)
734   {
735     _pimpl->_repos.insert(repoalias);
736     _pimpl->_flags &= ~SEARCH_ALL_REPOS;
737   }
738
739
740   void PoolQuery::addKind(const Resolvable::Kind &kind)
741   { _pimpl->_kinds.insert(kind); }
742
743
744   void PoolQuery::addString(const string & value)
745   { _pimpl->_strings.insert(value); }
746
747
748   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
749   { _pimpl->_attrs[attr].insert(value); }
750
751
752   void PoolQuery::setCaseSensitive(const bool value)
753   {
754     if (value)
755       _pimpl->_flags &= ~SEARCH_NOCASE;
756     else
757       _pimpl->_flags |= SEARCH_NOCASE;
758   }
759
760
761   void PoolQuery::setMatchSubstring()
762   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
763   void PoolQuery::setMatchExact()
764   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
765   void PoolQuery::setMatchRegex()
766   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
767   void PoolQuery::setMatchGlob()
768   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
769   void PoolQuery::setMatchWord()
770   {
771     _pimpl->_match_word = true;
772     _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
773   }
774
775   void PoolQuery::setFlags(int flags)
776   { _pimpl->_flags = flags; }
777
778
779   void PoolQuery::setInstalledOnly()
780   { _pimpl->_status_flags = INSTALLED_ONLY; }
781   void PoolQuery::setUninstalledOnly()
782   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
783   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
784   { _pimpl->_status_flags = flags; }
785
786
787   void PoolQuery::setRequireAll(const bool require_all)
788   { _pimpl->_require_all = require_all; }
789
790
791   const PoolQuery::StrContainer &
792   PoolQuery::strings() const
793   { return _pimpl->_strings; }
794
795   const PoolQuery::AttrMap &
796   PoolQuery::attributes() const
797   { return _pimpl->_attrs; }
798
799   const PoolQuery::Kinds &
800   PoolQuery::kinds() const
801   { return _pimpl->_kinds; }
802
803   const PoolQuery::StrContainer &
804   PoolQuery::repos() const
805   { return _pimpl->_repos; }
806
807   bool PoolQuery::caseSensitive() const
808   { return _pimpl->_flags & SEARCH_NOCASE; }
809
810   bool PoolQuery::matchExact() const
811   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
812   bool PoolQuery::matchSubstring() const
813   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
814   bool PoolQuery::matchGlob() const
815   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
816   bool PoolQuery::matchRegex() const
817   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
818   int PoolQuery::matchType() const
819   { return _pimpl->_flags & SEARCH_STRINGMASK; }
820
821   bool PoolQuery::matchWord() const
822   { return _pimpl->_match_word; }
823
824   bool PoolQuery::requireAll() const
825   { return _pimpl->_require_all; }
826
827   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
828   { return _pimpl->_status_flags; }
829
830   PoolQuery::const_iterator PoolQuery::begin() const
831   { return _pimpl->begin(); }
832
833
834   PoolQuery::const_iterator PoolQuery::end() const
835   { return _pimpl->end(); }
836
837
838   bool PoolQuery::empty()
839   { return _pimpl->begin() == _pimpl->end(); }
840
841   //! \todo collect the result, reuse if not dirty
842   PoolQuery::size_type PoolQuery::size()
843   {
844     size_type count = 0;
845     for(const_iterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
846     return count;
847   }
848
849
850   void PoolQuery::execute(ProcessResolvable fnc)
851   { invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc); }
852
853
854   ///////////////////////////////////////////////////////////////////
855   //
856   //  CLASS NAME : PoolQuery::Attr
857   //
858   /**
859    * represents all atributes in PoolQuery except SolvAtributes, which are
860    * used as is (not needed extend anything if someone adds new solv attr)
861    */
862   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
863   {
864     private:
865       friend class IdStringType<PoolQueryAttr>;
866       IdString _str;
867     public:
868     
869     //noAttr
870     PoolQueryAttr(){}
871
872     explicit PoolQueryAttr( const char* cstr_r )
873         : _str( cstr_r )
874       {}
875
876     explicit PoolQueryAttr( const std::string & str_r )
877         : _str( str_r )
878       {}
879
880     //unknown atributes
881     static const PoolQueryAttr noAttr;
882
883     // own attributes
884     static const PoolQueryAttr repoAttr;
885     static const PoolQueryAttr kindAttr;
886     static const PoolQueryAttr stringAttr;
887     static const PoolQueryAttr stringTypeAttr;
888     static const PoolQueryAttr requireAllAttr;
889     static const PoolQueryAttr caseSensitiveAttr;
890     static const PoolQueryAttr installStatusAttr;
891   };
892
893   const PoolQueryAttr PoolQueryAttr::noAttr;
894
895   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
896   const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
897   const PoolQueryAttr PoolQueryAttr::stringAttr( "global_string" );
898   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("string_type");
899   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
900   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
901   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
902
903   class StringTypeAttr : public IdStringType<PoolQueryAttr>
904   {
905     friend class IdStringType<StringTypeAttr>;
906     IdString _str;
907
908   public:
909     StringTypeAttr(){}
910     explicit StringTypeAttr( const char* cstr_r )
911             : _str( cstr_r ){}
912     explicit StringTypeAttr( const std::string & str_r )
913              : _str( str_r ){}
914
915     static const StringTypeAttr noAttr;
916
917     static const StringTypeAttr exactAttr;
918     static const StringTypeAttr substringAttr;
919     static const StringTypeAttr regexAttr;
920     static const StringTypeAttr globAttr;
921     static const StringTypeAttr wordAttr;
922   };
923     const StringTypeAttr StringTypeAttr::noAttr;
924
925     const StringTypeAttr StringTypeAttr::exactAttr("exact");
926     const StringTypeAttr StringTypeAttr::substringAttr("substring");
927     const StringTypeAttr StringTypeAttr::regexAttr("regex");
928     const StringTypeAttr StringTypeAttr::globAttr("glob");
929     const StringTypeAttr StringTypeAttr::wordAttr("word");
930
931   ///////////////////////////////////////////////////////////////////
932
933
934   //\TODO maybe ctor with stream can be usefull
935   bool PoolQuery::recover( istream &str, char delim )
936   {
937     bool finded_something = false; //indicates some atributes is finded
938     string s;
939     do {
940       if ( str.eof() )
941         break;
942
943       getline( str, s, delim );
944
945       if ((!s.empty()) && s[0]=='#') //comment
946       {
947         continue;
948       }
949
950       string::size_type pos = s.find(':');
951       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
952       {
953         if (finded_something) //is first blank line after record?
954         {
955           break;
956         }
957         else
958         {
959           continue;
960         }
961       }
962
963       finded_something = true;
964
965       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
966       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
967
968       PoolQueryAttr attribute( attrName );
969
970       if ( attribute==PoolQueryAttr::repoAttr )
971       {
972         addRepo( attrValue );
973       }
974       else if ( attribute==PoolQueryAttr::kindAttr )
975       {
976         addKind( Resolvable::Kind(attrValue) );
977       }
978       else if ( attribute==PoolQueryAttr::stringAttr )
979       {
980         addString( attrValue );
981       }
982       else if ( attribute==PoolQueryAttr::stringTypeAttr )
983       {
984         StringTypeAttr s(attrValue);
985         if( s == StringTypeAttr::regexAttr )
986         {
987           setMatchRegex();
988         }
989         else if ( s == StringTypeAttr::globAttr )
990         {
991           setMatchGlob();
992         }
993         else if ( s == StringTypeAttr::exactAttr )
994         {
995           setMatchExact();
996         }
997         else if ( s == StringTypeAttr::substringAttr )
998         {
999           setMatchSubstring();
1000         }
1001         else if ( s == StringTypeAttr::wordAttr )
1002         {
1003           setMatchWord();
1004         }
1005         else if ( s == StringTypeAttr::noAttr )
1006         {
1007           WAR << "unknown string type " << attrValue << endl;
1008         }
1009         else
1010         {
1011           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1012         }
1013       }
1014       else if ( attribute==PoolQueryAttr::requireAllAttr )
1015       {
1016         if ( str::strToTrue(attrValue) )
1017         {
1018           setRequireAll(true);
1019         }
1020         else if ( !str::strToFalse(attrValue) )
1021         {
1022           setRequireAll(false);
1023         }
1024         else
1025         {
1026           WAR << "unknown boolean value " << attrValue << endl;
1027         }
1028       }
1029       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1030       {
1031         if ( str::strToTrue(attrValue) )
1032         {
1033           setCaseSensitive(true);
1034         }
1035         else if ( !str::strToFalse(attrValue) )
1036         {
1037           setCaseSensitive(false);
1038         }
1039         else
1040         {
1041           WAR << "unknown boolean value " << attrValue << endl;
1042         }
1043       }
1044       else if ( attribute==PoolQueryAttr::installStatusAttr )
1045       {
1046         if( attrValue == "all" )
1047         {
1048           setStatusFilterFlags( ALL );
1049         }
1050         else if( attrValue == "installed" )
1051         {
1052           setInstalledOnly();
1053         }
1054         else if( attrValue == "not-installed" )
1055         {
1056           setUninstalledOnly();
1057         }
1058         else
1059         {
1060           WAR << "Unknown value for install status " << attrValue << endl;
1061         }
1062       }
1063       else if ( attribute==PoolQueryAttr::noAttr )
1064       {
1065         WAR << "empty attribute name" << endl;
1066       }
1067       else
1068       {
1069         string s = attrName;
1070         boost::replace_all( s,"_",":" );
1071         SolvAttr a(s);
1072         addAttribute(a,attrValue);
1073       }
1074       
1075     } while ( true );
1076
1077     return finded_something;
1078   }
1079
1080   void PoolQuery::serialize( ostream &str, char delim ) const
1081   {
1082     //separating delim
1083     str << delim; 
1084     //iterate thrue all settings and write it
1085     static const zypp::PoolQuery q; //not save default options, so create default query example
1086     
1087     for_( it, repos().begin(), repos().end() )
1088     {
1089       str << "repo: " << *it << delim ;
1090     }
1091
1092     for_( it, kinds().begin(), kinds().end() )
1093     {
1094       str << "kind: " << it->idStr() << delim ;
1095     }
1096
1097     if (matchType()!=q.matchType())
1098     {
1099       switch( matchType() )
1100       {
1101       case SEARCH_STRING:
1102         str << "string_type: exact" << delim;
1103         break;
1104       case SEARCH_SUBSTRING:
1105         str << "string_type: substring" << delim;
1106         break;
1107       case SEARCH_GLOB:
1108         str << "string_type: glob" << delim;
1109         break;
1110       case SEARCH_REGEX:
1111         str << "string_type: regex" << delim;
1112         break;
1113       default:
1114         WAR << "unknown match type "  << matchType() << endl;
1115       }
1116     }
1117
1118     if( caseSensitive() != q.caseSensitive() )
1119     {
1120       str << "case_sensitive: ";
1121       if (caseSensitive())
1122       {
1123         str << "on" << delim;
1124       }
1125       else 
1126       {
1127         str << "off" << delim;
1128       }
1129     }
1130
1131     if( requireAll() != q.requireAll() )
1132     {
1133       str << "require_all: ";
1134       if (requireAll())
1135       {
1136         str << "on" << delim;
1137       }
1138       else 
1139       {
1140         str << "off" << delim;
1141       }
1142     }
1143
1144     if( statusFilterFlags() != q.statusFilterFlags() )
1145     {
1146       switch( statusFilterFlags() )
1147       {
1148       case ALL:
1149         str << "install_status: all" << delim;
1150         break;
1151       case INSTALLED_ONLY:
1152         str << "install_status: installed" << delim;
1153         break;
1154       case UNINSTALLED_ONLY:
1155         str << "install_status: not-installed" << delim;
1156         break;
1157       }
1158     }
1159
1160     for_( it, strings().begin(), strings().end() )
1161     {
1162       str << "global_string: " << *it << delim;
1163     }
1164
1165     for_( it, attributes().begin(), attributes().end() )
1166     {
1167       string s = it->first.asString();
1168       boost::replace_all(s,":","_"); 
1169       for_( it2,it->second.begin(),it->second.end() )
1170       {
1171         str << s <<": "<< *it2 << delim;
1172       }
1173     }
1174
1175     //separating delim - protection
1176     str << delim; 
1177
1178   }
1179
1180
1181   string PoolQuery::asString() const
1182   { return _pimpl->asString(); }
1183
1184
1185   ostream & operator<<( ostream & str, const PoolQuery & obj )
1186   { return str << obj.asString(); }
1187
1188   //internal matching two containers O(n^2)
1189   template <class Container>
1190   bool equalContainers(const Container& a, const Container& b)
1191   {
1192     if (a.size()!=b.size())
1193       return false;
1194
1195     for_(it,a.begin(),a.end())
1196     {
1197       bool finded = false;
1198       for_( it2, b.begin(),b.end() )
1199       {
1200         if (*it==*it2)
1201         {
1202           finded = true;
1203           break;
1204         }
1205       }
1206
1207       if (!finded)
1208         return false;
1209     }
1210     return true;
1211   }
1212
1213   bool PoolQuery::operator==(const PoolQuery& a) const
1214   {
1215     if (!_pimpl->_compiled)
1216       _pimpl->compile();
1217     if (!a._pimpl->_compiled)
1218       a._pimpl->compile();
1219     if( matchType()!=a.matchType() )
1220       return false;
1221     if( a.matchWord()!=matchWord())
1222       return false;
1223     if( a.requireAll()!=requireAll() )
1224       return false;
1225     if(!equalContainers(a.kinds(), kinds()))
1226       return false;
1227     if(!equalContainers(a.repos(), repos()))
1228       return false;
1229     if(a._pimpl->_rcstrings!=_pimpl->_rcstrings)
1230       return false;
1231     if(!equalContainers(a._pimpl->_rcattrs, _pimpl->_rcattrs))
1232       return false;
1233     if(a._pimpl->_cflags!= _pimpl->_cflags)
1234       return false;
1235
1236     return true;
1237   }
1238
1239
1240   /////////////////////////////////////////////////////////////////
1241 } // namespace zypp
1242 ///////////////////////////////////////////////////////////////////
1243