Imported Upstream version 17.8.0
[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     if ( container_r.empty() )
736       return StrMatcher( std::string(), flags_r );
737
738     if ( container_r.size() == 1 && !_match_word )      // use RX to match words
739       return StrMatcher( *container_r.begin(), flags_r );
740
741     // Convert to a regex.
742     // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
743     //       SUBSTRING and REGEX match substrings      (match_word anchores SUBSTRING \b)
744     Match retflags( flags_r );
745     retflags.setModeRegex();
746     str::Str ret;
747
748     if ( flags_r.isModeString() || flags_r.isModeGlob() )
749       ret << "^";
750     else if ( _match_word )
751       ret << "\\b";
752
753     // (..|..|..)
754     char sep = '(';
755     for ( const::std::string & s : container_r )
756     {
757       ret << sep << rxEscape( s, flags_r );
758       if ( sep == '(' )
759         sep = '|';
760     }
761     ret << ')';
762
763     if ( flags_r.isModeString() || flags_r.isModeGlob() )
764       ret << "$";
765     else if ( _match_word )
766       ret << "\\b";
767
768     return StrMatcher( ret, retflags );
769   }
770
771   string PoolQuery::Impl::asString() const
772   {
773     ostringstream o;
774
775     o << "kinds: ";
776     if ( _kinds.empty() )
777       o << "ALL";
778     else
779     {
780       for(Kinds::const_iterator it = _kinds.begin();
781           it != _kinds.end(); ++it)
782         o << *it << " ";
783     }
784     o << endl;
785
786     o << "repos: ";
787     if ( _repos.empty() )
788       o << "ALL";
789     else
790     {
791       for(StrContainer::const_iterator it = _repos.begin();
792           it != _repos.end(); ++it)
793         o << *it << " ";
794     }
795     o << endl;
796
797     o << "version: "<< _op << " " << _edition.asString() << endl;
798     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
799                                        : "ALL" ) << endl;
800
801     o << "string match flags: " << Match(_flags) << endl;
802
803     // raw
804     o << "strings: ";
805     for(StrContainer::const_iterator it = _strings.begin();
806         it != _strings.end(); ++it)
807       o << *it << " ";
808     o << endl;
809
810     o << "attributes: " << endl;
811     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
812     {
813       o << "* " << ai->first << ": ";
814       for(StrContainer::const_iterator vi = ai->second.begin();
815           vi != ai->second.end(); ++vi)
816         o << *vi << " ";
817       o << endl;
818     }
819
820     o << "predicated: " << endl;
821     for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
822     {
823       o << "* " << *it << endl;
824     }
825
826     // compiled
827     o << "last attribute matcher compiled: " << endl;
828     if ( _attrMatchList.empty() )
829     {
830       o << "not yet compiled" << endl;
831     }
832     else
833     {
834       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
835       {
836         o << "* " << *it << endl;
837       }
838     }
839     return o.str();
840   }
841
842   ///////////////////////////////////////////////////////////////////
843
844   ///////////////////////////////////////////////////////////////////
845   //
846   //    CLASS NAME : PoolQuery
847   //
848   ///////////////////////////////////////////////////////////////////
849
850   PoolQuery::PoolQuery()
851     : _pimpl(new Impl())
852   {}
853
854   PoolQuery::~PoolQuery()
855   {}
856
857   void PoolQuery::addRepo(const std::string &repoalias)
858   {
859     if (repoalias.empty())
860     {
861       WAR << "ignoring an empty repository alias" << endl;
862       return;
863     }
864     _pimpl->_repos.insert(repoalias);
865   }
866
867   void PoolQuery::addKind(const ResKind & kind)
868   { _pimpl->_kinds.insert(kind); }
869
870   void PoolQuery::addString(const string & value)
871   { _pimpl->_strings.insert(value); }
872
873   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
874   { _pimpl->_attrs[attr].insert(value); }
875
876   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
877   {
878     // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
879     return addDependency( attr, name, op, edition, Arch_empty, Match::OTHER );
880   }
881
882   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
883   {
884     // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
885     return addDependency( attr, name, op, edition, arch, Match::OTHER );
886   }
887
888   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch, Match::Mode mode )
889   {
890     if ( op == Rel::NONE )      // will never match.
891       return;
892
893     // SolvAttr::name with explicit 'kind:name' will overwrite the default _kinds
894     ResKind explicitKind;
895     if ( attr == sat::SolvAttr::name )
896       explicitKind = ResKind::explicitBuiltin( name );
897
898     // Legacy: Match::OTHER and no additional constraints on edition/arch/kind
899     //         require addAttribute, otherwise de-serialisation breaks (serialized
900     //         and de-serialized query could be !=).
901     // From the results POV we could also use the predicated case below.
902     if ( op == Rel::ANY && arch.empty() && !explicitKind && mode == Match::OTHER )
903     {
904       addAttribute( attr, name );
905       return;
906     }
907
908     // Match::OTHER indicates need to compile
909     // (merge global search strings into name).
910     AttrMatchData attrMatchData( attr );
911     if ( !explicitKind )
912       attrMatchData.strMatcher = StrMatcher( name, mode );
913     else
914     {
915       // ResKind::explicitBuiltin call above asserts the presence of the ':' in name
916       attrMatchData.strMatcher = StrMatcher( strchr( name.c_str(), ':')+1, mode );
917       attrMatchData.kindPredicate = explicitKind;
918     }
919
920     if ( isDependencyAttribute( attr ) )
921       attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
922     else
923       attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
924
925     _pimpl->_uncompiledPredicated.insert( attrMatchData );
926   }
927
928   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
929   {
930     CapDetail cap( cap_r );
931     if ( ! cap.isSimple() ) // will never match.
932       return;
933
934     // Matches STRING per default. (won't get compiled!)
935     AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
936
937     if ( isDependencyAttribute( attr ) )
938       attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
939     else
940       attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
941
942     _pimpl->_uncompiledPredicated.insert( attrMatchData );
943   }
944
945   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
946   {
947     _pimpl->_edition = edition;
948     _pimpl->_op = op;
949   }
950
951   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = false; }
952   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString();       _pimpl->_match_word = false; }
953   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex();        _pimpl->_match_word = false; }
954   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob();         _pimpl->_match_word = false; }
955   void PoolQuery::setMatchWord()        { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = true; }
956
957   Match PoolQuery::flags() const
958   { return _pimpl->_flags; }
959   void PoolQuery::setFlags( const Match & flags )
960   { _pimpl->_flags = flags; }
961
962
963   void PoolQuery::setInstalledOnly()
964   { _pimpl->_status_flags = INSTALLED_ONLY; }
965   void PoolQuery::setUninstalledOnly()
966   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
967   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
968   { _pimpl->_status_flags = flags; }
969
970
971   const PoolQuery::StrContainer &
972   PoolQuery::strings() const
973   { return _pimpl->_strings; }
974
975   const PoolQuery::AttrRawStrMap &
976   PoolQuery::attributes() const
977   { return _pimpl->_attrs; }
978
979   const PoolQuery::StrContainer &
980   PoolQuery::attribute(const sat::SolvAttr & attr) const
981   {
982     static const PoolQuery::StrContainer nocontainer;
983     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
984     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
985   }
986
987   const Edition PoolQuery::edition() const
988   { return _pimpl->_edition; }
989   const Rel PoolQuery::editionRel() const
990   { return _pimpl->_op; }
991
992
993   const PoolQuery::Kinds &
994   PoolQuery::kinds() const
995   { return _pimpl->_kinds; }
996
997   const PoolQuery::StrContainer &
998   PoolQuery::repos() const
999   { return _pimpl->_repos; }
1000
1001
1002   bool PoolQuery::caseSensitive() const
1003   { return !_pimpl->_flags.test( Match::NOCASE ); }
1004   void PoolQuery::setCaseSensitive( bool value )
1005   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
1006
1007   bool PoolQuery::filesMatchFullPath() const
1008   { return _pimpl->_flags.test( Match::FILES ); }
1009   void PoolQuery::setFilesMatchFullPath( bool value )
1010   { _pimpl->_flags.turn( Match::FILES, value ); }
1011
1012   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
1013   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; }
1014   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
1015   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
1016   bool PoolQuery::matchWord() const             { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
1017
1018   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
1019   { return _pimpl->_status_flags; }
1020
1021   bool PoolQuery::empty() const
1022   {
1023     try { return begin() == end(); }
1024     catch (const Exception & ex) {}
1025     return true;
1026   }
1027
1028   PoolQuery::size_type PoolQuery::size() const
1029   {
1030     try
1031     {
1032       size_type count = 0;
1033       for_( it, begin(), end() )
1034         ++count;
1035       return count;
1036     }
1037     catch (const Exception & ex) {}
1038     return 0;
1039   }
1040
1041   void PoolQuery::execute(ProcessResolvable fnc)
1042   { invokeOnEach( begin(), end(), fnc); }
1043
1044
1045   /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
1046   /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const    { return false; }
1047
1048   ///////////////////////////////////////////////////////////////////
1049   //
1050   //  CLASS NAME : PoolQuery::Attr
1051   //
1052   /**
1053    * represents all atributes in PoolQuery except SolvAtributes, which are
1054    * used as is (not needed extend anything if someone adds new solv attr)
1055    */
1056   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1057   {
1058   private:
1059     friend class IdStringType<PoolQueryAttr>;
1060     IdString _str;
1061   public:
1062
1063     //noAttr
1064     PoolQueryAttr(){}
1065
1066     explicit PoolQueryAttr( const char* cstr_r )
1067         : _str( cstr_r )
1068       {}
1069
1070     explicit PoolQueryAttr( const std::string & str_r )
1071         : _str( str_r )
1072       {}
1073
1074     // unknown atributes
1075     static const PoolQueryAttr noAttr;
1076
1077     // PoolQuery's own attributes
1078     static const PoolQueryAttr repoAttr;
1079     static const PoolQueryAttr kindAttr;
1080     static const PoolQueryAttr stringAttr;
1081     static const PoolQueryAttr stringTypeAttr;
1082     static const PoolQueryAttr requireAllAttr;  // LEAGACY: attribute was defined but never implemented.
1083     static const PoolQueryAttr caseSensitiveAttr;
1084     static const PoolQueryAttr installStatusAttr;
1085     static const PoolQueryAttr editionAttr;
1086     static const PoolQueryAttr complexAttr;
1087   };
1088
1089   const PoolQueryAttr PoolQueryAttr::noAttr;
1090
1091   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1092   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1093   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1094   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1095   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");     // LEAGACY: attribute was defined but never implemented.
1096   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1097   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1098   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1099   const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1100
1101   class StringTypeAttr : public IdStringType<PoolQueryAttr>
1102   {
1103     friend class IdStringType<StringTypeAttr>;
1104     IdString _str;
1105
1106   public:
1107     StringTypeAttr(){}
1108     explicit StringTypeAttr( const char* cstr_r )
1109             : _str( cstr_r ){}
1110     explicit StringTypeAttr( const std::string & str_r )
1111              : _str( str_r ){}
1112
1113     static const StringTypeAttr noAttr;
1114
1115     static const StringTypeAttr exactAttr;
1116     static const StringTypeAttr substringAttr;
1117     static const StringTypeAttr regexAttr;
1118     static const StringTypeAttr globAttr;
1119     static const StringTypeAttr wordAttr;
1120   };
1121
1122   const StringTypeAttr StringTypeAttr::noAttr;
1123
1124   const StringTypeAttr StringTypeAttr::exactAttr("exact");
1125   const StringTypeAttr StringTypeAttr::substringAttr("substring");
1126   const StringTypeAttr StringTypeAttr::regexAttr("regex");
1127   const StringTypeAttr StringTypeAttr::globAttr("glob");
1128   const StringTypeAttr StringTypeAttr::wordAttr("word");
1129
1130   ///////////////////////////////////////////////////////////////////
1131
1132
1133   //\TODO maybe ctor with stream can be usefull
1134   //\TODO let it throw, let it throw, let it throw.
1135   bool PoolQuery::recover( istream &str, char delim )
1136   {
1137     bool finded_something = false; //indicates some atributes is finded
1138     string s;
1139     do {
1140       if ( str.eof() )
1141         break;
1142
1143       getline( str, s, delim );
1144
1145       if ((!s.empty()) && s[0]=='#') //comment
1146       {
1147         continue;
1148       }
1149
1150       string::size_type pos = s.find(':');
1151       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1152       {
1153         if (finded_something) //is first blank line after record?
1154         {
1155           break;
1156         }
1157         else
1158         {
1159           continue;
1160         }
1161       }
1162
1163       finded_something = true;
1164
1165       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1166       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1167
1168       PoolQueryAttr attribute( attrName );
1169
1170       if ( attribute==PoolQueryAttr::repoAttr )
1171       {
1172         addRepo( attrValue );
1173       }
1174       /* some backwards compatibility */
1175       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1176       {
1177         addKind( ResKind(attrValue) );
1178       }
1179       else if ( attribute==PoolQueryAttr::stringAttr
1180         || attribute=="global_string")
1181       {
1182         addString( attrValue );
1183       }
1184       else if ( attribute==PoolQueryAttr::stringTypeAttr
1185         || attribute=="string_type" )
1186       {
1187         StringTypeAttr s(attrValue);
1188         if( s == StringTypeAttr::regexAttr )
1189         {
1190           setMatchRegex();
1191         }
1192         else if ( s == StringTypeAttr::globAttr )
1193         {
1194           setMatchGlob();
1195         }
1196         else if ( s == StringTypeAttr::exactAttr )
1197         {
1198           setMatchExact();
1199         }
1200         else if ( s == StringTypeAttr::substringAttr )
1201         {
1202           setMatchSubstring();
1203         }
1204         else if ( s == StringTypeAttr::wordAttr )
1205         {
1206           setMatchWord();
1207         }
1208         else if ( s == StringTypeAttr::noAttr )
1209         {
1210           WAR << "unknown string type " << attrValue << endl;
1211         }
1212         else
1213         {
1214           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1215         }
1216       }
1217       else if ( attribute==PoolQueryAttr::requireAllAttr )
1218       {
1219         // LEAGACY: attribute was defined but never implemented.
1220         // Actually it should not occur outside our testcases.
1221       }
1222       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1223       {
1224         if ( str::strToTrue(attrValue) )
1225         {
1226           setCaseSensitive(true);
1227         }
1228         else if ( !str::strToFalse(attrValue) )
1229         {
1230           setCaseSensitive(false);
1231         }
1232         else
1233         {
1234           WAR << "unknown boolean value " << attrValue << endl;
1235         }
1236       }
1237       else if ( attribute==PoolQueryAttr::installStatusAttr )
1238       {
1239         if( attrValue == "all" )
1240         {
1241           setStatusFilterFlags( ALL );
1242         }
1243         else if( attrValue == "installed" )
1244         {
1245           setInstalledOnly();
1246         }
1247         else if( attrValue == "not-installed" )
1248         {
1249           setUninstalledOnly();
1250         }
1251         else
1252         {
1253           WAR << "Unknown value for install status " << attrValue << endl;
1254         }
1255       }
1256       else if ( attribute == PoolQueryAttr::editionAttr)
1257       {
1258         string::size_type pos;
1259         Rel rel("==");
1260         if (attrValue.find_first_of("=<>!") == 0)
1261         {
1262           pos = attrValue.find_last_of("=<>");
1263           rel = Rel(attrValue.substr(0, pos+1));
1264           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1265         }
1266
1267         setEdition(Edition(attrValue), rel);
1268       }
1269       else if ( attribute == PoolQueryAttr::complexAttr )
1270       {
1271         try
1272         {
1273           _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1274         }
1275         catch ( const Exception & err )
1276         {
1277           WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1278
1279         }
1280       }
1281       else if ( attribute==PoolQueryAttr::noAttr )
1282       {
1283         WAR << "empty attribute name" << endl;
1284       }
1285       else
1286       {
1287         string s = attrName;
1288         str::replaceAll( s,"_",":" );
1289         SolvAttr a(s);
1290         if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1291         {
1292           Capability c( attrValue );
1293           CapDetail d( c );
1294           if ( d.isVersioned() )
1295             addDependency( a, d.name().asString(), d.op(), d.ed() );
1296           else
1297             addDependency( a, attrValue );
1298         }
1299         else
1300           addAttribute( a, attrValue );
1301       }
1302
1303     } while ( true );
1304
1305     return finded_something;
1306   }
1307
1308   void PoolQuery::serialize( ostream &str, char delim ) const
1309   {
1310     //separating delim
1311     str << delim;
1312     //iterate thrue all settings and write it
1313     static const zypp::PoolQuery q; //not save default options, so create default query example
1314
1315     for_( it, repos().begin(), repos().end() )
1316     {
1317       str << "repo: " << *it << delim ;
1318     }
1319
1320     for_( it, kinds().begin(), kinds().end() )
1321     {
1322       str << PoolQueryAttr::kindAttr.asString() << ": "
1323           << it->idStr() << delim ;
1324     }
1325
1326     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1327       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1328
1329     if (matchMode()!=q.matchMode())
1330     {
1331       switch( matchMode() )
1332       {
1333       case Match::STRING:
1334         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1335         break;
1336       case Match::SUBSTRING:
1337         str << PoolQueryAttr::stringTypeAttr.asString()
1338             << ": substring" << delim;
1339         break;
1340       case Match::GLOB:
1341         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1342         break;
1343       case Match::REGEX:
1344         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1345         break;
1346       default:
1347         WAR << "unknown match type "  << matchMode() << endl;
1348       }
1349     }
1350
1351     if( caseSensitive() != q.caseSensitive() )
1352     {
1353       str << "case_sensitive: ";
1354       if (caseSensitive())
1355       {
1356         str << "on" << delim;
1357       }
1358       else
1359       {
1360         str << "off" << delim;
1361       }
1362     }
1363
1364     if( statusFilterFlags() != q.statusFilterFlags() )
1365     {
1366       switch( statusFilterFlags() )
1367       {
1368       case ALL:
1369         str << "install_status: all" << delim;
1370         break;
1371       case INSTALLED_ONLY:
1372         str << "install_status: installed" << delim;
1373         break;
1374       case UNINSTALLED_ONLY:
1375         str << "install_status: not-installed" << delim;
1376         break;
1377       }
1378     }
1379
1380     for_( it, strings().begin(), strings().end() )
1381     {
1382       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1383     }
1384
1385     for_( it, attributes().begin(), attributes().end() )
1386     {
1387       string s = it->first.asString();
1388       str::replaceAll(s,":","_");
1389       for_( it2,it->second.begin(),it->second.end() )
1390       {
1391         str << s <<": "<< *it2 << delim;
1392       }
1393     }
1394
1395     for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1396     {
1397       str << "complex: "<< it->serialize() << delim;
1398     }
1399
1400     //separating delim - protection
1401     str << delim;
1402   }
1403
1404   string PoolQuery::asString() const
1405   { return _pimpl->asString(); }
1406
1407   ostream & operator<<( ostream & str, const PoolQuery & obj )
1408   { return str << obj.asString(); }
1409
1410   std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1411   { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1412
1413   bool PoolQuery::operator==( const PoolQuery & rhs ) const
1414   { return *_pimpl == *rhs._pimpl; }
1415
1416   bool PoolQuery::operator<( const PoolQuery & rhs ) const
1417   { return *_pimpl < *rhs._pimpl; }
1418
1419   ///////////////////////////////////////////////////////////////////
1420   namespace detail
1421   { /////////////////////////////////////////////////////////////////
1422
1423     ///////////////////////////////////////////////////////////////////
1424     //
1425     //  CLASS NAME : PoolQueryMatcher
1426     //
1427     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1428      *
1429      * Basically the matcher performs a base query, which should preselect
1430      * candidates for a match. And has some filter conditions on top of it.
1431      * Query and fileter depend on the \ref PoolQuery settings.
1432      *
1433      * Matcher must be stateless, as it is shared between multiple
1434      * \ref PoolQueryIterator instances.
1435      *
1436      * If \ref base_iterator is at the \ref end, \ref advance moves it
1437      * to the first match. Otherwise advance moves to the next match, or
1438      * to the \ref end, if there is no more match.
1439      *
1440      * \note The original implementation treated an empty search string as
1441      * <it>"match always"</it>. We stay compatible.
1442      */
1443     class PoolQueryMatcher
1444     {
1445       public:
1446         typedef sat::LookupAttr::iterator base_iterator;
1447
1448       public:
1449         const base_iterator & end() const
1450         {
1451           static base_iterator _end;
1452           return _end;
1453         }
1454
1455         bool advance( base_iterator & base_r ) const
1456         {
1457           if ( base_r == end() )
1458             base_r = startNewQyery(); // first candidate
1459           else
1460           {
1461             base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1462             ++base_r; // advance to next candidate
1463           }
1464
1465           while ( base_r != end() )
1466           {
1467             if ( isAMatch( base_r ) )
1468               return true;
1469             // No match: try next
1470             ++base_r;
1471           }
1472           return false;
1473         }
1474
1475         /** Provide all matching attributes within this solvable.
1476          *
1477          */
1478         void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1479         {
1480           if ( base_r == end() )
1481             return;
1482
1483           sat::Solvable inSolvable( base_r.inSolvable() );
1484
1485           if ( _attrMatchList.size() == 1 )
1486           {
1487             // base_r is already on the 1st matching attribute!
1488             // String matching is done by the base iterator. We must check the predicate here.
1489             // Let's see if there are more matches for this solvable:
1490             base_iterator base( base_r );
1491             base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1492             return_r.push_back( base );
1493
1494             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1495             for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1496             {
1497               if ( ! predicate || predicate( base ) )
1498                 return_r.push_back( base );
1499             }
1500           }
1501           else
1502           {
1503             // Here: search all attributes ;(
1504             for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1505             {
1506               const AttrMatchData & matchData( *mi );
1507               sat::LookupAttr q( matchData.attr, inSolvable );
1508               if ( matchData.strMatcher ) // an empty searchstring matches always
1509                 q.setStrMatcher( matchData.strMatcher );
1510
1511               if ( ! q.empty() ) // there are matches.
1512               {
1513                 // now check any predicate:
1514                 const AttrMatchData::Predicate & predicate( matchData.predicate );
1515                 for_( it, q.begin(), q.end() )
1516                 {
1517                   if ( ! predicate || predicate( it ) )
1518                     return_r.push_back( it );
1519                 }
1520               }
1521             }
1522           }
1523         }
1524
1525       public:
1526         /** Ctor stores the \ref PoolQuery settings.
1527          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1528          */
1529         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1530         {
1531           query_r->compile();
1532
1533           // Repo restriction:
1534           sat::Pool satpool( sat::Pool::instance() );
1535
1536           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1537           {
1538             Repository r( satpool.reposFind( *it ) );
1539             if ( r )
1540               _repos.insert( r );
1541             else
1542               _neverMatchRepo = true;
1543           }
1544           // _neverMatchRepo: we just need to catch the case that no repo
1545           // matched, so we'd interpret the empty list as 'take from all'
1546           if ( _neverMatchRepo && ! _repos.empty() )
1547             _neverMatchRepo = false;
1548
1549           // Kind restriction:
1550           _kinds = query_r->_kinds;
1551           // Edition restriction:
1552           _op      = query_r->_op;
1553           _edition = query_r->_edition;
1554           // Status restriction:
1555           _status_flags = query_r->_status_flags;
1556           // StrMatcher
1557           _attrMatchList = query_r->_attrMatchList;
1558         }
1559
1560         ~PoolQueryMatcher()
1561         {}
1562
1563       private:
1564         /** Initialize a new base query. */
1565         base_iterator startNewQyery() const
1566         {
1567           sat::LookupAttr q;
1568
1569           if ( _neverMatchRepo )
1570             return q.end();
1571
1572           // Repo restriction:
1573           if ( _repos.size() == 1 )
1574             q.setRepo( *_repos.begin() );
1575           // else: handled in isAMatch.
1576
1577           // Attribute restriction:
1578           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1579           {
1580             const AttrMatchData & matchData( _attrMatchList.front() );
1581             q.setAttr( matchData.attr );
1582             if ( matchData.strMatcher ) // empty searchstring matches always
1583               q.setStrMatcher( matchData.strMatcher );
1584           }
1585           else // more than 1 attr (but not all)
1586           {
1587             // no restriction, it's all handled in isAMatch.
1588             q.setAttr( sat::SolvAttr::allAttr );
1589           }
1590
1591           return q.begin();
1592         }
1593
1594
1595         /** Check whether we are on a match.
1596          *
1597          * The check covers the whole Solvable, not just the current
1598          * attribute \c base_r points to. If there's no match, also
1599          * prepare \c base_r to advance appropriately. If there is
1600          * a match, simply return \c true. \ref advance always moves
1601          * to the next Solvable if there was a match.
1602          *
1603          * \note: Caller asserts we're not at \ref end.
1604         */
1605         bool isAMatch( base_iterator & base_r ) const
1606         {
1607           /////////////////////////////////////////////////////////////////////
1608           Repository inRepo( base_r.inRepo() );
1609           // Status restriction:
1610           if ( _status_flags
1611              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1612           {
1613             base_r.nextSkipRepo();
1614             return false;
1615           }
1616           // Repo restriction:
1617           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1618           {
1619             base_r.nextSkipRepo();
1620             return false;
1621           }
1622           /////////////////////////////////////////////////////////////////////
1623           sat::Solvable inSolvable( base_r.inSolvable() );
1624           // Edition restriction:
1625           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1626           {
1627             base_r.nextSkipSolvable();
1628             return false;
1629           }
1630
1631           // Kind restriction:
1632           // Delay the decision to nextSkipSolvable and return false, as there may be
1633           // some explicit kind:name predicate which overrules the global kinds.
1634           bool globalKindOk =( _kinds.empty() || inSolvable.isKind( _kinds.begin(), _kinds.end() ) );
1635
1636           /////////////////////////////////////////////////////////////////////
1637           // string and predicate matching:
1638
1639           if ( _attrMatchList.size() == 1 )
1640           {
1641             // String matching was done by the base iterator.
1642             // Now check any predicate:
1643             const AttrMatchData & matchData( _attrMatchList.front() );
1644
1645             if ( matchData.kindPredicate )
1646             {
1647               if ( matchData.kindPredicate != inSolvable.kind() )
1648               {
1649                 base_r.nextSkipSolvable();      // this matchData will never match in this solvable
1650                 return false;
1651               }
1652             }
1653             else if ( !globalKindOk )
1654               return false;                     // only matching kindPredicate could overwrite this
1655
1656             if ( !matchData.predicate || matchData.predicate( base_r ) )
1657               return true;
1658
1659             return false; // no skip as there may be more occurrences in this solvable of this attr.
1660           }
1661
1662           // Here: search all attributes ;(
1663           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1664           {
1665             const AttrMatchData & matchData( *mi );
1666
1667             if ( matchData.kindPredicate )
1668             {
1669               if ( matchData.kindPredicate != inSolvable.kind() )
1670                 continue;                       // this matchData does not apply
1671             }
1672             else if ( !globalKindOk )
1673               continue;                         // only matching kindPredicate could overwrite this
1674
1675             sat::LookupAttr q( matchData.attr, inSolvable );
1676             if ( matchData.strMatcher ) // an empty searchstring matches always
1677               q.setStrMatcher( matchData.strMatcher );
1678
1679             if ( ! q.empty() ) // there are matches.
1680             {
1681               // now check any predicate:
1682               const AttrMatchData::Predicate & predicate( matchData.predicate );
1683               if ( predicate )
1684               {
1685                 for_( it, q.begin(), q.end() )
1686                 {
1687                   if ( predicate( it ) )
1688                     return true;
1689                 }
1690               }
1691               else
1692                 return true;
1693             }
1694           }
1695           base_r.nextSkipSolvable();
1696           return false;
1697         }
1698
1699       private:
1700         /** Repositories include in the search. */
1701         std::set<Repository> _repos;
1702         DefaultIntegral<bool,false> _neverMatchRepo;
1703         /** Resolvable kinds to include. */
1704         std::set<ResKind> _kinds;
1705         /** Edition filter. */
1706         Rel _op;
1707         Edition _edition;
1708         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1709         int _status_flags;
1710         /** StrMatcher per attribtue. */
1711         AttrMatchList _attrMatchList;
1712     };
1713     ///////////////////////////////////////////////////////////////////
1714
1715     void PoolQueryIterator::increment()
1716     {
1717       // matcher restarts if at end! It is called from the ctor
1718       // to get the 1st match. But if the end is reached, it should
1719       // be deleted, otherwise we'd start over again.
1720       if ( !_matcher )
1721         return; // at end
1722       if ( _matches )
1723         _matches.reset(); // invalidate old matches
1724       if ( ! _matcher->advance( base_reference() ) )
1725         _matcher.reset();
1726     }
1727
1728     const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1729     {
1730       if ( _matches )
1731         return *_matches;
1732
1733       if ( !_matcher )
1734       {
1735         // at end of query:
1736         static const Matches _none;
1737         return _none;
1738       }
1739
1740       _matches.reset( new Matches );
1741       _matcher->matchDetail( base_reference(), *_matches );
1742       return *_matches;
1743     }
1744
1745     std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1746     {
1747       str << *obj;
1748       if ( ! obj.matchesEmpty() )
1749       {
1750         for_( it, obj.matchesBegin(), obj.matchesEnd() )
1751         {
1752           str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
1753         }
1754       }
1755       return str;
1756     }
1757
1758     ///////////////////////////////////////////////////////////////////
1759   } //namespace detail
1760   ///////////////////////////////////////////////////////////////////
1761
1762   detail::PoolQueryIterator PoolQuery::begin() const
1763   {
1764     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1765   }
1766
1767   /////////////////////////////////////////////////////////////////
1768 } // namespace zypp
1769 ///////////////////////////////////////////////////////////////////
1770