Add PoolQuery for name, edition AND architecture in one go. (bnc#614362)
[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
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/LogTools.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
19 #include "zypp/repo/RepoException.h"
20 #include "zypp/RelCompare.h"
21
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/sat/AttrMatcher.h"
25
26 #include "zypp/PoolQuery.h"
27
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
30
31 using namespace std;
32 using namespace zypp::sat;
33
34 ///////////////////////////////////////////////////////////////////
35 namespace zypp
36 { /////////////////////////////////////////////////////////////////
37
38   ///////////////////////////////////////////////////////////////////
39   namespace
40   { /////////////////////////////////////////////////////////////////
41
42     /////////////////////////////////////////////////////////////////
43     // some Helpers and Predicates
44     /////////////////////////////////////////////////////////////////
45
46     bool isDependencyAttribute( sat::SolvAttr attr_r )
47     {
48       static sat::SolvAttr deps[] = {
49         SolvAttr::provides,
50         SolvAttr::requires,
51         SolvAttr::recommends,
52         SolvAttr::obsoletes,
53         SolvAttr::conflicts,
54         SolvAttr::suggests,
55         SolvAttr::supplements,
56         SolvAttr::enhances,
57       };
58       for_( it, arrayBegin(deps), arrayEnd(deps) )
59         if ( *it == attr_r )
60           return true;
61       return false;
62     }
63
64     /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
65      * Query asserts \a iter_r points to a capability and we
66      * have to check the range only.
67      */
68     struct EditionRangePredicate
69     {
70       EditionRangePredicate( const Rel & op, const Edition & edition )
71         : _range( op, edition )
72         , _arch( Arch_empty )
73       {}
74       EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75         : _range( op, edition )
76         , _arch( arch )
77       {}
78
79       bool operator()( sat::LookupAttr::iterator iter_r )
80       {
81         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
82           return false;
83
84         CapDetail cap( iter_r.id() );
85         if ( ! cap.isSimple() )
86           return false;
87         if ( cap.isNamed() ) // no range to match
88           return true;
89         return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
90       }
91
92       std::string serialize() const
93       {
94         std::string ret( "EditionRange" );
95         str::appendEscaped( ret, _range.op.asString() );
96         str::appendEscaped( ret, _range.value.asString() );
97         str::appendEscaped( ret, _arch.asString() );
98         return ret;
99       }
100
101       Edition::MatchRange _range;
102       Arch                _arch;
103    };
104
105     /** Whether the current Solvables edition is within a given range and/or its arch matches. */
106     struct SolvableRangePredicate
107     {
108       SolvableRangePredicate( const Rel & op, const Edition & edition )
109         : _range( op, edition )
110         , _arch( Arch_empty )
111       {}
112
113       SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114         : _range( op, edition )
115         , _arch( arch )
116       {}
117
118       bool operator()( sat::LookupAttr::iterator iter_r )
119       {
120         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
121           return false;
122         return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
123       }
124
125       std::string serialize() const
126       {
127         std::string ret( "SolvableRange" );
128         str::appendEscaped( ret, _range.op.asString() );
129         str::appendEscaped( ret, _range.value.asString() );
130         str::appendEscaped( ret, _arch.asString() );
131         return ret;
132       }
133
134       Edition::MatchRange _range;
135       Arch                _arch;
136     };
137
138     /** Whether the current capability matches a given one.
139      * Query asserts \a iter_r points to a capability and we
140      * have to check the match only.
141      */
142     struct CapabilityMatchPredicate
143     {
144       CapabilityMatchPredicate( Capability cap_r )
145         : _cap( cap_r )
146       {}
147
148       bool operator()( sat::LookupAttr::iterator iter_r ) const
149       {
150         return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
151       }
152
153       std::string serialize() const
154       {
155         std::string ret( "CapabilityMatch" );
156         str::appendEscaped( ret, _cap.asString() );
157         return ret;
158       }
159
160       Capability _cap;
161     };
162
163     /////////////////////////////////////////////////////////////////
164     //
165     /////////////////////////////////////////////////////////////////
166     /** Match data per attribtue.
167      *
168      * This includes the attribute itself, an optional \ref sat::AttrMatcher
169      * to restrict the query to certain string values, and an optional
170      * boolean \ref Predicate that may apply further restrictions that can
171      * not be expressed by the \ref attrMatcher.
172      *
173      * Example for such a \ref predicate would be an additional edition range
174      * check whan looking for dependencies. The \ref attrMatcher would
175      * find potential matches by looking at the dependencies name, the
176      * predicate will then check the edition ranges.
177      *
178      * As the \ref predicate takes an iterator pointing to the current
179      * match, it's also suitable for sub-structure (flexarray) inspection
180      * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
181      *
182      * \note: \see \ref addPredicate for further constraints.
183      */
184     struct AttrMatchData
185     {
186       typedef function<bool(sat::LookupAttr::iterator)> Predicate;
187
188       static bool always( sat::LookupAttr::iterator ) { return true; }
189       static bool never( sat::LookupAttr::iterator ) { return false; }
190
191       AttrMatchData()
192       {}
193
194       AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r )
195         : attr( attr_r )
196         , attrMatcher( attrMatcher_r )
197       {}
198
199       AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r,
200                      const Predicate & predicate_r, const std::string & predicateStr_r )
201         : attr( attr_r )
202         , attrMatcher( attrMatcher_r )
203         , predicate( predicate_r )
204         , predicateStr( predicateStr_r )
205       {}
206
207       /** A usable Predicate must provide a string serialization.
208        * As there is no \c operator== for \ref Predicate, we compare it's
209        * string representation instead. If you add new predicated, check the
210        * deserialization code in \ref deserialize.
211        */
212       template<class _Predicate>
213       void addPredicate( const _Predicate & predicate_r )
214       {
215         predicate    = predicate_r;
216         predicateStr = predicate_r.serialize();
217       }
218
219       /** Dumb serialization.
220        * \code
221        *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
222        * \endcode
223       */
224       std::string serialize() const
225       {
226         std::string ret( "AttrMatchData" );
227         str::appendEscaped( ret, attr.asString() );
228         str::appendEscaped( ret, attrMatcher.searchstring() );
229         // TODO: Actually the flag should be serialized too, but for PoolQuery
230         // it's by now sufficient to differ between mode OTHER and others,
231         // i.e. whether to compile or not compile.
232         str::appendEscaped( ret, attrMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
233         str::appendEscaped( ret, predicateStr );
234         return ret;
235       }
236
237        /** Dumb restore from serialized string.
238         * \throw Exception on parse error.
239         */
240       static AttrMatchData deserialize( const std::string & str_r )
241       {
242         std::vector<std::string> words;
243         str::splitEscaped( str_r, std::back_inserter(words) );
244         if ( words.empty() || words[0] != "AttrMatchData" )
245           ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
246         if ( words.size() != 5 )
247           ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
248
249         AttrMatchData ret;
250         ret.attr = sat::SolvAttr( words[1] );
251         ret.attrMatcher = sat::AttrMatcher( words[2] );
252         if ( words[3] == "C" )
253           ret.attrMatcher.setFlags( Match::OTHER );
254         ret.predicateStr = words[4];
255
256         // now the predicate
257         words.clear();
258         str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
259         if ( ! words.empty() )
260         {
261           if ( words[0] == "EditionRange" )
262           {
263             switch( words.size() )
264             {
265               case 3:
266                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
267                 break;
268               case 4:
269                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
270                 break;
271               default:
272                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
273                 break;
274             }
275           }
276           else if ( words[0] == "SolvableRange" )
277           {
278             switch( words.size() )
279             {
280               case 3:
281                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
282                 break;
283               case 4:
284                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
285                 break;
286               default:
287                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
288                 break;
289             }
290           }
291           else if ( words[0] == "CapabilityMatch" )
292           {
293             if ( words.size() != 2 )
294               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
295             ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
296           }
297           else
298             ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
299         }
300         return ret;
301      }
302
303       sat::SolvAttr    attr;
304       sat::AttrMatcher attrMatcher;
305       Predicate        predicate;
306       std::string      predicateStr;
307     };
308
309     /** \relates AttrMatchData */
310     inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
311     {
312       str << obj.attr << ": " << obj.attrMatcher;
313       if ( obj.predicate )
314         str << " +(" << obj.predicateStr << ")";
315       return str;
316     }
317
318     /** \relates AttrMatchData */
319     inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
320     {
321       return ( lhs.attr == rhs.attr
322                && lhs.attrMatcher == rhs.attrMatcher
323                && lhs.predicateStr == rhs.predicateStr );
324     }
325
326     /** \relates AttrMatchData */
327     inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
328     { return !( lhs == rhs ); }
329
330     /** \relates AttrMatchData Arbitrary order for std::container. */
331     inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
332     {
333       if ( lhs.attr != rhs.attr )
334         return (  lhs.attr < rhs.attr );
335       if ( lhs.attrMatcher != rhs.attrMatcher )
336         return (  lhs.attrMatcher < rhs.attrMatcher );
337       if ( lhs.predicateStr != rhs.predicateStr )
338         return (  lhs.predicateStr < rhs.predicateStr );
339       return false;
340     }
341
342     typedef std::list<AttrMatchData> AttrMatchList;
343
344
345   } /////////////////////////////////////////////////////////////////
346   // namespace
347   ///////////////////////////////////////////////////////////////////
348
349   ///////////////////////////////////////////////////////////////////
350   //
351   //  CLASS NAME : PoolQuery::Impl
352   //
353   /** */
354   class PoolQuery::Impl
355   {
356   public:
357     Impl()
358       : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
359       , _match_word(false)
360       , _require_all(false)
361       , _status_flags(ALL)
362     {}
363
364     ~Impl()
365     {}
366
367   public:
368     /** String representation */
369     string asString() const;
370
371     /** \name Raw query options. */
372     //@{
373     /** Raw search strings. */
374     StrContainer _strings;
375     /** Raw attributes */
376     AttrRawStrMap _attrs;
377     /** Uncompiled attributes with predicate. */
378     std::set<AttrMatchData> _uncompiledPredicated;
379
380     /** Sat solver search flags */
381     Match _flags;
382     bool _match_word;
383     bool _require_all;
384
385     /** Sat solver status flags */
386     StatusFilter _status_flags;
387
388     /** Edition condition operand */
389     Edition _edition;
390     /** Operator for edition condition */
391     Rel _op;
392
393     /** Repos to search. */
394     StrContainer _repos;
395
396     /** Kinds to search */
397     Kinds _kinds;
398     //@}
399
400   public:
401
402     bool operator==( const PoolQuery::Impl & rhs ) const
403     {
404       return ( _strings == rhs._strings
405                && _attrs == rhs._attrs
406                && _uncompiledPredicated == rhs._uncompiledPredicated
407                && _flags == rhs._flags
408                && _match_word == rhs._match_word
409                && _require_all == rhs._require_all
410                && _status_flags == rhs._status_flags
411                && _edition == rhs._edition
412                && _op == rhs._op
413                && _repos == rhs._repos
414                && _kinds == rhs._kinds );
415     }
416
417     bool operator!=( const PoolQuery::Impl & rhs ) const
418     { return ! operator==( rhs ); }
419
420   public:
421     /** Compile the regex.
422      * Basically building the \ref _attrMatchList from strings.
423      * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
424      */
425     void compile() const;
426
427     /** AttrMatcher per attribtue. */
428     mutable AttrMatchList _attrMatchList;
429
430   private:
431     /** Pass flags from \ref compile, as they may have been changed. */
432     string createRegex( const StrContainer & container, const Match & flags ) const;
433
434   private:
435     friend Impl * rwcowClone<Impl>( const Impl * rhs );
436     /** clone for RWCOW_pointer */
437     Impl * clone() const
438     { return new Impl( *this ); }
439   };
440
441   ///////////////////////////////////////////////////////////////////
442
443   struct MyInserter
444   {
445     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
446
447     bool operator()(const string & str)
448     {
449       _cont.insert(str);
450       return true;
451     }
452
453     PoolQuery::StrContainer & _cont;
454   };
455
456
457   struct EmptyFilter
458   {
459     bool operator()(const string & str)
460     {
461       return !str.empty();
462     }
463   };
464
465   void PoolQuery::Impl::compile() const
466   {
467     _attrMatchList.clear();
468
469     Match cflags( _flags );
470     if ( cflags.mode() == Match::OTHER ) // this will never succeed...
471       ZYPP_THROW( MatchUnknownModeException( cflags ) );
472
473     /** Compiled search strings. */
474     string rcstrings;
475
476
477     // 'different'         - will have to iterate through all and match by ourselves (slow)
478     // 'same'              - will pass the compiled string to dataiterator_init
479     // 'one-attr'          - will pass it to dataiterator_init
480     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
481
482     // // NO ATTRIBUTE
483     // else
484     //   for all _strings
485     //     create regex; store in rcstrings; if more strings flag regex;
486     if (_attrs.empty())
487     {
488       ; // A default 'query-all' will be added after all sources are processed.
489     }
490
491     // // ONE ATTRIBUTE
492     // else if _attrs is not empty but it contains just one attr
493     //   for all _strings and _attr[key] strings
494     //     create regex; flag 'one-attr'; if more strings flag regex;
495     else if (_attrs.size() == 1)
496     {
497       StrContainer joined;
498       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
499       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
500       rcstrings = createRegex(joined, cflags);
501       if (joined.size() > 1) // switch to regex for multiple strings
502         cflags.setModeRegex();
503       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
504                                 sat::AttrMatcher( rcstrings, cflags ) ) );
505     }
506
507     // // MULTIPLE ATTRIBUTES
508     else
509     {
510       // check whether there are any per-attribute strings
511       bool attrvals_empty = true;
512       for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
513         if (!ai->second.empty())
514           for(StrContainer::const_iterator it = ai->second.begin();
515               it != ai->second.end(); it++)
516             if (!it->empty())
517             {
518               attrvals_empty = false;
519               goto attremptycheckend;
520             }
521 attremptycheckend:
522
523       // chceck whether the per-attribute strings are all the same
524       bool attrvals_thesame = true;
525       AttrRawStrMap::const_iterator ai = _attrs.begin();
526       const StrContainer & set1 = ai->second;
527       ++ai;
528       for (; ai != _attrs.end(); ++ai)
529       {
530         StrContainer result;
531         set_difference(
532           set1.begin(), set1.end(),
533           ai->second.begin(), ai->second.end(),
534           inserter(result, result.begin())/*, ltstr()*/);
535         if (!result.empty())
536         {
537           attrvals_thesame = false;
538           break;
539         }
540       }
541
542       // // THE SAME STRINGS FOR DIFFERENT ATTRS
543       // else if _attrs is not empty but it does not contain strings
544       //   for each key in _attrs take all _strings
545       //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
546       if (attrvals_empty || attrvals_thesame)
547       {
548         StrContainer joined;
549         if (attrvals_empty)
550         {
551           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
552           rcstrings = createRegex(joined, cflags);
553         }
554         else
555         {
556           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
557           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
558           rcstrings = createRegex(joined, cflags);
559         }
560         if (joined.size() > 1) // switch to regex for multiple strings
561           cflags.setModeRegex();
562         // May use the same AttrMatcher for all
563         sat::AttrMatcher matcher( rcstrings, cflags );
564         for_( ai, _attrs.begin(), _attrs.end() )
565         {
566           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
567         }
568       }
569
570       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
571       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
572       //   for each key in _attrs take all _strings + all _attrs[key] strings
573       //     create regex; flag 'different'; if more strings flag regex;
574       else
575       {
576         for_(ai, _attrs.begin(), _attrs.end())
577         {
578           StrContainer joined;
579           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
580           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
581           string s = createRegex(joined, cflags);
582           if (joined.size() > 1) // switch to regex for multiple strings
583             cflags.setModeRegex();
584           _attrMatchList.push_back( AttrMatchData( ai->first,
585                                     sat::AttrMatcher( s, cflags ) ) );
586         }
587       }
588     }
589
590     // Now handle any predicated queries
591     if ( ! _uncompiledPredicated.empty() )
592     {
593       StrContainer global;
594       invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
595       for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
596       {
597         if ( it->attrMatcher.flags().mode() == Match::OTHER )
598         {
599           // need to compile:
600           StrContainer joined( global );
601           const std::string & mstr( it->attrMatcher.searchstring() );
602           if ( ! mstr.empty() )
603             joined.insert( mstr );
604
605           cflags = _flags;
606           rcstrings = createRegex( joined, cflags );
607           if ( joined.size() > 1 ) // switch to regex for multiple strings
608             cflags.setModeRegex();
609
610           _attrMatchList.push_back( AttrMatchData( it->attr,
611                                     sat::AttrMatcher( rcstrings, cflags ),
612                                                       it->predicate, it->predicateStr ) );
613         }
614         else
615         {
616           // copy matcher
617          _attrMatchList.push_back( *it );
618         }
619       }
620     }
621
622     // If no attributes defined at all, then add 'query all'
623     if ( _attrMatchList.empty() )
624     {
625       cflags = _flags;
626       rcstrings = createRegex( _strings, cflags );
627       if ( _strings.size() > 1 ) // switch to regex for multiple strings
628         cflags.setModeRegex();
629       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
630                                 sat::AttrMatcher( rcstrings, cflags ) ) );
631     }
632
633     // Finally check here, whether all involved regex compile.
634     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
635     {
636       it->attrMatcher.compile(); // throws on error
637     }
638     //DBG << asString() << endl;
639   }
640
641
642   /**
643    * Converts '*' and '?' wildcards within str into their regex equivalents.
644    */
645   static string wildcards2regex(const string & str)
646   {
647     string regexed = str;
648
649     string r_all(".*"); // regex equivalent of '*'
650     string r_one(".");  // regex equivalent of '?'
651     string::size_type pos;
652
653     // replace all "*" in input with ".*"
654     for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
655       regexed = regexed.replace(pos, 1, r_all);
656
657     // replace all "?" in input with "."
658     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
659       regexed = regexed.replace(pos, 1, r_one);
660
661     return regexed;
662   }
663
664   string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
665   {
666 //! macro for word boundary tags for regexes
667 #define WB (_match_word ? string("\\b") : string())
668     string rstr;
669
670     if (container.empty())
671       return rstr;
672
673     if (container.size() == 1)
674     {
675       return WB + *container.begin() + WB;
676     }
677
678     // multiple strings
679
680     bool use_wildcards = flags.isModeGlob();
681     StrContainer::const_iterator it = container.begin();
682     string tmp;
683
684     if (use_wildcards)
685       tmp = wildcards2regex(*it);
686     else
687       tmp = *it;
688
689     if (_require_all)
690     {
691       if ( ! flags.isModeString() ) // not match exact
692         tmp += ".*" + WB + tmp;
693       rstr = "(?=" + tmp + ")";
694     }
695     else
696     {
697       if ( flags.isModeString() || flags.isModeGlob() )
698         rstr = "^";
699       rstr += WB + "(" + tmp;
700     }
701
702     ++it;
703
704     for (; it != container.end(); ++it)
705     {
706       if (use_wildcards)
707         tmp = wildcards2regex(*it);
708       else
709         tmp = *it;
710
711       if (_require_all)
712       {
713         if ( ! flags.isModeString() ) // not match exact
714           tmp += ".*" + WB + tmp;
715         rstr += "(?=" + tmp + ")";
716       }
717       else
718       {
719         rstr += "|" + tmp;
720       }
721     }
722
723     if (_require_all)
724     {
725       if ( ! flags.isModeString() ) // not match exact
726         rstr += WB + ".*";
727     }
728     else
729     {
730       rstr += ")" + WB;
731       if ( flags.isModeString() || flags.isModeGlob() )
732         rstr += "$";
733     }
734
735     return rstr;
736 #undef WB
737   }
738
739   string PoolQuery::Impl::asString() const
740   {
741     ostringstream o;
742
743     o << "kinds: ";
744     if ( _kinds.empty() )
745       o << "ALL";
746     else
747     {
748       for(Kinds::const_iterator it = _kinds.begin();
749           it != _kinds.end(); ++it)
750         o << *it << " ";
751     }
752     o << endl;
753
754     o << "repos: ";
755     if ( _repos.empty() )
756       o << "ALL";
757     else
758     {
759       for(StrContainer::const_iterator it = _repos.begin();
760           it != _repos.end(); ++it)
761         o << *it << " ";
762     }
763     o << endl;
764
765     o << "version: "<< _op << " " << _edition.asString() << endl;
766     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
767                                        : "ALL" ) << endl;
768
769     o << "string match flags: " << Match(_flags) << endl;
770
771     // raw
772     o << "strings: ";
773     for(StrContainer::const_iterator it = _strings.begin();
774         it != _strings.end(); ++it)
775       o << *it << " ";
776     o << endl;
777
778     o << "attributes: " << endl;
779     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
780     {
781       o << "* " << ai->first << ": ";
782       for(StrContainer::const_iterator vi = ai->second.begin();
783           vi != ai->second.end(); ++vi)
784         o << *vi << " ";
785       o << endl;
786     }
787
788     o << "predicated: " << endl;
789     for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
790     {
791       o << "* " << *it << endl;
792     }
793
794     // compiled
795     o << "last attribute matcher compiled: " << endl;
796     if ( _attrMatchList.empty() )
797     {
798       o << "not yet compiled" << endl;
799     }
800     else
801     {
802       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
803       {
804         o << "* " << *it << endl;
805       }
806     }
807     return o.str();
808   }
809
810   ///////////////////////////////////////////////////////////////////
811
812   ///////////////////////////////////////////////////////////////////
813   //
814   //    CLASS NAME : PoolQuery
815   //
816   ///////////////////////////////////////////////////////////////////
817
818   PoolQuery::PoolQuery()
819     : _pimpl(new Impl())
820   {}
821
822   PoolQuery::~PoolQuery()
823   {}
824
825   void PoolQuery::addRepo(const std::string &repoalias)
826   {
827     if (repoalias.empty())
828     {
829       WAR << "ignoring an empty repository alias" << endl;
830       return;
831     }
832     _pimpl->_repos.insert(repoalias);
833   }
834
835   void PoolQuery::addKind(const ResKind & kind)
836   { _pimpl->_kinds.insert(kind); }
837
838   void PoolQuery::addString(const string & value)
839   { _pimpl->_strings.insert(value); }
840
841   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
842   { _pimpl->_attrs[attr].insert(value); }
843
844   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
845   { return addDependency( attr, name, op, edition, Arch_empty ); }
846
847   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
848   {
849     switch ( op.inSwitch() )
850     {
851       case Rel::ANY_e:  // no additional constraint on edition.
852         if ( arch.empty() )     // no additional constraint on arch.
853           addAttribute( attr, name );
854         return;
855
856       case Rel::NONE_e: // will never match.
857         return;
858
859       default: // go and add the predicated query (uncompiled)
860         break;
861     }
862
863     // Match::OTHER indicates need to compile
864     // (merge global search strings into name).
865     AttrMatchData attrMatchData( attr, sat::AttrMatcher( name, Match::OTHER ) );
866
867     if ( isDependencyAttribute( attr ) )
868       attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
869     else
870       attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
871
872     _pimpl->_uncompiledPredicated.insert( attrMatchData );
873   }
874
875   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
876   {
877     CapDetail cap( cap_r );
878     if ( ! cap.isSimple() ) // will never match.
879       return;
880
881     // Matches STRING per default. (won't get compiled!)
882     AttrMatchData attrMatchData( attr, sat::AttrMatcher( cap.name().asString() ) );
883
884     if ( isDependencyAttribute( attr ) )
885       attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
886     else
887       attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
888
889     _pimpl->_uncompiledPredicated.insert( attrMatchData );
890   }
891
892   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
893   {
894     _pimpl->_edition = edition;
895     _pimpl->_op = op;
896   }
897
898   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring(); }
899   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString(); }
900   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex(); }
901   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob(); }
902   void PoolQuery::setMatchWord()
903   {
904     _pimpl->_match_word = true;
905     _pimpl->_flags.setModeRegex();
906   }
907
908   Match PoolQuery::flags() const
909   { return _pimpl->_flags; }
910   void PoolQuery::setFlags( const Match & flags )
911   { _pimpl->_flags = flags; }
912
913
914   void PoolQuery::setInstalledOnly()
915   { _pimpl->_status_flags = INSTALLED_ONLY; }
916   void PoolQuery::setUninstalledOnly()
917   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
918   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
919   { _pimpl->_status_flags = flags; }
920
921
922   void PoolQuery::setRequireAll(bool require_all)
923   { _pimpl->_require_all = require_all; }
924
925
926   const PoolQuery::StrContainer &
927   PoolQuery::strings() const
928   { return _pimpl->_strings; }
929
930   const PoolQuery::AttrRawStrMap &
931   PoolQuery::attributes() const
932   { return _pimpl->_attrs; }
933
934   const PoolQuery::StrContainer &
935   PoolQuery::attribute(const sat::SolvAttr & attr) const
936   {
937     static const PoolQuery::StrContainer nocontainer;
938     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
939     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
940   }
941
942   const Edition PoolQuery::edition() const
943   { return _pimpl->_edition; }
944   const Rel PoolQuery::editionRel() const
945   { return _pimpl->_op; }
946
947
948   const PoolQuery::Kinds &
949   PoolQuery::kinds() const
950   { return _pimpl->_kinds; }
951
952   const PoolQuery::StrContainer &
953   PoolQuery::repos() const
954   { return _pimpl->_repos; }
955
956
957   bool PoolQuery::caseSensitive() const
958   { return !_pimpl->_flags.test( Match::NOCASE ); }
959   void PoolQuery::setCaseSensitive( bool value )
960   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
961
962   bool PoolQuery::filesMatchFullPath() const
963   { return _pimpl->_flags.test( Match::FILES ); }
964   void PoolQuery::setFilesMatchFullPath( bool value )
965   { _pimpl->_flags.turn( Match::FILES, value ); }
966
967   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
968   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring(); }
969   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
970   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
971
972   bool PoolQuery::matchWord() const
973   { return _pimpl->_match_word; }
974
975   bool PoolQuery::requireAll() const
976   { return _pimpl->_require_all; }
977
978   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
979   { return _pimpl->_status_flags; }
980
981   bool PoolQuery::empty() const
982   {
983     try { return begin() == end(); }
984     catch (const Exception & ex) {}
985     return true;
986   }
987
988   PoolQuery::size_type PoolQuery::size() const
989   {
990     try
991     {
992       size_type count = 0;
993       for_( it, begin(), end() )
994         ++count;
995       return count;
996     }
997     catch (const Exception & ex) {}
998     return 0;
999   }
1000
1001   void PoolQuery::execute(ProcessResolvable fnc)
1002   { invokeOnEach( begin(), end(), fnc); }
1003
1004
1005   ///////////////////////////////////////////////////////////////////
1006   //
1007   //  CLASS NAME : PoolQuery::Attr
1008   //
1009   /**
1010    * represents all atributes in PoolQuery except SolvAtributes, which are
1011    * used as is (not needed extend anything if someone adds new solv attr)
1012    */
1013   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1014   {
1015   private:
1016     friend class IdStringType<PoolQueryAttr>;
1017     IdString _str;
1018   public:
1019
1020     //noAttr
1021     PoolQueryAttr(){}
1022
1023     explicit PoolQueryAttr( const char* cstr_r )
1024         : _str( cstr_r )
1025       {}
1026
1027     explicit PoolQueryAttr( const std::string & str_r )
1028         : _str( str_r )
1029       {}
1030
1031     // unknown atributes
1032     static const PoolQueryAttr noAttr;
1033
1034     // PoolQuery's own attributes
1035     static const PoolQueryAttr repoAttr;
1036     static const PoolQueryAttr kindAttr;
1037     static const PoolQueryAttr stringAttr;
1038     static const PoolQueryAttr stringTypeAttr;
1039     static const PoolQueryAttr requireAllAttr;
1040     static const PoolQueryAttr caseSensitiveAttr;
1041     static const PoolQueryAttr installStatusAttr;
1042     static const PoolQueryAttr editionAttr;
1043     static const PoolQueryAttr complexAttr;
1044   };
1045
1046   const PoolQueryAttr PoolQueryAttr::noAttr;
1047
1048   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1049   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1050   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1051   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1052   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
1053   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1054   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1055   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1056   const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1057
1058   class StringTypeAttr : public IdStringType<PoolQueryAttr>
1059   {
1060     friend class IdStringType<StringTypeAttr>;
1061     IdString _str;
1062
1063   public:
1064     StringTypeAttr(){}
1065     explicit StringTypeAttr( const char* cstr_r )
1066             : _str( cstr_r ){}
1067     explicit StringTypeAttr( const std::string & str_r )
1068              : _str( str_r ){}
1069
1070     static const StringTypeAttr noAttr;
1071
1072     static const StringTypeAttr exactAttr;
1073     static const StringTypeAttr substringAttr;
1074     static const StringTypeAttr regexAttr;
1075     static const StringTypeAttr globAttr;
1076     static const StringTypeAttr wordAttr;
1077   };
1078
1079   const StringTypeAttr StringTypeAttr::noAttr;
1080
1081   const StringTypeAttr StringTypeAttr::exactAttr("exact");
1082   const StringTypeAttr StringTypeAttr::substringAttr("substring");
1083   const StringTypeAttr StringTypeAttr::regexAttr("regex");
1084   const StringTypeAttr StringTypeAttr::globAttr("glob");
1085   const StringTypeAttr StringTypeAttr::wordAttr("word");
1086
1087   ///////////////////////////////////////////////////////////////////
1088
1089
1090   //\TODO maybe ctor with stream can be usefull
1091   //\TODO let it throw, let it throw, let it throw.
1092   bool PoolQuery::recover( istream &str, char delim )
1093   {
1094     bool finded_something = false; //indicates some atributes is finded
1095     string s;
1096     do {
1097       if ( str.eof() )
1098         break;
1099
1100       getline( str, s, delim );
1101
1102       if ((!s.empty()) && s[0]=='#') //comment
1103       {
1104         continue;
1105       }
1106
1107       string::size_type pos = s.find(':');
1108       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1109       {
1110         if (finded_something) //is first blank line after record?
1111         {
1112           break;
1113         }
1114         else
1115         {
1116           continue;
1117         }
1118       }
1119
1120       finded_something = true;
1121
1122       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1123       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1124
1125       PoolQueryAttr attribute( attrName );
1126
1127       MIL << "attribute name: " << attrName << endl;
1128
1129       if ( attribute==PoolQueryAttr::repoAttr )
1130       {
1131         addRepo( attrValue );
1132       }
1133       /* some backwards compatibility */
1134       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1135       {
1136         addKind( ResKind(attrValue) );
1137       }
1138       else if ( attribute==PoolQueryAttr::stringAttr
1139         || attribute=="global_string")
1140       {
1141         addString( attrValue );
1142       }
1143       else if ( attribute==PoolQueryAttr::stringTypeAttr
1144         || attribute=="string_type" )
1145       {
1146         StringTypeAttr s(attrValue);
1147         if( s == StringTypeAttr::regexAttr )
1148         {
1149           setMatchRegex();
1150         }
1151         else if ( s == StringTypeAttr::globAttr )
1152         {
1153           setMatchGlob();
1154         }
1155         else if ( s == StringTypeAttr::exactAttr )
1156         {
1157           setMatchExact();
1158         }
1159         else if ( s == StringTypeAttr::substringAttr )
1160         {
1161           setMatchSubstring();
1162         }
1163         else if ( s == StringTypeAttr::wordAttr )
1164         {
1165           setMatchWord();
1166         }
1167         else if ( s == StringTypeAttr::noAttr )
1168         {
1169           WAR << "unknown string type " << attrValue << endl;
1170         }
1171         else
1172         {
1173           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1174         }
1175       }
1176       else if ( attribute==PoolQueryAttr::requireAllAttr )
1177       {
1178         if ( str::strToTrue(attrValue) )
1179         {
1180           setRequireAll(true);
1181         }
1182         else if ( !str::strToFalse(attrValue) )
1183         {
1184           setRequireAll(false);
1185         }
1186         else
1187         {
1188           WAR << "unknown boolean value " << attrValue << endl;
1189         }
1190       }
1191       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1192       {
1193         if ( str::strToTrue(attrValue) )
1194         {
1195           setCaseSensitive(true);
1196         }
1197         else if ( !str::strToFalse(attrValue) )
1198         {
1199           setCaseSensitive(false);
1200         }
1201         else
1202         {
1203           WAR << "unknown boolean value " << attrValue << endl;
1204         }
1205       }
1206       else if ( attribute==PoolQueryAttr::installStatusAttr )
1207       {
1208         if( attrValue == "all" )
1209         {
1210           setStatusFilterFlags( ALL );
1211         }
1212         else if( attrValue == "installed" )
1213         {
1214           setInstalledOnly();
1215         }
1216         else if( attrValue == "not-installed" )
1217         {
1218           setUninstalledOnly();
1219         }
1220         else
1221         {
1222           WAR << "Unknown value for install status " << attrValue << endl;
1223         }
1224       }
1225       else if ( attribute == PoolQueryAttr::editionAttr)
1226       {
1227         string::size_type pos;
1228         Rel rel("==");
1229         if (attrValue.find_first_of("=<>!") == 0)
1230         {
1231           pos = attrValue.find_last_of("=<>");
1232           rel = Rel(attrValue.substr(0, pos+1));
1233           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1234         }
1235
1236         setEdition(Edition(attrValue), rel);
1237       }
1238       else if ( attribute == PoolQueryAttr::complexAttr )
1239       {
1240         try
1241         {
1242           _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1243         }
1244         catch ( const Exception & err )
1245         {
1246           WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1247
1248         }
1249       }
1250       else if ( attribute==PoolQueryAttr::noAttr )
1251       {
1252         WAR << "empty attribute name" << endl;
1253       }
1254       else
1255       {
1256         string s = attrName;
1257         str::replaceAll( s,"_",":" );
1258         SolvAttr a(s);
1259         if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1260         {
1261           Capability c( attrValue );
1262           CapDetail d( c );
1263           if ( d.isVersioned() )
1264             addDependency( a, d.name().asString(), d.op(), d.ed() );
1265           else
1266             addDependency( a, attrValue );
1267         }
1268         else
1269           addAttribute( a, attrValue );
1270       }
1271
1272     } while ( true );
1273
1274     return finded_something;
1275   }
1276
1277   void PoolQuery::serialize( ostream &str, char delim ) const
1278   {
1279     //separating delim
1280     str << delim;
1281     //iterate thrue all settings and write it
1282     static const zypp::PoolQuery q; //not save default options, so create default query example
1283
1284     for_( it, repos().begin(), repos().end() )
1285     {
1286       str << "repo: " << *it << delim ;
1287     }
1288
1289     for_( it, kinds().begin(), kinds().end() )
1290     {
1291       str << PoolQueryAttr::kindAttr.asString() << ": "
1292           << it->idStr() << delim ;
1293     }
1294
1295     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1296       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1297
1298     if (matchMode()!=q.matchMode())
1299     {
1300       switch( matchMode() )
1301       {
1302       case Match::STRING:
1303         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1304         break;
1305       case Match::SUBSTRING:
1306         str << PoolQueryAttr::stringTypeAttr.asString()
1307             << ": substring" << delim;
1308         break;
1309       case Match::GLOB:
1310         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1311         break;
1312       case Match::REGEX:
1313         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1314         break;
1315       default:
1316         WAR << "unknown match type "  << matchMode() << endl;
1317       }
1318     }
1319
1320     if( caseSensitive() != q.caseSensitive() )
1321     {
1322       str << "case_sensitive: ";
1323       if (caseSensitive())
1324       {
1325         str << "on" << delim;
1326       }
1327       else
1328       {
1329         str << "off" << delim;
1330       }
1331     }
1332
1333     if( requireAll() != q.requireAll() )
1334     {
1335       str << "require_all: ";
1336       if (requireAll())
1337       {
1338         str << "on" << delim;
1339       }
1340       else
1341       {
1342         str << "off" << delim;
1343       }
1344     }
1345
1346     if( statusFilterFlags() != q.statusFilterFlags() )
1347     {
1348       switch( statusFilterFlags() )
1349       {
1350       case ALL:
1351         str << "install_status: all" << delim;
1352         break;
1353       case INSTALLED_ONLY:
1354         str << "install_status: installed" << delim;
1355         break;
1356       case UNINSTALLED_ONLY:
1357         str << "install_status: not-installed" << delim;
1358         break;
1359       }
1360     }
1361
1362     for_( it, strings().begin(), strings().end() )
1363     {
1364       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1365     }
1366
1367     for_( it, attributes().begin(), attributes().end() )
1368     {
1369       string s = it->first.asString();
1370       str::replaceAll(s,":","_");
1371       for_( it2,it->second.begin(),it->second.end() )
1372       {
1373         str << s <<": "<< *it2 << delim;
1374       }
1375     }
1376
1377     for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1378     {
1379       str << "complex: "<< it->serialize() << delim;
1380     }
1381
1382     //separating delim - protection
1383     str << delim;
1384   }
1385
1386   string PoolQuery::asString() const
1387   { return _pimpl->asString(); }
1388
1389   ostream & operator<<( ostream & str, const PoolQuery & obj )
1390   { return str << obj.asString(); }
1391
1392   std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1393   { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1394
1395   bool PoolQuery::operator==( const PoolQuery & rhs ) const
1396   { return *_pimpl == *rhs._pimpl; }
1397
1398   ///////////////////////////////////////////////////////////////////
1399   namespace detail
1400   { /////////////////////////////////////////////////////////////////
1401
1402     ///////////////////////////////////////////////////////////////////
1403     //
1404     //  CLASS NAME : PoolQueryMatcher
1405     //
1406     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1407      *
1408      * Basically the matcher performs a base query, which should preselect
1409      * candidates for a match. And has some filter conditions on top of it.
1410      * Query and fileter depend on the \ref PoolQuery settings.
1411      *
1412      * Matcher must be stateless, as it is shared between multiple
1413      * \ref PoolQueryIterator instances.
1414      *
1415      * If \ref base_iterator is at the \ref end, \ref advance moves it
1416      * to the first match. Otherwise advance moves to the next match, or
1417      * to the \ref end, if there is no more match.
1418      *
1419      * \note The original implementation treated an empty search string as
1420      * <it>"match always"</it>. We stay compatible.
1421      */
1422     class PoolQueryMatcher
1423     {
1424       public:
1425         typedef sat::LookupAttr::iterator base_iterator;
1426
1427       public:
1428         const base_iterator & end() const
1429         {
1430           static base_iterator _end;
1431           return _end;
1432         }
1433
1434         bool advance( base_iterator & base_r ) const
1435         {
1436           if ( base_r == end() )
1437             base_r = startNewQyery(); // first candidate
1438           else
1439           {
1440             base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1441             ++base_r; // advance to next candidate
1442           }
1443
1444           while ( base_r != end() )
1445           {
1446             if ( isAMatch( base_r ) )
1447               return true;
1448             // No match: try next
1449             ++base_r;
1450           }
1451           return false;
1452         }
1453
1454         /** Provide all matching attributes within this solvable.
1455          *
1456          */
1457         void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1458         {
1459           if ( base_r == end() )
1460             return;
1461
1462           sat::Solvable inSolvable( base_r.inSolvable() );
1463
1464           if ( _attrMatchList.size() == 1 )
1465           {
1466             // base_r is already on the 1st matching attribute!
1467             // String matching is done by the base iterator. We must check the predicate here.
1468             // Let's see if there are more matches for this solvable:
1469             base_iterator base( base_r );
1470             base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1471             return_r.push_back( base );
1472
1473             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1474             for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1475             {
1476               if ( ! predicate || predicate( base ) )
1477                 return_r.push_back( base );
1478             }
1479           }
1480           else
1481           {
1482             // Here: search all attributes ;(
1483             for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1484             {
1485               const AttrMatchData & matchData( *mi );
1486               sat::LookupAttr q( matchData.attr, inSolvable );
1487               if ( matchData.attrMatcher ) // an empty searchstring matches always
1488                 q.setAttrMatcher( matchData.attrMatcher );
1489
1490               if ( ! q.empty() ) // there are matches.
1491               {
1492                 // now check any predicate:
1493                 const AttrMatchData::Predicate & predicate( matchData.predicate );
1494                 for_( it, q.begin(), q.end() )
1495                 {
1496                   if ( ! predicate || predicate( it ) )
1497                     return_r.push_back( it );
1498                 }
1499               }
1500             }
1501           }
1502         }
1503
1504       public:
1505         /** Ctor stores the \ref PoolQuery settings.
1506          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1507          */
1508         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1509         {
1510           query_r->compile();
1511
1512           // Repo restriction:
1513           sat::Pool satpool( sat::Pool::instance() );
1514           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1515           {
1516             Repository r( satpool.reposFind( *it ) );
1517             if ( r )
1518               _repos.insert( r );
1519           }
1520           // Kind restriction:
1521           _kinds = query_r->_kinds;
1522           // Edition restriction:
1523           _op      = query_r->_op;
1524           _edition = query_r->_edition;
1525           // Status restriction:
1526           _status_flags = query_r->_status_flags;
1527           // AttrMatcher
1528           _attrMatchList = query_r->_attrMatchList;
1529         }
1530
1531         ~PoolQueryMatcher()
1532         {}
1533
1534       private:
1535         /** Initialize a new base query. */
1536         base_iterator startNewQyery() const
1537         {
1538           sat::LookupAttr q;
1539
1540           // Repo restriction:
1541           if ( _repos.size() == 1 )
1542             q.setRepo( *_repos.begin() );
1543           // else: handled in isAMatch.
1544
1545           // Attribute restriction:
1546           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1547           {
1548             const AttrMatchData & matchData( _attrMatchList.front() );
1549             q.setAttr( matchData.attr );
1550             if ( matchData.attrMatcher ) // empty searchstring matches always
1551               q.setAttrMatcher( matchData.attrMatcher );
1552           }
1553           else // more than 1 attr (but not all)
1554           {
1555             // no restriction, it's all handled in isAMatch.
1556             q.setAttr( sat::SolvAttr::allAttr );
1557           }
1558
1559           return q.begin();
1560         }
1561
1562
1563         /** Check whether we are on a match.
1564          *
1565          * The check covers the whole Solvable, not just the current
1566          * attribute \c base_r points to. If there's no match, also
1567          * prepare \c base_r to advance appropriately. If there is
1568          * a match, simply return \c true. \ref advance always moves
1569          * to the next Solvable if there was a match.
1570          *
1571          * \note: Caller asserts we're not at \ref end.
1572         */
1573         bool isAMatch( base_iterator & base_r ) const
1574         {
1575           /////////////////////////////////////////////////////////////////////
1576           Repository inRepo( base_r.inRepo() );
1577           // Status restriction:
1578           if ( _status_flags
1579              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1580           {
1581             base_r.nextSkipRepo();
1582             return false;
1583           }
1584           // Repo restriction:
1585           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1586           {
1587             base_r.nextSkipRepo();
1588             return false;
1589           }
1590           /////////////////////////////////////////////////////////////////////
1591           sat::Solvable inSolvable( base_r.inSolvable() );
1592           // Kind restriction:
1593           if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1594           {
1595             base_r.nextSkipSolvable();
1596             return false;
1597           }
1598
1599           // Edition restriction:
1600           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1601           {
1602             base_r.nextSkipSolvable();
1603             return false;
1604           }
1605           /////////////////////////////////////////////////////////////////////
1606           // string and predicate matching:
1607
1608           if ( _attrMatchList.size() == 1 )
1609           {
1610             // String matching was done by the base iterator.
1611             // Now check any predicate:
1612             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1613             if ( ! predicate || predicate( base_r ) )
1614               return true;
1615
1616             return false; // no skip as there may be more occurrences od this attr.
1617           }
1618
1619           // Here: search all attributes ;(
1620           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1621           {
1622             const AttrMatchData & matchData( *mi );
1623             sat::LookupAttr q( matchData.attr, inSolvable );
1624             if ( matchData.attrMatcher ) // an empty searchstring matches always
1625               q.setAttrMatcher( matchData.attrMatcher );
1626
1627             if ( ! q.empty() ) // there are matches.
1628             {
1629               // now check any predicate:
1630               const AttrMatchData::Predicate & predicate( matchData.predicate );
1631               if ( predicate )
1632               {
1633                 for_( it, q.begin(), q.end() )
1634                 {
1635                   if ( predicate( it ) )
1636                     return true;
1637                 }
1638               }
1639               else
1640                 return true;
1641             }
1642           }
1643           base_r.nextSkipSolvable();
1644           return false;
1645         }
1646
1647       private:
1648         /** Repositories include in the search. */
1649         std::set<Repository> _repos;
1650         /** Resolvable kinds to include. */
1651         std::set<ResKind> _kinds;
1652         /** Edition filter. */
1653         Rel _op;
1654         Edition _edition;
1655         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1656         int _status_flags;
1657         /** AttrMatcher per attribtue. */
1658         AttrMatchList _attrMatchList;
1659     };
1660     ///////////////////////////////////////////////////////////////////
1661
1662     void PoolQueryIterator::increment()
1663     {
1664       // matcher restarts if at end! It is called from the ctor
1665       // to get the 1st match. But if the end is reached, it should
1666       // be deleted, otherwise we'd start over again.
1667       if ( !_matcher )
1668         return; // at end
1669       if ( _matches )
1670         _matches.reset(); // invalidate old matches
1671       if ( ! _matcher->advance( base_reference() ) )
1672         _matcher.reset();
1673     }
1674
1675     const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1676     {
1677       if ( _matches )
1678         return *_matches;
1679
1680       if ( !_matcher )
1681       {
1682         // at end of query:
1683         static const Matches _none;
1684         return _none;
1685       }
1686
1687       _matches.reset( new Matches );
1688       _matcher->matchDetail( base_reference(), *_matches );
1689       return *_matches;
1690     }
1691
1692     std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1693     {
1694       str << *obj;
1695       if ( ! obj.matchesEmpty() )
1696       {
1697         for_( it, obj.matchesBegin(), obj.matchesEnd() )
1698         {
1699           str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
1700         }
1701       }
1702       return str;
1703     }
1704
1705     ///////////////////////////////////////////////////////////////////
1706   } //namespace detail
1707   ///////////////////////////////////////////////////////////////////
1708
1709   detail::PoolQueryIterator PoolQuery::begin() const
1710   {
1711     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1712   }
1713
1714   /////////////////////////////////////////////////////////////////
1715 } // namespace zypp
1716 ///////////////////////////////////////////////////////////////////
1717