- don't hold const CompiledAttrMap & in PoolQueryIterator
[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/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     /** Regex-compiled search strings. */
71     mutable string _rcstrings;
72     mutable regex_t _regex;
73     /** Raw attributes */
74     AttrMap _attrs;
75     /** Regex-compiled attributes */
76     mutable CompiledAttrMap _rcattrs;
77     mutable map<sat::SolvAttr, regex_t> _rattrs;
78
79     /** Repos to search. */
80     StrContainer _repos;
81     /** Kinds to search */
82     Kinds _kinds;
83
84     /** Sat solver search flags */
85     int _flags;
86     /** Backup of search flags. compile() may change the flags if needed, so
87      * in order to reuse the query, the original flags need to be stored
88      * at the start of compile() */
89     mutable int _cflags;
90     /** Sat solver status flags */
91     int _status_flags;
92
93     bool _match_word;
94
95     bool _require_all;
96
97     /** Sat solver Dataiterator structure */
98     mutable ::_Dataiterator _rdit;
99
100     mutable bool _compiled;
101
102     /** Function for processing found solvables. Used in execute(). */
103     mutable PoolQuery::ProcessResolvable _fnc;
104     
105   private:
106     friend Impl * rwcowClone<Impl>( const Impl * rhs );
107     /** clone for RWCOW_pointer */
108     Impl * clone() const
109     { return new Impl( *this ); }
110   };
111
112   static void
113   compileRegex(regex_t * regex, const string & str, bool nocase)
114   {
115     /* We feed multiple lines eventually (e.g. authors or descriptions),
116        so set REG_NEWLINE. */
117     if (regcomp(regex, str.c_str(),
118         REG_EXTENDED | REG_NOSUB | REG_NEWLINE | (nocase ? REG_ICASE : 0)) != 0)
119       ZYPP_THROW(Exception(
120         str::form(_("Invalid regular expression '%s'"), str.c_str())));
121   }
122
123   struct MyInserter
124   {
125     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
126     
127     bool operator()(const string & str)
128     {
129       _cont.insert(str);
130       return true;
131     }
132     
133     PoolQuery::StrContainer & _cont;
134   };
135
136   
137   struct EmptyFilter
138   {
139     bool operator()(const string & str)
140     {
141       return !str.empty();
142     }
143   };
144
145
146   void PoolQuery::Impl::compile() const
147   {
148     _cflags = _flags;
149
150     // 'different'         - will have to iterate through all and match by ourselves (slow)
151     // 'same'              - will pass the compiled string to dataiterator_init
152     // 'one-attr'          - will pass it to dataiterator_init
153     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
154     
155     // // NO ATTRIBUTE
156     // else
157     //   for all _strings
158     //     create regex; store in _rcstrings; if more strings flag regex;
159     if (_attrs.empty())
160     {
161       _rcstrings = createRegex(_strings);
162       if (_strings.size() > 1)
163         _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
164     }
165
166     // // ONE ATTRIBUTE 
167     // else if _attrs is not empty but it contains just one attr
168     //   for all _strings and _attr[key] strings
169     //     create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
170     else if (_attrs.size() == 1)
171     {
172       StrContainer joined;
173       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
174       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
175       _rcstrings = createRegex(joined);
176       _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
177     }
178
179     // // MULTIPLE ATTRIBUTES
180     else
181     {
182       // check whether there are any per-attribute strings 
183       bool attrvals_empty = true;
184       for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
185         if (!ai->second.empty())
186           for(StrContainer::const_iterator it = ai->second.begin();
187               it != ai->second.end(); it++)
188             if (!it->empty())
189             {
190               attrvals_empty = false;
191               goto attremptycheckend;
192             }
193 attremptycheckend:
194
195       // chceck whether the per-attribute strings are all the same
196       bool attrvals_thesame = true;
197       AttrMap::const_iterator ai = _attrs.begin();
198       const StrContainer & set1 = ai->second;
199       ++ai;
200       for (; ai != _attrs.end(); ++ai)
201       {
202         StrContainer result;
203         set_difference(
204           set1.begin(), set1.end(),
205           ai->second.begin(), ai->second.end(),
206           inserter(result, result.begin())/*, ltstr()*/);
207         if (!result.empty())
208         {
209           attrvals_thesame = false;
210           break;
211         }
212       }
213
214       // // THE SAME STRINGS FOR DIFFERENT ATTRS
215       // else if _attrs is not empty but it does not contain strings
216       //   for each key in _attrs take all _strings
217       //     create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
218       if (attrvals_empty || attrvals_thesame)
219       {
220         StrContainer joined;
221         if (attrvals_empty)
222         {
223           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
224           _rcstrings = createRegex(joined);
225         }
226         else
227         {
228           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
229           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
230           _rcstrings = createRegex(joined);
231         }
232         // copy the _attrs keys to _rcattrs
233         for_(ai, _attrs.begin(), _attrs.end())
234           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
235
236         if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
237           compileRegex(&_regex, _rcstrings, _cflags & SEARCH_NOCASE);
238       }
239
240       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
241       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
242       //   for each key in _attrs take all _strings + all _attrs[key] strings
243       //     create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
244       else
245       {
246         for_(ai, _attrs.begin(), _attrs.end())
247         {
248           StrContainer joined;
249           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
250           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
251           string s = createRegex(joined);
252           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, s));
253
254           if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
255           {
256             regex_t regex;
257             compileRegex(&regex, s, _cflags & SEARCH_NOCASE);
258             _rattrs.insert(pair<sat::SolvAttr, regex_t>(ai->first, regex));
259           }
260         }
261       }
262     }
263
264     // tell the Dataiterator to search only in one repo if only one specified
265     if (_repos.size() == 1)
266       _cflags &= ~SEARCH_ALL_REPOS;
267
268     _compiled = true;
269     
270     DBG << asString() << endl;
271   }
272
273   /**
274    * Converts '*' and '?' wildcards within str into their regex equivalents.
275    */
276   static string wildcards2regex(const string & str)
277   {
278     string regexed = str;
279
280     str::regex all("\\*"); // regex to search for '*'
281     str::regex one("\\?"); // regex to search for '?'
282     string r_all(".*"); // regex equivalent of '*'
283     string r_one(".");  // regex equivalent of '?'
284     string::size_type pos;
285
286     // replace all "*" in input with ".*"
287     for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
288       regexed = regexed.replace(pos, 1, r_all);
289
290     // replace all "?" in input with "."
291     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
292       regexed = regexed.replace(pos, 1, r_one);
293
294     DBG << " -> " << regexed << endl;
295
296     return regexed;
297   }
298
299 //! macro for word boundary tags for regexes
300 #define WB (_match_word ? string("\\b") : string())
301
302   string PoolQuery::Impl::createRegex(const StrContainer & container) const
303   {
304     string rstr;
305
306     if (container.empty())
307       return rstr;
308
309     if (container.size() == 1)
310     {
311       if (_match_word)
312         return ".*" + WB + *container.begin() + WB + ".*";
313
314       return *container.begin();
315     }
316
317     // multiple strings
318
319     bool use_wildcards = (_cflags & SEARCH_STRINGMASK) == SEARCH_GLOB;
320     StrContainer::const_iterator it = container.begin();
321     string tmp;
322
323     if (use_wildcards)
324       tmp = wildcards2regex(*it);
325
326     if (_require_all)
327     {
328       if (!(_cflags & SEARCH_STRING)) // not match exact
329         tmp += ".*" + WB + tmp;
330       rstr = "(?=" + tmp + ")";
331     }
332     else
333     {
334       if (_cflags & SEARCH_STRING) // match exact
335         rstr = "^";
336       else
337         rstr = ".*" + WB;
338
339       rstr += "(" + tmp;
340     }
341
342     ++it;
343
344     for (; it != container.end(); ++it)
345     {
346       if (use_wildcards)
347         tmp = wildcards2regex(*it);
348
349       if (_require_all)
350       {
351         if (!(_cflags & SEARCH_STRING)) // not match exact
352           tmp += ".*" + WB + tmp;
353         rstr += "(?=" + tmp + ")";
354       }
355       else
356       {
357         rstr += "|" + tmp;
358       }
359     }
360
361     if (_require_all)
362     {
363       if (!(_cflags & SEARCH_STRING)) // not match exact
364         rstr += WB + ".*";
365     }
366     else
367     {
368       rstr += ")";
369       if (_cflags & SEARCH_STRING) // match exact
370         rstr += "$";
371       else
372         rstr += WB + ".*";
373     }
374
375     return rstr;
376   }
377
378
379   PoolQuery::const_iterator PoolQuery::Impl::begin() const
380   {
381     compile();
382
383     // if only one repository has been specified, find it in the pool
384     sat::Pool pool(sat::Pool::instance());
385     sat::Pool::RepositoryIterator itr = pool.reposBegin();
386     if (!(_cflags & SEARCH_ALL_REPOS) && _repos.size() == 1)
387     {
388       string theone = *_repos.begin();
389       for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
390       if (itr == pool.reposEnd())
391       {
392         RepoInfo info; info.setAlias(theone);
393         ERR << "Repository not found in sat pool." <<  endl;
394         ZYPP_THROW(repo::RepoNotFoundException(info));
395       }
396     }
397
398     DBG << "_cflags:" << _cflags << endl;
399
400     if (_rcattrs.empty())
401     {
402     ::dataiterator_init(&_rdit,
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,
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,
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(
430         str::form(_("Invalid regular expression '%s'"), _rcstrings.c_str())));
431
432     PoolQuery::const_iterator it(this);
433     it.increment();
434     return it;
435   }
436
437   PoolQuery::const_iterator PoolQuery::Impl::end() const
438   {
439     INT << "end" << endl;
440     return PoolQuery::const_iterator();
441   }
442
443
444   string PoolQuery::Impl::asString() const
445   {
446     ostringstream o;
447
448     o << "compiled: " << _compiled << endl;
449     
450     o << "kinds: ";
451     for(Kinds::const_iterator it = _kinds.begin();
452         it != _kinds.end(); ++it)
453       o << *it << " ";
454     o << endl;
455
456     o << "repos: ";
457     for(StrContainer::const_iterator it = _repos.begin();
458         it != _repos.end(); ++it)
459       o << *it << " ";
460     o << endl;
461
462     o << "string match flags:" << endl;
463     o << "* string/substring/glob/regex: " << (_cflags & SEARCH_STRINGMASK) << endl; 
464     o << "* SEARCH_NOCASE: " << ((_cflags & SEARCH_NOCASE) ? "yes" : "no") << endl;
465     o << "* SEARCH_ALL_REPOS: " << ((_cflags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
466     o << "status filter flags:" << _status_flags << endl;
467
468     // raw
469
470     o << "strings: ";
471     for(StrContainer::const_iterator it = _strings.begin();
472         it != _strings.end(); ++it)
473       o << *it << " ";
474     o << endl;
475
476     o << "attributes: " << endl;
477     for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
478     {
479       o << "* " << ai->first << ": ";
480       for(StrContainer::const_iterator vi = ai->second.begin();
481           vi != ai->second.end(); ++vi)
482         o << *vi << " ";
483       o << endl;
484     }
485
486     // compiled
487
488     o << "compiled strings: " << _rcstrings << endl;
489     o << "compiled attributes:" << endl;
490     for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
491       o << "* " << ai->first << ": " << ai->second << endl;
492
493     return o.str();
494   }
495
496   /** \relates PoolQuery::Impl Stream output *//*
497   inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
498   {
499     return str << "PoolQuery::Impl";
500   }
501   */
502   ///////////////////////////////////////////////////////////////////
503
504   ///////////////////////////////////////////////////////////////////
505   namespace detail
506   { /////////////////////////////////////////////////////////////////
507
508   ///////////////////////////////////////////////////////////////////
509   //
510   //  CLASS NAME : PoolQuery::ResultIterator
511   //
512   ///////////////////////////////////////////////////////////////////
513
514   PoolQueryIterator::PoolQueryIterator(const PoolQuery::Impl * pqimpl)
515   : PoolQueryIterator::iterator_adaptor_(pqimpl ? &pqimpl->_rdit : 0)
516   , _rdit(pqimpl ? &pqimpl->_rdit : 0)
517   , _pqimpl(pqimpl)
518   , _sid(0)
519   , _has_next(true)
520   , _do_matching(false)
521   , _pool((sat::Pool::instance()))
522   {
523     if (_pqimpl->_rcattrs.size() > 1)
524       _do_matching = true;
525   }
526
527   void PoolQueryIterator::increment()
528   {
529     if (!_rdit)
530       return;
531
532     bool got_match = false;
533     if (_has_next)
534     {
535       DBG << "last: " << _sid << endl;
536       while (_has_next && !(got_match = matchSolvable()));
537     }
538
539     // no more solvables and the last did not match
540     if (!got_match && !_has_next)
541     {
542       base_reference() = 0;
543       _sid = 0;
544     }
545
546     DBG << "next: " << _sid << endl;
547   }
548
549   bool PoolQueryIterator::matchSolvable()
550   {
551     _sid = _rdit->solvid;
552
553     bool new_solvable = true;
554     bool matches = !_do_matching;
555     bool in_repo;
556     bool drop_by_kind_status = false;
557     bool drop_by_repo = false;
558     do
559     {
560       //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
561       in_repo = _sid >= _rdit->repo->start;
562
563       if (in_repo && new_solvable)
564       {
565         while(1)
566         {
567           drop_by_repo = false;
568           if (!_pqimpl->_repos.empty() && 
569             _pqimpl->_repos.find(_rdit->repo->name) == _pqimpl->_repos.end())
570           {
571             drop_by_repo = true;
572             break;
573           }
574
575           drop_by_kind_status = false;
576
577           // whether to drop an uninstalled (repo) solvable
578           if ( (_pqimpl->_status_flags & PoolQuery::INSTALLED_ONLY) &&
579                _rdit->repo->name != _pool.systemRepoName() )
580           {
581             drop_by_kind_status = true;
582             break;
583           }
584
585           // whether to drop an installed (target) solvable
586           if ((_pqimpl->_status_flags & PoolQuery::UNINSTALLED_ONLY) &&
587                _rdit->repo->name == _pool.systemRepoName())
588           {
589             drop_by_kind_status = true;
590             break;
591           }
592
593           // whether to drop unwanted kind
594           if (!_pqimpl->_kinds.empty())
595           {
596             sat::Solvable s(_sid);
597             // the user wants to filter by kind.
598             if (_pqimpl->_kinds.find(s.kind()) == _pqimpl->_kinds.end())
599               drop_by_kind_status = true;
600           }
601
602           break;
603         }
604
605         matches = matches && !drop_by_kind_status && !drop_by_repo;
606       }
607
608       if (_do_matching && !drop_by_kind_status)
609       {
610         if (!matches && in_repo)
611         {
612           SolvAttr attr(_rdit->key->name);
613           PoolQuery::CompiledAttrMap::const_iterator ai = _pqimpl->_rcattrs.find(attr);
614           if (ai != _pqimpl->_rcattrs.end())
615           {
616             if ((_pqimpl->_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
617             {
618               const regex_t * regex;
619               if (_pqimpl->_rcstrings.empty())
620               {
621                 map<sat::SolvAttr, regex_t>::const_iterator rai = _pqimpl->_rattrs.find(attr);
622                 if (rai != _pqimpl->_rattrs.end())
623                   regex = &rai->second;
624                 else
625                 {
626                   ERR << "no compiled regex found for " <<  attr << endl;
627                   continue;
628                 }
629               }
630               else
631                 regex = &_pqimpl->_regex;
632               matches = ::dataiterator_match(_rdit, _pqimpl->_cflags, regex);
633             }
634             else
635             {
636               const string & sstr =
637                 _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
638               matches = ::dataiterator_match(_rdit, _pqimpl->_cflags, sstr.c_str());
639             }
640
641 //            if (matches)
642               /* After calling dataiterator_match (with any string matcher set)
643                  the kv.str member will be filled with something sensible.  */
644   /*            INT << "value: " << _rdit->kv.str << endl
645                   << " mstr: " <<  sstr << endl;*/ 
646           }
647         }
648       }
649
650       if (drop_by_repo)
651       {
652         Repository nextRepo(Repository(_rdit->repo).nextInPool());
653         ::dataiterator_skip_repo(_rdit);
654         if (nextRepo)
655           ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
656         drop_by_repo = false;
657       }
658       else if (drop_by_kind_status)
659       {
660         ::dataiterator_skip_solvable(_rdit);
661         drop_by_kind_status = false;
662       }
663
664       if ((_has_next = ::dataiterator_step(_rdit)))
665       {
666         new_solvable = _rdit->solvid != _sid;
667         if (!in_repo)
668           _sid = _rdit->solvid;
669       }
670       // no more attributes in this repo, return
671       else
672       {
673         // check for more repos to jump to
674         if (!_pqimpl->_repos.empty())
675         {
676           Repository nextRepo(Repository(_rdit->repo).nextInPool());
677           if (nextRepo)
678           {
679             ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
680             _has_next = ::dataiterator_step(_rdit);
681           }
682         }
683
684         // did the last solvable match conditions?
685         return matches && in_repo;
686       }
687     }
688     while (!new_solvable || !in_repo);
689
690     return matches;
691   }
692
693   ///////////////////////////////////////////////////////////////////
694   } //namespace detail
695   ///////////////////////////////////////////////////////////////////
696
697   ///////////////////////////////////////////////////////////////////
698   //
699   //    CLASS NAME : PoolQuery
700   //
701   ///////////////////////////////////////////////////////////////////
702
703   PoolQuery::PoolQuery()
704     : _pimpl(new Impl())
705   {}
706
707
708   PoolQuery::~PoolQuery()
709   {}
710
711
712   void PoolQuery::addRepo(const std::string &repoalias)
713   {
714     _pimpl->_repos.insert(repoalias);
715     _pimpl->_flags &= ~SEARCH_ALL_REPOS;
716   }
717
718
719   void PoolQuery::addKind(const Resolvable::Kind &kind)
720   { _pimpl->_kinds.insert(kind); }
721
722
723   void PoolQuery::addString(const string & value)
724   { _pimpl->_strings.insert(value); }
725
726
727   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
728   { _pimpl->_attrs[attr].insert(value); }
729
730
731   void PoolQuery::setCaseSensitive(const bool value)
732   {
733     if (value)
734       _pimpl->_flags &= ~SEARCH_NOCASE;
735     else
736       _pimpl->_flags |= SEARCH_NOCASE;
737   }
738
739
740   void PoolQuery::setMatchSubstring()
741   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
742   void PoolQuery::setMatchExact()
743   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
744   void PoolQuery::setMatchRegex()
745   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
746   void PoolQuery::setMatchGlob()
747   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
748   void PoolQuery::setMatchWord()
749   {
750     _pimpl->_match_word = true;
751     _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
752   }
753
754   void PoolQuery::setFlags(int flags)
755   { _pimpl->_flags = flags; }
756
757
758   void PoolQuery::setInstalledOnly()
759   { _pimpl->_status_flags = INSTALLED_ONLY; }
760   void PoolQuery::setUninstalledOnly()
761   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
762   void PoolQuery::setStatusFilterFlags( int flags )
763   { _pimpl->_status_flags = flags; }
764
765
766   void PoolQuery::setRequireAll(const bool require_all)
767   { _pimpl->_require_all = require_all; }
768
769
770   const PoolQuery::StrContainer &
771   PoolQuery::strings() const
772   { return _pimpl->_strings; }
773
774   const PoolQuery::AttrMap &
775   PoolQuery::attributes() const
776   { return _pimpl->_attrs; }
777
778   const PoolQuery::Kinds &
779   PoolQuery::kinds() const
780   { return _pimpl->_kinds; }
781
782   const PoolQuery::StrContainer &
783   PoolQuery::repos() const
784   { return _pimpl->_repos; }
785
786   bool PoolQuery::caseSensitive() const
787   { return _pimpl->_flags & SEARCH_NOCASE; }
788
789   bool PoolQuery::matchExact() const
790   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
791   bool PoolQuery::matchSubstring() const
792   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
793   bool PoolQuery::matchGlob() const
794   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
795   bool PoolQuery::matchRegex() const
796   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
797   int PoolQuery::matchType() const
798   { return _pimpl->_flags & SEARCH_STRINGMASK; }
799
800   bool PoolQuery::matchWord() const
801   { return _pimpl->_match_word; }
802
803   bool PoolQuery::requireAll() const
804   { return _pimpl->_require_all; }
805
806
807
808   PoolQuery::const_iterator PoolQuery::begin() const
809   { return _pimpl->begin(); }
810
811
812   PoolQuery::const_iterator PoolQuery::end() const
813   { return _pimpl->end(); }
814
815
816   bool PoolQuery::empty()
817   { return _pimpl->begin() == _pimpl->end(); }
818
819   //! \todo collect the result, reuse if not dirty
820   PoolQuery::size_type PoolQuery::size()
821   {
822     size_type count = 0;
823     for(const_iterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
824     return count;
825   }
826
827
828   void PoolQuery::execute(ProcessResolvable fnc)
829   { invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc); }
830
831
832   ///////////////////////////////////////////////////////////////////
833   //
834   //  CLASS NAME : PoolQuery::Attr
835   //
836   /**
837    * represents all atributes in PoolQuery except SolvAtributes, which are
838    * used as is (not needed extend anything if someone adds new solv attr)
839    */
840   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
841   {
842     private:
843       friend class IdStringType<PoolQueryAttr>;
844       IdString _str;
845       sat::SolvAttr sa;
846
847     public:
848     
849     //noAttr
850     PoolQueryAttr():isSolvAttr(false){}
851
852     explicit PoolQueryAttr( const char* cstr_r )
853         : _str( cstr_r ),isSolvAttr(false){}
854
855     explicit PoolQueryAttr( const std::string & str_r )
856         : _str( str_r ),isSolvAttr(false)
857     {
858       if( _str==noAttr ){
859         string s(str_r);
860         boost::replace_all(s,"_",":");
861         sa = sat::SolvAttr(s);
862         if( sa != sat::SolvAttr::noAttr )
863         {
864           isSolvAttr = true; 
865         }
866       }
867     }
868
869     const sat::SolvAttr& solvAttr() { return sa;}
870
871     //unknown atributes
872     static const PoolQueryAttr noAttr;
873
874     // own attributes
875     static const PoolQueryAttr repoAttr;
876     static const PoolQueryAttr kindAttr;
877
878     // exported attributes from SolvAtributes
879     bool isSolvAttr;
880   };
881
882   const PoolQueryAttr PoolQueryAttr::noAttr;
883
884   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
885   const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
886
887   ///////////////////////////////////////////////////////////////////
888
889
890   //\TODO maybe ctor with stream can be usefull
891   bool PoolQuery::recover( istream &str, char delim )
892   {
893     bool finded_something = false; //indicates some atributes is finded
894     string s;
895     do {
896       if ( str.eof() )
897         break;
898
899       getline( str, s, delim );
900
901       if ((!s.empty()) && s[0]=='#') //comment
902       {
903         continue;
904       }
905
906       string::size_type pos = s.find(':');
907       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
908       {
909         if (finded_something) //is first blank line after record?
910         {
911           break;
912         }
913         else
914         {
915           continue;
916         }
917       }
918
919       finded_something = true;
920
921       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
922       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
923
924       PoolQueryAttr attribute( attrName );
925
926       if ( attribute==PoolQueryAttr::repoAttr )
927       {
928         addRepo( attrValue );
929       }
930       else if ( attribute==PoolQueryAttr::kindAttr )
931       {
932         addKind( Resolvable::Kind(attrValue) );
933       }
934       else if ( attribute==PoolQueryAttr::noAttr )
935       {
936         if (attribute.isSolvAttr)
937         {
938           addAttribute(attribute.solvAttr(),attrValue);
939         }
940         else
941         {
942           WAR << "unknown attribute " << attrName << endl;
943         }
944       }
945       else
946       {
947         WAR << "forget recover some attribute defined as PoolQuery attribute: " << attrName << endl;
948       }
949       
950     } while ( true );
951
952     return finded_something;
953   }
954
955   void PoolQuery::serialize( ostream &str, char delim ) const
956   {
957     //separating delim
958     str << delim; 
959     //iterate thrue all settings and write it
960     
961     for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
962     {
963       str << "repo: " << *it << delim ;
964     }
965
966     for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
967     {
968       str << "kind: " << it->idStr() << delim ;
969     }
970
971     //separating delim - protection
972     str << delim; 
973
974   }
975
976
977   string PoolQuery::asString() const
978   { return _pimpl->asString(); }
979
980
981   ostream & operator<<( ostream & str, const PoolQuery & obj )
982   { return str << obj.asString(); }
983
984   //internal matching two containers O(n^2)
985   template <class Container>
986   bool equalContainers(const Container& a, const Container& b)
987   {
988     if (a.size()!=b.size())
989       return false;
990
991     for_(it,a.begin(),a.end())
992     {
993       bool finded = false;
994       for_( it2, b.begin(),b.end() )
995       {
996         if (*it==*it2)
997         {
998           finded = true;
999           break;
1000         }
1001       }
1002
1003       if (!finded)
1004         return false;
1005     }
1006     return true;
1007   }
1008
1009   bool PoolQuery::operator==(const PoolQuery& a) const
1010   {
1011     if (!_pimpl->_compiled)
1012       _pimpl->compile();
1013     if (!a._pimpl->_compiled)
1014       a._pimpl->compile();
1015     if( matchType()!=a.matchType() )
1016       return false;
1017     if( a.matchWord()!=matchWord())
1018       return false;
1019     if( a.requireAll()!=requireAll() )
1020       return false;
1021     if(!equalContainers(a.kinds(), kinds()))
1022       return false;
1023     if(!equalContainers(a.repos(), repos()))
1024       return false;
1025     if(a._pimpl->_rcstrings!=_pimpl->_rcstrings)
1026       return false;
1027     if(!equalContainers(a._pimpl->_rcattrs, _pimpl->_rcattrs))
1028       return false;
1029     if(a._pimpl->_cflags!= _pimpl->_cflags)
1030       return false;
1031
1032     return true;
1033   }
1034
1035
1036   /////////////////////////////////////////////////////////////////
1037 } // namespace zypp
1038 ///////////////////////////////////////////////////////////////////
1039