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