94489ffe59fb7d7313d6664f9685bcfe4fc91d57
[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/base/StrMatcher.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 StrMatcher
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 strMatcher.
172      *
173      * Example for such a \ref predicate would be an additional edition range
174      * check whan looking for dependencies. The \ref strMatcher 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 StrMatcher & strMatcher_r )
195         : attr( attr_r )
196         , strMatcher( strMatcher_r )
197       {}
198
199       AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
200                      const Predicate & predicate_r, const std::string & predicateStr_r )
201         : attr( attr_r )
202         , strMatcher( strMatcher_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, strMatcher.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, strMatcher.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.strMatcher = StrMatcher( words[2] );
252         if ( words[3] == "C" )
253           ret.strMatcher.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       StrMatcher strMatcher;
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.strMatcher;
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.strMatcher == rhs.strMatcher
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.strMatcher != rhs.strMatcher )
336         return (  lhs.strMatcher < rhs.strMatcher );
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       , _status_flags(ALL)
361     {}
362
363     ~Impl()
364     {}
365
366   public:
367     /** String representation */
368     string asString() const;
369
370     /** \name Raw query options. */
371     //@{
372     /** Raw search strings. */
373     StrContainer _strings;
374     /** Raw attributes */
375     AttrRawStrMap _attrs;
376     /** Uncompiled attributes with predicate. */
377     std::set<AttrMatchData> _uncompiledPredicated;
378
379     /** Sat solver search flags */
380     Match _flags;
381     bool _match_word;
382
383     /** Sat solver status flags */
384     StatusFilter _status_flags;
385
386     /** Edition condition operand */
387     Edition _edition;
388     /** Operator for edition condition */
389     Rel _op;
390
391     /** Repos to search. */
392     StrContainer _repos;
393
394     /** Kinds to search */
395     Kinds _kinds;
396     //@}
397
398   public:
399
400     bool operator==( const PoolQuery::Impl & rhs ) const
401     {
402       if ( _flags == rhs._flags
403         // bnc#792901: while libzypp uses exact match mode for a single
404         // package name lock, zypper always uses glob. :(
405         // We unify those two forms to enable zypper to remove zypp locks
406         // without need to actually evaluate the query (which would require
407         // repos to be loaded).
408         || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
409             || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
410           && _strings.empty()
411           && _attrs.size() == 1
412           && _attrs.begin()->first == sat::SolvAttr::name ) )
413       {
414         return ( _strings == rhs._strings
415               && _attrs == rhs._attrs
416               && _uncompiledPredicated == rhs._uncompiledPredicated
417               && _match_word == rhs._match_word
418               && _status_flags == rhs._status_flags
419               && _edition == rhs._edition
420               && _op == rhs._op
421               && _repos == rhs._repos
422               && _kinds == rhs._kinds );
423       }
424       return false;
425     }
426
427     bool operator!=( const PoolQuery::Impl & rhs ) const
428     { return ! operator==( rhs ); }
429
430   public:
431     /** Compile the regex.
432      * Basically building the \ref _attrMatchList from strings.
433      * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
434      */
435     void compile() const;
436
437     /** StrMatcher per attribtue. */
438     mutable AttrMatchList _attrMatchList;
439
440   private:
441     /** Join patterns in \a container_r according to \a flags_r into a single \ref StrMatcher.
442      * The \ref StrMatcher returned will be a REGEX if more than one pattern was passed.
443      */
444     StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const;
445
446   private:
447     friend Impl * rwcowClone<Impl>( const Impl * rhs );
448     /** clone for RWCOW_pointer */
449     Impl * clone() const
450     { return new Impl( *this ); }
451   };
452
453   ///////////////////////////////////////////////////////////////////
454
455   struct MyInserter
456   {
457     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
458
459     bool operator()(const string & str)
460     {
461       _cont.insert(str);
462       return true;
463     }
464
465     PoolQuery::StrContainer & _cont;
466   };
467
468
469   struct EmptyFilter
470   {
471     bool operator()(const string & str)
472     {
473       return !str.empty();
474     }
475   };
476
477   void PoolQuery::Impl::compile() const
478   {
479     _attrMatchList.clear();
480
481     if ( _flags.mode() == Match::OTHER ) // this will never succeed...
482       ZYPP_THROW( MatchUnknownModeException( _flags ) );
483
484     // 'different'         - will have to iterate through all and match by ourselves (slow)
485     // 'same'              - will pass the compiled string to dataiterator_init
486     // 'one-attr'          - will pass it to dataiterator_init
487     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
488
489     // // NO ATTRIBUTE
490     // else
491     //   for all _strings
492     //     create regex; store in rcstrings; if more strings flag regex;
493     if (_attrs.empty())
494     {
495       ; // A default 'query-all' will be added after all sources are processed.
496     }
497
498     // // ONE ATTRIBUTE
499     // else if _attrs is not empty but it contains just one attr
500     //   for all _strings and _attr[key] strings
501     //     create regex; flag 'one-attr'; if more strings flag regex;
502     else if (_attrs.size() == 1)
503     {
504       StrContainer joined;
505       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
506       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
507
508       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
509     }
510
511     // // MULTIPLE ATTRIBUTES
512     else
513     {
514       // check whether there are any per-attribute strings
515       bool attrvals_empty = true;
516       for_( ai, _attrs.begin(), _attrs.end() )
517       {
518         if ( ai->second.empty() )
519           continue;
520         for_( it, ai->second.begin(), ai->second.end() )
521         {
522           if ( !it->empty() )
523           {
524             attrvals_empty = false;
525             break;
526           }
527         }
528         if ( ! attrvals_empty )
529           break;
530       }
531
532       // chceck whether the per-attribute strings are all the same
533       bool attrvals_thesame = true;
534       AttrRawStrMap::const_iterator ai = _attrs.begin();
535       const StrContainer & set1 = ai->second;
536       ++ai;
537       for (; ai != _attrs.end(); ++ai)
538       {
539         StrContainer result;
540         set_difference(
541           set1.begin(), set1.end(),
542           ai->second.begin(), ai->second.end(),
543           inserter(result, result.begin())/*, ltstr()*/);
544         if (!result.empty())
545         {
546           attrvals_thesame = false;
547           break;
548         }
549       }
550
551       // // THE SAME STRINGS FOR DIFFERENT ATTRS
552       // else if _attrs is not empty but it does not contain strings
553       //   for each key in _attrs take all _strings
554       //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
555       if (attrvals_empty || attrvals_thesame)
556       {
557         StrContainer joined;
558         if (attrvals_empty)
559         {
560           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
561         }
562         else
563         {
564           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
565           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
566         }
567
568         // May use the same StrMatcher for all
569         StrMatcher matcher( joinedStrMatcher( joined, _flags ) );
570         for_( ai, _attrs.begin(), _attrs.end() )
571         {
572           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
573         }
574       }
575
576       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
577       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
578       //   for each key in _attrs take all _strings + all _attrs[key] strings
579       //     create regex; flag 'different'; if more strings flag regex;
580       else
581       {
582         for_(ai, _attrs.begin(), _attrs.end())
583         {
584           StrContainer joined;
585           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
586           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
587
588           _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
589         }
590       }
591     }
592
593     // Now handle any predicated queries
594     if ( ! _uncompiledPredicated.empty() )
595     {
596       StrContainer global;
597       invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
598       for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
599       {
600         if ( it->strMatcher.flags().mode() == Match::OTHER )
601         {
602           // need to compile:
603           StrContainer joined( global );
604           const std::string & mstr( it->strMatcher.searchstring() );
605           if ( ! mstr.empty() )
606             joined.insert( mstr );
607
608           // copy and exchange the StrMatcher
609           AttrMatchData nattr( *it );
610           nattr.strMatcher = joinedStrMatcher( joined, _flags );
611           _attrMatchList.push_back( std::move(nattr) );
612         }
613         else
614         {
615           // copy matcher
616          _attrMatchList.push_back( *it );
617         }
618       }
619     }
620
621     // If no attributes defined at all, then add 'query all'
622     if ( _attrMatchList.empty() )
623     {
624       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
625     }
626
627     // Finally check here, whether all involved regex compile.
628     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
629     {
630       it->strMatcher.compile(); // throws on error
631     }
632     //DBG << asString() << endl;
633   }
634
635   ///////////////////////////////////////////////////////////////////
636   namespace
637   {
638     /** Escape \a str_r for use in a regex.
639      * \a flags_r determines whether the input string is interpreted
640      * as regex, glob or plain string.
641      */
642     std::string rxEscape( std::string str_r, const Match & flags_r )
643     {
644       if ( str_r.empty() || flags_r.isModeRegex() )
645         return str_r;
646
647       if ( flags_r.isModeGlob() )
648         return str::rxEscapeGlob( std::move(str_r) );
649
650       return str::rxEscapeStr( std::move(str_r) );
651     }
652   } // namespace
653   ///////////////////////////////////////////////////////////////////
654
655   StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const
656   {
657     if ( container_r.empty() )
658       return StrMatcher( std::string(), flags_r );
659
660     if ( container_r.size() == 1 && !_match_word )      // use RX to match words
661       return StrMatcher( *container_r.begin(), flags_r );
662
663     // Convert to a regex.
664     // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
665     //       SUBSTRING and REGEX match substrings      (match_word anchores SUBSTRING \b)
666     Match retflags( flags_r );
667     retflags.setModeRegex();
668     str::Str ret;
669
670     if ( flags_r.isModeString() || flags_r.isModeGlob() )
671       ret << "^";
672     else if ( _match_word )
673       ret << "\\b";
674
675     // (..|..|..)
676     char sep = '(';
677     for ( const::std::string & s : container_r )
678     {
679       ret << sep << rxEscape( s, flags_r );
680       if ( sep == '(' )
681         sep = '|';
682     }
683     ret << ')';
684
685     if ( flags_r.isModeString() || flags_r.isModeGlob() )
686       ret << "$";
687     else if ( _match_word )
688       ret << "\\b";
689
690     return StrMatcher( ret, retflags );
691   }
692
693   string PoolQuery::Impl::asString() const
694   {
695     ostringstream o;
696
697     o << "kinds: ";
698     if ( _kinds.empty() )
699       o << "ALL";
700     else
701     {
702       for(Kinds::const_iterator it = _kinds.begin();
703           it != _kinds.end(); ++it)
704         o << *it << " ";
705     }
706     o << endl;
707
708     o << "repos: ";
709     if ( _repos.empty() )
710       o << "ALL";
711     else
712     {
713       for(StrContainer::const_iterator it = _repos.begin();
714           it != _repos.end(); ++it)
715         o << *it << " ";
716     }
717     o << endl;
718
719     o << "version: "<< _op << " " << _edition.asString() << endl;
720     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
721                                        : "ALL" ) << endl;
722
723     o << "string match flags: " << Match(_flags) << endl;
724
725     // raw
726     o << "strings: ";
727     for(StrContainer::const_iterator it = _strings.begin();
728         it != _strings.end(); ++it)
729       o << *it << " ";
730     o << endl;
731
732     o << "attributes: " << endl;
733     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
734     {
735       o << "* " << ai->first << ": ";
736       for(StrContainer::const_iterator vi = ai->second.begin();
737           vi != ai->second.end(); ++vi)
738         o << *vi << " ";
739       o << endl;
740     }
741
742     o << "predicated: " << endl;
743     for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
744     {
745       o << "* " << *it << endl;
746     }
747
748     // compiled
749     o << "last attribute matcher compiled: " << endl;
750     if ( _attrMatchList.empty() )
751     {
752       o << "not yet compiled" << endl;
753     }
754     else
755     {
756       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
757       {
758         o << "* " << *it << endl;
759       }
760     }
761     return o.str();
762   }
763
764   ///////////////////////////////////////////////////////////////////
765
766   ///////////////////////////////////////////////////////////////////
767   //
768   //    CLASS NAME : PoolQuery
769   //
770   ///////////////////////////////////////////////////////////////////
771
772   PoolQuery::PoolQuery()
773     : _pimpl(new Impl())
774   {}
775
776   PoolQuery::~PoolQuery()
777   {}
778
779   void PoolQuery::addRepo(const std::string &repoalias)
780   {
781     if (repoalias.empty())
782     {
783       WAR << "ignoring an empty repository alias" << endl;
784       return;
785     }
786     _pimpl->_repos.insert(repoalias);
787   }
788
789   void PoolQuery::addKind(const ResKind & kind)
790   { _pimpl->_kinds.insert(kind); }
791
792   void PoolQuery::addString(const string & value)
793   { _pimpl->_strings.insert(value); }
794
795   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
796   { _pimpl->_attrs[attr].insert(value); }
797
798   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
799   { return addDependency( attr, name, op, edition, Arch_empty ); }
800
801   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
802   {
803     switch ( op.inSwitch() )
804     {
805       case Rel::ANY_e:  // no additional constraint on edition.
806         if ( arch.empty() )     // no additional constraint on arch.
807         {
808           addAttribute( attr, name );
809           return;
810         }
811         break;
812
813       case Rel::NONE_e: // will never match.
814         return;
815
816       default: // go and add the predicated query (uncompiled)
817         break;
818     }
819
820     // Match::OTHER indicates need to compile
821     // (merge global search strings into name).
822     AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
823
824     if ( isDependencyAttribute( attr ) )
825       attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
826     else
827       attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
828
829     _pimpl->_uncompiledPredicated.insert( attrMatchData );
830   }
831
832   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
833   {
834     CapDetail cap( cap_r );
835     if ( ! cap.isSimple() ) // will never match.
836       return;
837
838     // Matches STRING per default. (won't get compiled!)
839     AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
840
841     if ( isDependencyAttribute( attr ) )
842       attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
843     else
844       attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
845
846     _pimpl->_uncompiledPredicated.insert( attrMatchData );
847   }
848
849   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
850   {
851     _pimpl->_edition = edition;
852     _pimpl->_op = op;
853   }
854
855   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = false; }
856   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString();       _pimpl->_match_word = false; }
857   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex();        _pimpl->_match_word = false; }
858   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob();         _pimpl->_match_word = false; }
859   void PoolQuery::setMatchWord()        { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = true; }
860
861   Match PoolQuery::flags() const
862   { return _pimpl->_flags; }
863   void PoolQuery::setFlags( const Match & flags )
864   { _pimpl->_flags = flags; }
865
866
867   void PoolQuery::setInstalledOnly()
868   { _pimpl->_status_flags = INSTALLED_ONLY; }
869   void PoolQuery::setUninstalledOnly()
870   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
871   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
872   { _pimpl->_status_flags = flags; }
873
874
875   const PoolQuery::StrContainer &
876   PoolQuery::strings() const
877   { return _pimpl->_strings; }
878
879   const PoolQuery::AttrRawStrMap &
880   PoolQuery::attributes() const
881   { return _pimpl->_attrs; }
882
883   const PoolQuery::StrContainer &
884   PoolQuery::attribute(const sat::SolvAttr & attr) const
885   {
886     static const PoolQuery::StrContainer nocontainer;
887     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
888     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
889   }
890
891   const Edition PoolQuery::edition() const
892   { return _pimpl->_edition; }
893   const Rel PoolQuery::editionRel() const
894   { return _pimpl->_op; }
895
896
897   const PoolQuery::Kinds &
898   PoolQuery::kinds() const
899   { return _pimpl->_kinds; }
900
901   const PoolQuery::StrContainer &
902   PoolQuery::repos() const
903   { return _pimpl->_repos; }
904
905
906   bool PoolQuery::caseSensitive() const
907   { return !_pimpl->_flags.test( Match::NOCASE ); }
908   void PoolQuery::setCaseSensitive( bool value )
909   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
910
911   bool PoolQuery::filesMatchFullPath() const
912   { return _pimpl->_flags.test( Match::FILES ); }
913   void PoolQuery::setFilesMatchFullPath( bool value )
914   { _pimpl->_flags.turn( Match::FILES, value ); }
915
916   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
917   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; }
918   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
919   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
920   bool PoolQuery::matchWord() const             { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
921
922   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
923   { return _pimpl->_status_flags; }
924
925   bool PoolQuery::empty() const
926   {
927     try { return begin() == end(); }
928     catch (const Exception & ex) {}
929     return true;
930   }
931
932   PoolQuery::size_type PoolQuery::size() const
933   {
934     try
935     {
936       size_type count = 0;
937       for_( it, begin(), end() )
938         ++count;
939       return count;
940     }
941     catch (const Exception & ex) {}
942     return 0;
943   }
944
945   void PoolQuery::execute(ProcessResolvable fnc)
946   { invokeOnEach( begin(), end(), fnc); }
947
948
949   /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
950   /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const    { return false; }
951
952   ///////////////////////////////////////////////////////////////////
953   //
954   //  CLASS NAME : PoolQuery::Attr
955   //
956   /**
957    * represents all atributes in PoolQuery except SolvAtributes, which are
958    * used as is (not needed extend anything if someone adds new solv attr)
959    */
960   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
961   {
962   private:
963     friend class IdStringType<PoolQueryAttr>;
964     IdString _str;
965   public:
966
967     //noAttr
968     PoolQueryAttr(){}
969
970     explicit PoolQueryAttr( const char* cstr_r )
971         : _str( cstr_r )
972       {}
973
974     explicit PoolQueryAttr( const std::string & str_r )
975         : _str( str_r )
976       {}
977
978     // unknown atributes
979     static const PoolQueryAttr noAttr;
980
981     // PoolQuery's own attributes
982     static const PoolQueryAttr repoAttr;
983     static const PoolQueryAttr kindAttr;
984     static const PoolQueryAttr stringAttr;
985     static const PoolQueryAttr stringTypeAttr;
986     static const PoolQueryAttr requireAllAttr;  // LEAGACY: attribute was defined but never implemented.
987     static const PoolQueryAttr caseSensitiveAttr;
988     static const PoolQueryAttr installStatusAttr;
989     static const PoolQueryAttr editionAttr;
990     static const PoolQueryAttr complexAttr;
991   };
992
993   const PoolQueryAttr PoolQueryAttr::noAttr;
994
995   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
996   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
997   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
998   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
999   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");     // LEAGACY: attribute was defined but never implemented.
1000   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1001   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1002   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1003   const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1004
1005   class StringTypeAttr : public IdStringType<PoolQueryAttr>
1006   {
1007     friend class IdStringType<StringTypeAttr>;
1008     IdString _str;
1009
1010   public:
1011     StringTypeAttr(){}
1012     explicit StringTypeAttr( const char* cstr_r )
1013             : _str( cstr_r ){}
1014     explicit StringTypeAttr( const std::string & str_r )
1015              : _str( str_r ){}
1016
1017     static const StringTypeAttr noAttr;
1018
1019     static const StringTypeAttr exactAttr;
1020     static const StringTypeAttr substringAttr;
1021     static const StringTypeAttr regexAttr;
1022     static const StringTypeAttr globAttr;
1023     static const StringTypeAttr wordAttr;
1024   };
1025
1026   const StringTypeAttr StringTypeAttr::noAttr;
1027
1028   const StringTypeAttr StringTypeAttr::exactAttr("exact");
1029   const StringTypeAttr StringTypeAttr::substringAttr("substring");
1030   const StringTypeAttr StringTypeAttr::regexAttr("regex");
1031   const StringTypeAttr StringTypeAttr::globAttr("glob");
1032   const StringTypeAttr StringTypeAttr::wordAttr("word");
1033
1034   ///////////////////////////////////////////////////////////////////
1035
1036
1037   //\TODO maybe ctor with stream can be usefull
1038   //\TODO let it throw, let it throw, let it throw.
1039   bool PoolQuery::recover( istream &str, char delim )
1040   {
1041     bool finded_something = false; //indicates some atributes is finded
1042     string s;
1043     do {
1044       if ( str.eof() )
1045         break;
1046
1047       getline( str, s, delim );
1048
1049       if ((!s.empty()) && s[0]=='#') //comment
1050       {
1051         continue;
1052       }
1053
1054       string::size_type pos = s.find(':');
1055       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1056       {
1057         if (finded_something) //is first blank line after record?
1058         {
1059           break;
1060         }
1061         else
1062         {
1063           continue;
1064         }
1065       }
1066
1067       finded_something = true;
1068
1069       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1070       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1071
1072       PoolQueryAttr attribute( attrName );
1073
1074       if ( attribute==PoolQueryAttr::repoAttr )
1075       {
1076         addRepo( attrValue );
1077       }
1078       /* some backwards compatibility */
1079       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1080       {
1081         addKind( ResKind(attrValue) );
1082       }
1083       else if ( attribute==PoolQueryAttr::stringAttr
1084         || attribute=="global_string")
1085       {
1086         addString( attrValue );
1087       }
1088       else if ( attribute==PoolQueryAttr::stringTypeAttr
1089         || attribute=="string_type" )
1090       {
1091         StringTypeAttr s(attrValue);
1092         if( s == StringTypeAttr::regexAttr )
1093         {
1094           setMatchRegex();
1095         }
1096         else if ( s == StringTypeAttr::globAttr )
1097         {
1098           setMatchGlob();
1099         }
1100         else if ( s == StringTypeAttr::exactAttr )
1101         {
1102           setMatchExact();
1103         }
1104         else if ( s == StringTypeAttr::substringAttr )
1105         {
1106           setMatchSubstring();
1107         }
1108         else if ( s == StringTypeAttr::wordAttr )
1109         {
1110           setMatchWord();
1111         }
1112         else if ( s == StringTypeAttr::noAttr )
1113         {
1114           WAR << "unknown string type " << attrValue << endl;
1115         }
1116         else
1117         {
1118           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1119         }
1120       }
1121       else if ( attribute==PoolQueryAttr::requireAllAttr )
1122       {
1123         // LEAGACY: attribute was defined but never implemented.
1124         // Actually it should not occur outside our testcases.
1125       }
1126       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1127       {
1128         if ( str::strToTrue(attrValue) )
1129         {
1130           setCaseSensitive(true);
1131         }
1132         else if ( !str::strToFalse(attrValue) )
1133         {
1134           setCaseSensitive(false);
1135         }
1136         else
1137         {
1138           WAR << "unknown boolean value " << attrValue << endl;
1139         }
1140       }
1141       else if ( attribute==PoolQueryAttr::installStatusAttr )
1142       {
1143         if( attrValue == "all" )
1144         {
1145           setStatusFilterFlags( ALL );
1146         }
1147         else if( attrValue == "installed" )
1148         {
1149           setInstalledOnly();
1150         }
1151         else if( attrValue == "not-installed" )
1152         {
1153           setUninstalledOnly();
1154         }
1155         else
1156         {
1157           WAR << "Unknown value for install status " << attrValue << endl;
1158         }
1159       }
1160       else if ( attribute == PoolQueryAttr::editionAttr)
1161       {
1162         string::size_type pos;
1163         Rel rel("==");
1164         if (attrValue.find_first_of("=<>!") == 0)
1165         {
1166           pos = attrValue.find_last_of("=<>");
1167           rel = Rel(attrValue.substr(0, pos+1));
1168           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1169         }
1170
1171         setEdition(Edition(attrValue), rel);
1172       }
1173       else if ( attribute == PoolQueryAttr::complexAttr )
1174       {
1175         try
1176         {
1177           _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1178         }
1179         catch ( const Exception & err )
1180         {
1181           WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1182
1183         }
1184       }
1185       else if ( attribute==PoolQueryAttr::noAttr )
1186       {
1187         WAR << "empty attribute name" << endl;
1188       }
1189       else
1190       {
1191         string s = attrName;
1192         str::replaceAll( s,"_",":" );
1193         SolvAttr a(s);
1194         if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1195         {
1196           Capability c( attrValue );
1197           CapDetail d( c );
1198           if ( d.isVersioned() )
1199             addDependency( a, d.name().asString(), d.op(), d.ed() );
1200           else
1201             addDependency( a, attrValue );
1202         }
1203         else
1204           addAttribute( a, attrValue );
1205       }
1206
1207     } while ( true );
1208
1209     return finded_something;
1210   }
1211
1212   void PoolQuery::serialize( ostream &str, char delim ) const
1213   {
1214     //separating delim
1215     str << delim;
1216     //iterate thrue all settings and write it
1217     static const zypp::PoolQuery q; //not save default options, so create default query example
1218
1219     for_( it, repos().begin(), repos().end() )
1220     {
1221       str << "repo: " << *it << delim ;
1222     }
1223
1224     for_( it, kinds().begin(), kinds().end() )
1225     {
1226       str << PoolQueryAttr::kindAttr.asString() << ": "
1227           << it->idStr() << delim ;
1228     }
1229
1230     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1231       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1232
1233     if (matchMode()!=q.matchMode())
1234     {
1235       switch( matchMode() )
1236       {
1237       case Match::STRING:
1238         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1239         break;
1240       case Match::SUBSTRING:
1241         str << PoolQueryAttr::stringTypeAttr.asString()
1242             << ": substring" << delim;
1243         break;
1244       case Match::GLOB:
1245         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1246         break;
1247       case Match::REGEX:
1248         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1249         break;
1250       default:
1251         WAR << "unknown match type "  << matchMode() << endl;
1252       }
1253     }
1254
1255     if( caseSensitive() != q.caseSensitive() )
1256     {
1257       str << "case_sensitive: ";
1258       if (caseSensitive())
1259       {
1260         str << "on" << delim;
1261       }
1262       else
1263       {
1264         str << "off" << delim;
1265       }
1266     }
1267
1268     if( statusFilterFlags() != q.statusFilterFlags() )
1269     {
1270       switch( statusFilterFlags() )
1271       {
1272       case ALL:
1273         str << "install_status: all" << delim;
1274         break;
1275       case INSTALLED_ONLY:
1276         str << "install_status: installed" << delim;
1277         break;
1278       case UNINSTALLED_ONLY:
1279         str << "install_status: not-installed" << delim;
1280         break;
1281       }
1282     }
1283
1284     for_( it, strings().begin(), strings().end() )
1285     {
1286       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1287     }
1288
1289     for_( it, attributes().begin(), attributes().end() )
1290     {
1291       string s = it->first.asString();
1292       str::replaceAll(s,":","_");
1293       for_( it2,it->second.begin(),it->second.end() )
1294       {
1295         str << s <<": "<< *it2 << delim;
1296       }
1297     }
1298
1299     for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1300     {
1301       str << "complex: "<< it->serialize() << delim;
1302     }
1303
1304     //separating delim - protection
1305     str << delim;
1306   }
1307
1308   string PoolQuery::asString() const
1309   { return _pimpl->asString(); }
1310
1311   ostream & operator<<( ostream & str, const PoolQuery & obj )
1312   { return str << obj.asString(); }
1313
1314   std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1315   { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1316
1317   bool PoolQuery::operator==( const PoolQuery & rhs ) const
1318   { return *_pimpl == *rhs._pimpl; }
1319
1320   ///////////////////////////////////////////////////////////////////
1321   namespace detail
1322   { /////////////////////////////////////////////////////////////////
1323
1324     ///////////////////////////////////////////////////////////////////
1325     //
1326     //  CLASS NAME : PoolQueryMatcher
1327     //
1328     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1329      *
1330      * Basically the matcher performs a base query, which should preselect
1331      * candidates for a match. And has some filter conditions on top of it.
1332      * Query and fileter depend on the \ref PoolQuery settings.
1333      *
1334      * Matcher must be stateless, as it is shared between multiple
1335      * \ref PoolQueryIterator instances.
1336      *
1337      * If \ref base_iterator is at the \ref end, \ref advance moves it
1338      * to the first match. Otherwise advance moves to the next match, or
1339      * to the \ref end, if there is no more match.
1340      *
1341      * \note The original implementation treated an empty search string as
1342      * <it>"match always"</it>. We stay compatible.
1343      */
1344     class PoolQueryMatcher
1345     {
1346       public:
1347         typedef sat::LookupAttr::iterator base_iterator;
1348
1349       public:
1350         const base_iterator & end() const
1351         {
1352           static base_iterator _end;
1353           return _end;
1354         }
1355
1356         bool advance( base_iterator & base_r ) const
1357         {
1358           if ( base_r == end() )
1359             base_r = startNewQyery(); // first candidate
1360           else
1361           {
1362             base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1363             ++base_r; // advance to next candidate
1364           }
1365
1366           while ( base_r != end() )
1367           {
1368             if ( isAMatch( base_r ) )
1369               return true;
1370             // No match: try next
1371             ++base_r;
1372           }
1373           return false;
1374         }
1375
1376         /** Provide all matching attributes within this solvable.
1377          *
1378          */
1379         void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1380         {
1381           if ( base_r == end() )
1382             return;
1383
1384           sat::Solvable inSolvable( base_r.inSolvable() );
1385
1386           if ( _attrMatchList.size() == 1 )
1387           {
1388             // base_r is already on the 1st matching attribute!
1389             // String matching is done by the base iterator. We must check the predicate here.
1390             // Let's see if there are more matches for this solvable:
1391             base_iterator base( base_r );
1392             base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1393             return_r.push_back( base );
1394
1395             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1396             for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1397             {
1398               if ( ! predicate || predicate( base ) )
1399                 return_r.push_back( base );
1400             }
1401           }
1402           else
1403           {
1404             // Here: search all attributes ;(
1405             for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1406             {
1407               const AttrMatchData & matchData( *mi );
1408               sat::LookupAttr q( matchData.attr, inSolvable );
1409               if ( matchData.strMatcher ) // an empty searchstring matches always
1410                 q.setStrMatcher( matchData.strMatcher );
1411
1412               if ( ! q.empty() ) // there are matches.
1413               {
1414                 // now check any predicate:
1415                 const AttrMatchData::Predicate & predicate( matchData.predicate );
1416                 for_( it, q.begin(), q.end() )
1417                 {
1418                   if ( ! predicate || predicate( it ) )
1419                     return_r.push_back( it );
1420                 }
1421               }
1422             }
1423           }
1424         }
1425
1426       public:
1427         /** Ctor stores the \ref PoolQuery settings.
1428          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1429          */
1430         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1431         {
1432           query_r->compile();
1433
1434           // Repo restriction:
1435           sat::Pool satpool( sat::Pool::instance() );
1436
1437           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1438           {
1439             Repository r( satpool.reposFind( *it ) );
1440             if ( r )
1441               _repos.insert( r );
1442             else
1443               _neverMatchRepo = true;
1444           }
1445           // _neverMatchRepo: we just need to catch the case that no repo
1446           // matched, so we'd interpret the empty list as 'take from all'
1447           if ( _neverMatchRepo && ! _repos.empty() )
1448             _neverMatchRepo = false;
1449
1450           // Kind restriction:
1451           _kinds = query_r->_kinds;
1452           // Edition restriction:
1453           _op      = query_r->_op;
1454           _edition = query_r->_edition;
1455           // Status restriction:
1456           _status_flags = query_r->_status_flags;
1457           // StrMatcher
1458           _attrMatchList = query_r->_attrMatchList;
1459         }
1460
1461         ~PoolQueryMatcher()
1462         {}
1463
1464       private:
1465         /** Initialize a new base query. */
1466         base_iterator startNewQyery() const
1467         {
1468           sat::LookupAttr q;
1469
1470           if ( _neverMatchRepo )
1471             return q.end();
1472
1473           // Repo restriction:
1474           if ( _repos.size() == 1 )
1475             q.setRepo( *_repos.begin() );
1476           // else: handled in isAMatch.
1477
1478           // Attribute restriction:
1479           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1480           {
1481             const AttrMatchData & matchData( _attrMatchList.front() );
1482             q.setAttr( matchData.attr );
1483             if ( matchData.strMatcher ) // empty searchstring matches always
1484               q.setStrMatcher( matchData.strMatcher );
1485           }
1486           else // more than 1 attr (but not all)
1487           {
1488             // no restriction, it's all handled in isAMatch.
1489             q.setAttr( sat::SolvAttr::allAttr );
1490           }
1491
1492           return q.begin();
1493         }
1494
1495
1496         /** Check whether we are on a match.
1497          *
1498          * The check covers the whole Solvable, not just the current
1499          * attribute \c base_r points to. If there's no match, also
1500          * prepare \c base_r to advance appropriately. If there is
1501          * a match, simply return \c true. \ref advance always moves
1502          * to the next Solvable if there was a match.
1503          *
1504          * \note: Caller asserts we're not at \ref end.
1505         */
1506         bool isAMatch( base_iterator & base_r ) const
1507         {
1508           /////////////////////////////////////////////////////////////////////
1509           Repository inRepo( base_r.inRepo() );
1510           // Status restriction:
1511           if ( _status_flags
1512              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1513           {
1514             base_r.nextSkipRepo();
1515             return false;
1516           }
1517           // Repo restriction:
1518           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1519           {
1520             base_r.nextSkipRepo();
1521             return false;
1522           }
1523           /////////////////////////////////////////////////////////////////////
1524           sat::Solvable inSolvable( base_r.inSolvable() );
1525           // Kind restriction:
1526           if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1527           {
1528             base_r.nextSkipSolvable();
1529             return false;
1530           }
1531
1532           // Edition restriction:
1533           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1534           {
1535             base_r.nextSkipSolvable();
1536             return false;
1537           }
1538           /////////////////////////////////////////////////////////////////////
1539           // string and predicate matching:
1540
1541           if ( _attrMatchList.size() == 1 )
1542           {
1543             // String matching was done by the base iterator.
1544             // Now check any predicate:
1545             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1546             if ( ! predicate || predicate( base_r ) )
1547               return true;
1548
1549             return false; // no skip as there may be more occurrences od this attr.
1550           }
1551
1552           // Here: search all attributes ;(
1553           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1554           {
1555             const AttrMatchData & matchData( *mi );
1556             sat::LookupAttr q( matchData.attr, inSolvable );
1557             if ( matchData.strMatcher ) // an empty searchstring matches always
1558               q.setStrMatcher( matchData.strMatcher );
1559
1560             if ( ! q.empty() ) // there are matches.
1561             {
1562               // now check any predicate:
1563               const AttrMatchData::Predicate & predicate( matchData.predicate );
1564               if ( predicate )
1565               {
1566                 for_( it, q.begin(), q.end() )
1567                 {
1568                   if ( predicate( it ) )
1569                     return true;
1570                 }
1571               }
1572               else
1573                 return true;
1574             }
1575           }
1576           base_r.nextSkipSolvable();
1577           return false;
1578         }
1579
1580       private:
1581         /** Repositories include in the search. */
1582         std::set<Repository> _repos;
1583         DefaultIntegral<bool,false> _neverMatchRepo;
1584         /** Resolvable kinds to include. */
1585         std::set<ResKind> _kinds;
1586         /** Edition filter. */
1587         Rel _op;
1588         Edition _edition;
1589         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1590         int _status_flags;
1591         /** StrMatcher per attribtue. */
1592         AttrMatchList _attrMatchList;
1593     };
1594     ///////////////////////////////////////////////////////////////////
1595
1596     void PoolQueryIterator::increment()
1597     {
1598       // matcher restarts if at end! It is called from the ctor
1599       // to get the 1st match. But if the end is reached, it should
1600       // be deleted, otherwise we'd start over again.
1601       if ( !_matcher )
1602         return; // at end
1603       if ( _matches )
1604         _matches.reset(); // invalidate old matches
1605       if ( ! _matcher->advance( base_reference() ) )
1606         _matcher.reset();
1607     }
1608
1609     const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1610     {
1611       if ( _matches )
1612         return *_matches;
1613
1614       if ( !_matcher )
1615       {
1616         // at end of query:
1617         static const Matches _none;
1618         return _none;
1619       }
1620
1621       _matches.reset( new Matches );
1622       _matcher->matchDetail( base_reference(), *_matches );
1623       return *_matches;
1624     }
1625
1626     std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1627     {
1628       str << *obj;
1629       if ( ! obj.matchesEmpty() )
1630       {
1631         for_( it, obj.matchesBegin(), obj.matchesEnd() )
1632         {
1633           str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
1634         }
1635       }
1636       return str;
1637     }
1638
1639     ///////////////////////////////////////////////////////////////////
1640   } //namespace detail
1641   ///////////////////////////////////////////////////////////////////
1642
1643   detail::PoolQueryIterator PoolQuery::begin() const
1644   {
1645     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1646   }
1647
1648   /////////////////////////////////////////////////////////////////
1649 } // namespace zypp
1650 ///////////////////////////////////////////////////////////////////
1651