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